aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml10
-rw-r--r--BUILD85
-rw-r--r--CHANGES.txt77
-rw-r--r--INSTALL.txt237
-rw-r--r--Makefile.am420
-rw-r--r--Protobuf.podspec2
-rw-r--r--README.md206
-rw-r--r--appveyor.yml64
-rw-r--r--cmake/CMakeLists.txt2
-rw-r--r--cmake/README.md9
-rw-r--r--cmake/extract_includes.bat.in4
-rw-r--r--cmake/libprotobuf.cmake1
-rw-r--r--cmake/libprotoc.cmake2
-rw-r--r--cmake/tests.cmake6
-rw-r--r--configure.ac14
-rw-r--r--conformance/ConformanceJava.java28
-rw-r--r--conformance/Makefile.am101
-rw-r--r--conformance/conformance.proto61
-rw-r--r--conformance/conformance_cpp.cc17
-rw-r--r--conformance/conformance_objc.m179
-rwxr-xr-xconformance/conformance_python.py130
-rw-r--r--conformance/conformance_test.cc1518
-rw-r--r--conformance/conformance_test.h22
-rw-r--r--conformance/conformance_test_runner.cc88
-rw-r--r--conformance/failure_list_cpp.txt85
-rw-r--r--conformance/failure_list_csharp.txt16
-rw-r--r--conformance/failure_list_java.txt49
-rw-r--r--conformance/failure_list_objc.txt4
-rw-r--r--conformance/failure_list_python-post26.txt2
-rw-r--r--conformance/failure_list_python.txt85
-rw-r--r--conformance/failure_list_python_cpp.txt110
-rw-r--r--conformance/failure_list_ruby.txt335
-rw-r--r--conformance/third_party/jsoncpp/json.h2075
-rw-r--r--conformance/third_party/jsoncpp/jsoncpp.cpp5192
-rw-r--r--csharp/CHANGES.txt2
-rw-r--r--csharp/README.md48
-rwxr-xr-xcsharp/generate_protos.sh12
-rw-r--r--csharp/keys/Google.Protobuf.snkbin0 -> 596 bytes
-rw-r--r--csharp/keys/README.md6
-rw-r--r--csharp/src/AddressBook/Addressbook.cs23
-rw-r--r--csharp/src/Google.Protobuf.Conformance/Conformance.cs1458
-rw-r--r--csharp/src/Google.Protobuf.Conformance/Program.cs66
-rw-r--r--csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs56
-rw-r--r--csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs52
-rw-r--r--csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj5
-rw-r--r--csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs162
-rw-r--r--csharp/src/Google.Protobuf.Test/JsonParserTest.cs226
-rw-r--r--csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs14
-rw-r--r--csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs18
-rw-r--r--csharp/src/Google.Protobuf.Test/Reflection/TypeRegistryTest.cs94
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/ForeignMessagePartial.cs45
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs32
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs8
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs8
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs44
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs162
-rw-r--r--csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs79
-rw-r--r--csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs23
-rw-r--r--csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs38
-rw-r--r--csharp/src/Google.Protobuf.Test/WellKnownTypes/FieldMaskTest.cs62
-rw-r--r--csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs31
-rw-r--r--csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs35
-rw-r--r--csharp/src/Google.Protobuf/CodedInputStream.cs6
-rw-r--r--csharp/src/Google.Protobuf/Collections/MapField.cs69
-rw-r--r--csharp/src/Google.Protobuf/Collections/RepeatedField.cs9
-rw-r--r--csharp/src/Google.Protobuf/FieldCodec.cs143
-rw-r--r--csharp/src/Google.Protobuf/Google.Protobuf.csproj19
-rw-r--r--csharp/src/Google.Protobuf/Google.Protobuf.nuspec67
-rw-r--r--csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs69
-rw-r--r--csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs11
-rw-r--r--csharp/src/Google.Protobuf/JsonFormatter.cs310
-rw-r--r--csharp/src/Google.Protobuf/JsonParser.cs337
-rw-r--r--csharp/src/Google.Protobuf/JsonTokenizer.cs1046
-rw-r--r--csharp/src/Google.Protobuf/MessageExtensions.cs28
-rw-r--r--csharp/src/Google.Protobuf/MessageParser.cs8
-rw-r--r--csharp/src/Google.Protobuf/ProtoPreconditions.cs (renamed from csharp/src/Google.Protobuf/Preconditions.cs)9
-rw-r--r--csharp/src/Google.Protobuf/Reflection/Descriptor.cs540
-rw-r--r--csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs10
-rw-r--r--csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs33
-rw-r--r--csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs132
-rw-r--r--csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs103
-rw-r--r--csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs72
-rw-r--r--csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs178
-rw-r--r--csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs15
-rw-r--r--csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs183
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Any.cs18
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs2
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Api.cs31
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs8
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs121
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs14
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs10
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs122
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs10
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs18
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs14
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs92
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Type.cs168
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/ValuePartial.cs6
-rw-r--r--csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs51
-rw-r--r--csharp/src/Google.Protobuf/packages.config4
-rw-r--r--examples/Makefile21
-rw-r--r--examples/README.txt25
-rw-r--r--examples/add_person.go133
-rwxr-xr-xexamples/add_person.py10
-rw-r--r--examples/add_person_test.go58
-rw-r--r--examples/addressbook.proto20
-rw-r--r--examples/list_people.go61
-rwxr-xr-xexamples/list_people.py5
-rw-r--r--examples/list_people_test.go120
-rwxr-xr-xgenerate_descriptor_proto.sh39
-rw-r--r--gmock.BUILD5
-rw-r--r--java/core/generate-sources-build.xml20
-rw-r--r--java/core/generate-test-sources-build.xml43
-rw-r--r--java/core/pom.xml118
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractMessage.java (renamed from java/src/main/java/com/google/protobuf/AbstractMessage.java)13
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java (renamed from java/src/main/java/com/google/protobuf/AbstractMessageLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractParser.java (renamed from java/src/main/java/com/google/protobuf/AbstractParser.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java (renamed from java/src/main/java/com/google/protobuf/AbstractProtobufList.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/BlockingRpcChannel.java (renamed from java/src/main/java/com/google/protobuf/BlockingRpcChannel.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/BlockingService.java (renamed from java/src/main/java/com/google/protobuf/BlockingService.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/BooleanArrayList.java (renamed from java/src/main/java/com/google/protobuf/BooleanArrayList.java)11
-rw-r--r--java/core/src/main/java/com/google/protobuf/ByteString.java (renamed from java/src/main/java/com/google/protobuf/ByteString.java)601
-rw-r--r--java/core/src/main/java/com/google/protobuf/CodedInputStream.java (renamed from java/src/main/java/com/google/protobuf/CodedInputStream.java)205
-rw-r--r--java/core/src/main/java/com/google/protobuf/CodedOutputStream.java (renamed from java/src/main/java/com/google/protobuf/CodedOutputStream.java)91
-rw-r--r--java/core/src/main/java/com/google/protobuf/Descriptors.java (renamed from java/src/main/java/com/google/protobuf/Descriptors.java)43
-rw-r--r--java/core/src/main/java/com/google/protobuf/DoubleArrayList.java (renamed from java/src/main/java/com/google/protobuf/DoubleArrayList.java)11
-rw-r--r--java/core/src/main/java/com/google/protobuf/DynamicMessage.java (renamed from java/src/main/java/com/google/protobuf/DynamicMessage.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExperimentalApi.java36
-rw-r--r--java/core/src/main/java/com/google/protobuf/Extension.java (renamed from java/src/main/java/com/google/protobuf/Extension.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionLite.java (renamed from java/src/main/java/com/google/protobuf/ExtensionLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java (renamed from java/src/main/java/com/google/protobuf/ExtensionRegistry.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java (renamed from java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/FieldSet.java (renamed from java/src/main/java/com/google/protobuf/FieldSet.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/FloatArrayList.java (renamed from java/src/main/java/com/google/protobuf/FloatArrayList.java)11
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessage.java (renamed from java/src/main/java/com/google/protobuf/GeneratedMessage.java)60
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java (renamed from java/src/main/java/com/google/protobuf/GeneratedMessageLite.java)31
-rw-r--r--java/core/src/main/java/com/google/protobuf/IntArrayList.java (renamed from java/src/main/java/com/google/protobuf/IntArrayList.java)11
-rw-r--r--java/core/src/main/java/com/google/protobuf/Internal.java (renamed from java/src/main/java/com/google/protobuf/Internal.java)30
-rw-r--r--java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java (renamed from java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java)12
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyField.java (renamed from java/src/main/java/com/google/protobuf/LazyField.java)4
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyFieldLite.java (renamed from java/src/main/java/com/google/protobuf/LazyFieldLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java (renamed from java/src/main/java/com/google/protobuf/LazyStringArrayList.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyStringList.java (renamed from java/src/main/java/com/google/protobuf/LazyStringList.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/LongArrayList.java (renamed from java/src/main/java/com/google/protobuf/LongArrayList.java)11
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapEntry.java (renamed from java/src/main/java/com/google/protobuf/MapEntry.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapEntryLite.java (renamed from java/src/main/java/com/google/protobuf/MapEntryLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapField.java (renamed from java/src/main/java/com/google/protobuf/MapField.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapFieldLite.java (renamed from java/src/main/java/com/google/protobuf/MapFieldLite.java)11
-rw-r--r--java/core/src/main/java/com/google/protobuf/Message.java (renamed from java/src/main/java/com/google/protobuf/Message.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageLite.java (renamed from java/src/main/java/com/google/protobuf/MessageLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java (renamed from java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageLiteToString.java (renamed from python/google/protobuf/util/__init__.py)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java (renamed from java/src/main/java/com/google/protobuf/MessageOrBuilder.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageReflection.java (renamed from java/src/main/java/com/google/protobuf/MessageReflection.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/MutabilityOracle.java (renamed from java/src/main/java/com/google/protobuf/MutabilityOracle.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/NioByteString.java309
-rw-r--r--java/core/src/main/java/com/google/protobuf/Parser.java (renamed from java/src/main/java/com/google/protobuf/Parser.java)12
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java (renamed from java/src/main/java/com/google/protobuf/ProtobufArrayList.java)4
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java (renamed from java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtocolStringList.java (renamed from java/src/main/java/com/google/protobuf/ProtocolStringList.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java (renamed from java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/RopeByteString.java (renamed from java/src/main/java/com/google/protobuf/RopeByteString.java)258
-rw-r--r--java/core/src/main/java/com/google/protobuf/RpcCallback.java (renamed from java/src/main/java/com/google/protobuf/RpcCallback.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/RpcChannel.java (renamed from java/src/main/java/com/google/protobuf/RpcChannel.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/RpcController.java (renamed from java/src/main/java/com/google/protobuf/RpcController.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/RpcUtil.java (renamed from java/src/main/java/com/google/protobuf/RpcUtil.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/Service.java (renamed from java/src/main/java/com/google/protobuf/Service.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/ServiceException.java (renamed from java/src/main/java/com/google/protobuf/ServiceException.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java (renamed from java/src/main/java/com/google/protobuf/SingleFieldBuilder.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/SmallSortedMap.java (renamed from java/src/main/java/com/google/protobuf/SmallSortedMap.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormat.java (renamed from java/src/main/java/com/google/protobuf/TextFormat.java)54
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java0
-rw-r--r--java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java (renamed from java/src/main/java/com/google/protobuf/UninitializedMessageException.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java (renamed from java/src/main/java/com/google/protobuf/UnknownFieldSet.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java (renamed from java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java (renamed from java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java63
-rw-r--r--java/core/src/main/java/com/google/protobuf/Utf8.java (renamed from java/src/main/java/com/google/protobuf/Utf8.java)0
-rw-r--r--java/core/src/main/java/com/google/protobuf/WireFormat.java (renamed from java/src/main/java/com/google/protobuf/WireFormat.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java (renamed from java/src/test/java/com/google/protobuf/AbstractMessageTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/AnyTest.java (renamed from java/src/test/java/com/google/protobuf/AnyTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/BooleanArrayListTest.java)8
-rw-r--r--java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java (renamed from java/src/test/java/com/google/protobuf/BoundedByteStringTest.java)6
-rw-r--r--java/core/src/test/java/com/google/protobuf/ByteStringTest.java (renamed from java/src/test/java/com/google/protobuf/ByteStringTest.java)24
-rw-r--r--java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java (renamed from java/src/test/java/com/google/protobuf/CheckUtf8Test.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java (renamed from java/src/test/java/com/google/protobuf/CodedInputStreamTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java (renamed from java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java)45
-rw-r--r--java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java (renamed from java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/DescriptorsTest.java (renamed from java/src/test/java/com/google/protobuf/DescriptorsTest.java)9
-rw-r--r--java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/DoubleArrayListTest.java)6
-rw-r--r--java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java (renamed from java/src/test/java/com/google/protobuf/DynamicMessageTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java (renamed from java/src/test/java/com/google/protobuf/FieldPresenceTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/FloatArrayListTest.java)6
-rw-r--r--java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java (renamed from java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java (renamed from java/src/test/java/com/google/protobuf/GeneratedMessageTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/IntArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/IntArrayListTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java (renamed from java/src/test/java/com/google/protobuf/IsValidUtf8Test.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java (renamed from java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java (renamed from java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyFieldTest.java (renamed from java/src/test/java/com/google/protobuf/LazyFieldTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java (renamed from java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java (renamed from java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java (renamed from java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LiteTest.java (renamed from java/src/test/java/com/google/protobuf/LiteTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java (renamed from java/src/test/java/com/google/protobuf/LiteralByteStringTest.java)20
-rw-r--r--java/core/src/test/java/com/google/protobuf/LongArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/LongArrayListTest.java)6
-rw-r--r--java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java (renamed from java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/MapForProto2Test.java (renamed from java/src/test/java/com/google/protobuf/MapForProto2Test.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/MapTest.java (renamed from java/src/test/java/com/google/protobuf/MapTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/MessageTest.java (renamed from java/src/test/java/com/google/protobuf/MessageTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java (renamed from java/src/test/java/com/google/protobuf/NestedBuildersTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/NioByteStringTest.java545
-rw-r--r--java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java211
-rw-r--r--java/core/src/test/java/com/google/protobuf/ParserTest.java (renamed from java/src/test/java/com/google/protobuf/ParserTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java (renamed from java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java)4
-rw-r--r--java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java (renamed from java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java (renamed from java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java)2
-rw-r--r--java/core/src/test/java/com/google/protobuf/RopeByteStringTest.java (renamed from java/src/test/java/com/google/protobuf/RopeByteStringTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/ServiceTest.java (renamed from java/src/test/java/com/google/protobuf/ServiceTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java (renamed from java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java (renamed from java/src/test/java/com/google/protobuf/SmallSortedMapTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java (renamed from java/src/test/java/com/google/protobuf/TestBadIdentifiers.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/TestUtil.java (renamed from java/src/test/java/com/google/protobuf/TestUtil.java)154
-rw-r--r--java/core/src/test/java/com/google/protobuf/TextFormatTest.java (renamed from java/src/test/java/com/google/protobuf/TextFormatTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java (renamed from java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java (renamed from java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java (renamed from java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java (renamed from java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/WellKnownTypesTest.java (renamed from java/src/test/java/com/google/protobuf/WellKnownTypesTest.java)0
-rw-r--r--java/core/src/test/java/com/google/protobuf/WireFormatTest.java (renamed from java/src/test/java/com/google/protobuf/WireFormatTest.java)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/any_test.proto (renamed from java/src/test/java/com/google/protobuf/any_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/field_presence_test.proto (renamed from java/src/test/java/com/google/protobuf/field_presence_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto (renamed from java/src/test/java/com/google/protobuf/lazy_fields_lite.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto (renamed from java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto (renamed from java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto (renamed from java/src/test/java/com/google/protobuf/map_for_proto2_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto (renamed from java/src/test/java/com/google/protobuf/map_initialization_order_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_test.proto (renamed from java/src/test/java/com/google/protobuf/map_test.proto)1
-rw-r--r--java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto (renamed from java/src/test/java/com/google/protobuf/multiple_files_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto (renamed from java/src/test/java/com/google/protobuf/nested_builders_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/nested_extension.proto (renamed from java/src/test/java/com/google/protobuf/nested_extension.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto (renamed from java/src/test/java/com/google/protobuf/nested_extension_lite.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto (renamed from java/src/test/java/com/google/protobuf/non_nested_extension.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto (renamed from java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto (renamed from java/src/test/java/com/google/protobuf/outer_class_name_test.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto (renamed from java/src/test/java/com/google/protobuf/outer_class_name_test2.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto (renamed from java/src/test/java/com/google/protobuf/outer_class_name_test3.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto (renamed from java/src/test/java/com/google/protobuf/test_bad_identifiers.proto)1
-rw-r--r--java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto (renamed from java/src/test/java/com/google/protobuf/test_check_utf8.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto (renamed from java/src/test/java/com/google/protobuf/test_check_utf8_size.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/test_custom_options.proto (renamed from java/src/test/java/com/google/protobuf/test_custom_options.proto)0
-rw-r--r--java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto (renamed from java/src/test/java/com/google/protobuf/test_extra_interfaces.proto)0
-rw-r--r--java/lite/pom.xml156
-rw-r--r--java/pom.xml417
-rw-r--r--java/src/main/java/com/google/protobuf/BoundedByteString.java180
-rw-r--r--java/src/main/java/com/google/protobuf/LiteralByteString.java372
-rw-r--r--java/util/pom.xml185
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java79
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/JsonFormat.java226
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/TimeUtil.java12
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java48
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java390
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java67
-rw-r--r--java/util/src/test/proto/com/google/protobuf/util/json_test.proto (renamed from java/util/src/test/java/com/google/protobuf/util/json_test.proto)12
-rw-r--r--javanano/pom.xml4
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/FieldArray.java4
-rw-r--r--js/README.md14
-rw-r--r--js/binary/arith.js413
-rw-r--r--js/binary/arith_test.js355
-rw-r--r--js/binary/constants.js320
-rw-r--r--js/binary/decoder.js1005
-rw-r--r--js/binary/decoder_test.js327
-rw-r--r--js/binary/proto_test.js487
-rw-r--r--js/binary/reader.js1127
-rw-r--r--js/binary/reader_test.js889
-rw-r--r--js/binary/utils.js979
-rw-r--r--js/binary/utils_test.js632
-rw-r--r--js/binary/writer.js2124
-rw-r--r--js/binary/writer_test.js123
-rw-r--r--js/data.proto51
-rw-r--r--js/debug.js140
-rw-r--r--js/debug_test.js101
-rw-r--r--js/gulpfile.js29
-rw-r--r--js/jasmine.json12
-rw-r--r--js/message.js1125
-rw-r--r--js/message_test.js974
-rw-r--r--js/node_loader.js49
-rw-r--r--js/package.json21
-rw-r--r--js/proto3_test.js279
-rw-r--r--js/proto3_test.proto89
-rw-r--r--js/test.proto212
-rw-r--r--js/test2.proto54
-rw-r--r--js/test3.proto53
-rw-r--r--js/test4.proto (renamed from src/google/protobuf/compiler/cpp/test_large_enum_value.proto)17
-rw-r--r--js/test5.proto44
-rw-r--r--js/test_bootstrap.js41
-rw-r--r--js/testbinary.proto185
-rw-r--r--js/testempty.proto34
-rw-r--r--m4/acx_check_suncc.m417
-rw-r--r--m4/stl_hash.m43
-rwxr-xr-xobjectivec/DevTools/full_mac_build.sh27
-rw-r--r--objectivec/GPBCodedInputStream.m335
-rw-r--r--objectivec/GPBCodedInputStream_PackagePrivate.h17
-rw-r--r--objectivec/GPBDictionary.m166
-rw-r--r--objectivec/GPBMessage.h2
-rw-r--r--objectivec/GPBMessage.m38
-rw-r--r--objectivec/GPBMessage_PackagePrivate.h7
-rw-r--r--objectivec/GPBProtocolBuffers.h1
-rw-r--r--objectivec/GPBRootObject.m28
-rw-r--r--objectivec/GPBUtilities.h2
-rw-r--r--objectivec/GPBUtilities.m4
-rw-r--r--objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj12
-rw-r--r--objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme19
-rw-r--r--objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme13
-rw-r--r--objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj12
-rw-r--r--objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme19
-rw-r--r--objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme13
-rw-r--r--objectivec/README.md4
-rw-r--r--objectivec/Tests/GPBCodedInputStreamTests.m10
-rw-r--r--objectivec/Tests/GPBMessageTests+Merge.m4
-rw-r--r--objectivec/Tests/GPBMessageTests+Runtime.m1
-rw-r--r--objectivec/Tests/GPBMessageTests+Serialization.m171
-rw-r--r--objectivec/Tests/GPBMessageTests.m24
-rw-r--r--objectivec/Tests/GPBStringTests.m513
-rw-r--r--objectivec/Tests/GPBSwiftTests.swift16
-rw-r--r--objectivec/Tests/GPBWireFormatTests.m1
-rw-r--r--objectivec/Tests/UnitTests-Info.plist2
-rw-r--r--objectivec/Tests/iOSTestHarness/Info.plist2
-rw-r--r--objectivec/google/protobuf/Any.pbobjc.h9
-rw-r--r--objectivec/google/protobuf/Api.pbobjc.h3
-rw-r--r--objectivec/google/protobuf/Descriptor.pbobjc.h54
-rw-r--r--objectivec/google/protobuf/Descriptor.pbobjc.m149
-rw-r--r--objectivec/google/protobuf/Duration.pbobjc.h2
-rw-r--r--objectivec/google/protobuf/Empty.pbobjc.h2
-rw-r--r--objectivec/google/protobuf/FieldMask.pbobjc.h4
-rw-r--r--objectivec/google/protobuf/SourceContext.pbobjc.h2
-rw-r--r--objectivec/google/protobuf/Struct.pbobjc.h2
-rw-r--r--objectivec/google/protobuf/Timestamp.pbobjc.h2
-rw-r--r--objectivec/google/protobuf/Type.pbobjc.h59
-rw-r--r--objectivec/google/protobuf/Type.pbobjc.m13
-rw-r--r--objectivec/google/protobuf/Wrappers.pbobjc.h2
-rwxr-xr-xpost_process_dist.sh2
-rw-r--r--protobuf.bzl19
-rw-r--r--protoc-artifacts/README.md4
-rw-r--r--protoc-artifacts/pom.xml2
-rwxr-xr-xpython/google/protobuf/__init__.py2
-rwxr-xr-xpython/google/protobuf/descriptor.py12
-rw-r--r--python/google/protobuf/descriptor_database.py4
-rw-r--r--python/google/protobuf/descriptor_pool.py69
-rw-r--r--python/google/protobuf/internal/any_test.proto42
-rwxr-xr-xpython/google/protobuf/internal/containers.py23
-rw-r--r--python/google/protobuf/internal/descriptor_pool_test.py176
-rwxr-xr-xpython/google/protobuf/internal/descriptor_test.py20
-rw-r--r--python/google/protobuf/internal/json_format_test.py302
-rw-r--r--python/google/protobuf/internal/message_factory_test.py7
-rw-r--r--python/google/protobuf/internal/message_set_extensions.proto8
-rwxr-xr-xpython/google/protobuf/internal/message_test.py81
-rwxr-xr-xpython/google/protobuf/internal/python_message.py45
-rwxr-xr-xpython/google/protobuf/internal/reflection_test.py18
-rwxr-xr-xpython/google/protobuf/internal/text_format_test.py170
-rw-r--r--python/google/protobuf/internal/well_known_types.py720
-rw-r--r--python/google/protobuf/internal/well_known_types_test.py636
-rw-r--r--python/google/protobuf/json_format.py443
-rw-r--r--python/google/protobuf/message_factory.py4
-rw-r--r--python/google/protobuf/pyext/descriptor.cc29
-rw-r--r--python/google/protobuf/pyext/descriptor_database.cc145
-rw-r--r--python/google/protobuf/pyext/descriptor_database.h75
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.cc111
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.h10
-rw-r--r--python/google/protobuf/pyext/extension_dict.cc47
-rw-r--r--python/google/protobuf/pyext/extension_dict.h9
-rw-r--r--python/google/protobuf/pyext/map_container.cc965
-rw-r--r--python/google/protobuf/pyext/map_container.h (renamed from python/google/protobuf/pyext/message_map_container.h)71
-rw-r--r--python/google/protobuf/pyext/message.cc299
-rw-r--r--python/google/protobuf/pyext/message.h6
-rw-r--r--python/google/protobuf/pyext/message_map_container.cc569
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.h4
-rw-r--r--python/google/protobuf/pyext/repeated_scalar_container.h4
-rw-r--r--python/google/protobuf/pyext/scalar_map_container.cc542
-rw-r--r--python/google/protobuf/pyext/scalar_map_container.h115
-rw-r--r--python/google/protobuf/symbol_database.py32
-rwxr-xr-xpython/google/protobuf/text_format.py264
-rwxr-xr-xpython/setup.py22
-rw-r--r--python/tox.ini4
-rw-r--r--ruby/Gemfile.lock6
-rw-r--r--ruby/Rakefile11
-rw-r--r--ruby/google-protobuf.gemspec13
-rw-r--r--ruby/lib/google/protobuf.rb6
-rwxr-xr-xruby/travis-test.sh5
-rw-r--r--six.BUILD1
-rw-r--r--src/Makefile.am19
-rw-r--r--src/README.md209
-rw-r--r--src/google/protobuf/any.cc13
-rw-r--r--src/google/protobuf/any.h2
-rw-r--r--src/google/protobuf/any.pb.cc7
-rw-r--r--src/google/protobuf/any.pb.h2
-rw-r--r--src/google/protobuf/any.proto17
-rw-r--r--src/google/protobuf/api.pb.cc15
-rw-r--r--src/google/protobuf/api.pb.h4
-rw-r--r--src/google/protobuf/api.proto5
-rw-r--r--src/google/protobuf/arena.h87
-rwxr-xr-xsrc/google/protobuf/arenastring.h13
-rw-r--r--src/google/protobuf/compiler/command_line_interface.cc64
-rw-r--r--src/google/protobuf/compiler/command_line_interface.h3
-rw-r--r--src/google/protobuf/compiler/command_line_interface_unittest.cc30
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_file.cc16
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_helpers.cc5
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_helpers.h2
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message.cc28
-rw-r--r--src/google/protobuf/compiler/cpp/metadata_test.cc (renamed from src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc)41
-rw-r--r--src/google/protobuf/compiler/csharp/csharp_map_field.cc3
-rw-r--r--src/google/protobuf/compiler/csharp/csharp_message.cc2
-rw-r--r--src/google/protobuf/compiler/csharp/csharp_primitive_field.cc4
-rw-r--r--src/google/protobuf/compiler/csharp/csharp_reflection_class.cc10
-rw-r--r--src/google/protobuf/compiler/importer.cc13
-rw-r--r--src/google/protobuf/compiler/importer.h9
-rw-r--r--src/google/protobuf/compiler/importer_unittest.cc8
-rw-r--r--src/google/protobuf/compiler/java/java_enum.cc16
-rw-r--r--src/google/protobuf/compiler/java/java_enum_field_lite.cc40
-rw-r--r--src/google/protobuf/compiler/java/java_enum_lite.cc32
-rw-r--r--src/google/protobuf/compiler/java/java_extension_lite.cc0
-rw-r--r--src/google/protobuf/compiler/java/java_extension_lite.h0
-rw-r--r--src/google/protobuf/compiler/java/java_file.cc34
-rw-r--r--src/google/protobuf/compiler/java/java_message.cc17
-rw-r--r--src/google/protobuf/compiler/java/java_message_builder.cc2
-rw-r--r--src/google/protobuf/compiler/java/java_primitive_field_lite.cc38
-rw-r--r--src/google/protobuf/compiler/java/java_string_field.cc80
-rw-r--r--src/google/protobuf/compiler/java/java_string_field.h1
-rw-r--r--src/google/protobuf/compiler/java/java_string_field_lite.cc66
-rw-r--r--src/google/protobuf/compiler/java/java_string_field_lite.h1
-rwxr-xr-xsrc/google/protobuf/compiler/js/js_generator.cc2622
-rwxr-xr-xsrc/google/protobuf/compiler/js/js_generator.h265
-rw-r--r--src/google/protobuf/compiler/main.cc6
-rw-r--r--src/google/protobuf/compiler/mock_code_generator.cc6
-rw-r--r--src/google/protobuf/compiler/objectivec/objectivec_file.cc2
-rw-r--r--src/google/protobuf/compiler/parser.cc64
-rw-r--r--src/google/protobuf/compiler/parser.h2
-rw-r--r--src/google/protobuf/compiler/parser_unittest.cc56
-rw-r--r--src/google/protobuf/compiler/plugin.pb.cc3
-rw-r--r--src/google/protobuf/compiler/plugin.pb.h2
-rw-r--r--src/google/protobuf/compiler/subprocess.cc1
-rw-r--r--src/google/protobuf/descriptor.cc66
-rw-r--r--src/google/protobuf/descriptor.h10
-rw-r--r--src/google/protobuf/descriptor.pb.cc972
-rw-r--r--src/google/protobuf/descriptor.pb.h409
-rw-r--r--src/google/protobuf/descriptor.proto28
-rw-r--r--src/google/protobuf/descriptor_unittest.cc193
-rw-r--r--src/google/protobuf/duration.pb.cc7
-rw-r--r--src/google/protobuf/duration.proto9
-rw-r--r--src/google/protobuf/dynamic_message.cc2
-rw-r--r--src/google/protobuf/empty.pb.cc49
-rw-r--r--src/google/protobuf/empty.pb.h13
-rw-r--r--src/google/protobuf/empty.proto8
-rw-r--r--src/google/protobuf/field_mask.pb.cc7
-rw-r--r--src/google/protobuf/field_mask.proto11
-rw-r--r--src/google/protobuf/io/coded_stream.cc18
-rw-r--r--src/google/protobuf/io/coded_stream.h12
-rw-r--r--src/google/protobuf/io/strtod.cc11
-rw-r--r--src/google/protobuf/io/strtod.h5
-rw-r--r--src/google/protobuf/io/tokenizer.cc2
-rw-r--r--src/google/protobuf/io/tokenizer_unittest.cc2
-rw-r--r--src/google/protobuf/io/zero_copy_stream_impl_lite.cc31
-rw-r--r--src/google/protobuf/io/zero_copy_stream_impl_lite.h24
-rw-r--r--src/google/protobuf/map.h22
-rw-r--r--src/google/protobuf/message.h32
-rw-r--r--src/google/protobuf/repeated_field.h32
-rw-r--r--src/google/protobuf/source_context.pb.cc7
-rw-r--r--src/google/protobuf/source_context.proto7
-rw-r--r--src/google/protobuf/struct.pb.cc15
-rw-r--r--src/google/protobuf/struct.proto9
-rw-r--r--src/google/protobuf/stubs/atomicops.h2
-rw-r--r--src/google/protobuf/stubs/callback.h83
-rw-r--r--src/google/protobuf/stubs/platform_macros.h4
-rw-r--r--src/google/protobuf/stubs/port.h2
-rw-r--r--src/google/protobuf/stubs/statusor.h4
-rw-r--r--src/google/protobuf/stubs/stringpiece_unittest.cc17
-rw-r--r--src/google/protobuf/stubs/strutil.cc86
-rw-r--r--src/google/protobuf/stubs/strutil.h24
-rw-r--r--src/google/protobuf/text_format.cc39
-rw-r--r--src/google/protobuf/text_format.h14
-rw-r--r--src/google/protobuf/timestamp.pb.cc49
-rw-r--r--src/google/protobuf/timestamp.pb.h13
-rw-r--r--src/google/protobuf/timestamp.proto11
-rw-r--r--src/google/protobuf/type.pb.cc183
-rw-r--r--src/google/protobuf/type.pb.h59
-rw-r--r--src/google/protobuf/type.proto60
-rw-r--r--src/google/protobuf/unittest_custom_options.proto27
-rw-r--r--src/google/protobuf/unittest_well_known_types.proto2
-rw-r--r--src/google/protobuf/util/field_comparator.h2
-rw-r--r--src/google/protobuf/util/field_comparator_test.cc7
-rw-r--r--src/google/protobuf/util/internal/datapiece.cc74
-rw-r--r--src/google/protobuf/util/internal/datapiece.h3
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter.cc90
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter.h23
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter_test.cc35
-rw-r--r--src/google/protobuf/util/internal/error_listener.h2
-rw-r--r--src/google/protobuf/util/internal/json_escaping.cc4
-rw-r--r--src/google/protobuf/util/internal/json_objectwriter_test.cc205
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser.cc2
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser_test.cc1
-rw-r--r--src/google/protobuf/util/internal/object_writer.h5
-rw-r--r--src/google/protobuf/util/internal/proto_writer.cc744
-rw-r--r--src/google/protobuf/util/internal/proto_writer.h315
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.cc187
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.h15
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource_test.cc10
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.cc1560
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.h318
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter_test.cc134
-rw-r--r--src/google/protobuf/util/internal/snake2camel_objectwriter.h165
-rw-r--r--src/google/protobuf/util/internal/testdata/books.proto2
-rw-r--r--src/google/protobuf/util/internal/testdata/default_value.proto7
-rw-r--r--src/google/protobuf/util/internal/testdata/default_value_test.proto7
-rw-r--r--src/google/protobuf/util/internal/testdata/maps.proto29
-rw-r--r--src/google/protobuf/util/internal/type_info.cc3
-rw-r--r--src/google/protobuf/util/internal/utility.cc29
-rw-r--r--src/google/protobuf/util/internal/utility.h5
-rw-r--r--src/google/protobuf/util/json_format_proto3.proto9
-rw-r--r--src/google/protobuf/util/message_differencer.cc22
-rw-r--r--src/google/protobuf/util/message_differencer.h10
-rwxr-xr-xsrc/google/protobuf/util/message_differencer_unittest.cc58
-rw-r--r--src/google/protobuf/util/type_resolver_util.cc45
-rw-r--r--src/google/protobuf/util/type_resolver_util_test.cc14
-rw-r--r--src/google/protobuf/wire_format_lite_inl.h22
-rw-r--r--src/google/protobuf/wrappers.pb.cc467
-rw-r--r--src/google/protobuf/wrappers.pb.h205
-rw-r--r--src/google/protobuf/wrappers.proto9
-rwxr-xr-xtravis.sh52
-rw-r--r--util/python/BUILD8
532 files changed, 45813 insertions, 9940 deletions
diff --git a/.gitignore b/.gitignore
index b0a92f82..a2d6ca9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ python/.eggs/
python/.tox
python/build/
python/google/protobuf/compiler/
+python/google/protobuf/util/
src/protoc
src/unittest_proto_middleman
@@ -103,9 +104,12 @@ conformance/com/
conformance/conformance-cpp
conformance/conformance-csharp
conformance/conformance-java
+conformance/conformance-objc
conformance/conformance-test-runner
conformance/conformance.pb.cc
conformance/conformance.pb.h
+conformance/Conformance.pbobjc.h
+conformance/Conformance.pbobjc.m
conformance/conformance.rb
conformance/javac_middleman
conformance/protoc_middleman
diff --git a/.travis.yml b/.travis.yml
index 7be0d18e..e368826f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,12 +15,14 @@ env:
- CONFIG=cpp
- CONFIG=cpp_distcheck
- CONFIG=csharp
+ - CONFIG=golang
- CONFIG=java_jdk6
- CONFIG=java_jdk7
- CONFIG=java_oracle7
- CONFIG=javanano_jdk6
- CONFIG=javanano_jdk7
- CONFIG=javanano_oracle7
+ - CONFIG=javascript
- CONFIG=python
- CONFIG=python_cpp
- CONFIG=ruby19
@@ -48,6 +50,10 @@ matrix:
# which doesn't work on OS X.
- os: osx
env: CONFIG=csharp
+ # Requires installing golang, currently travis.sh is doing that with apt-get
+ # which doesn't work on OS X.
+ - os: osx
+ env: CONFIG=golang
# Add into the matrix OS X tests of Objective C (needs Xcode, so it won't
# work on other platforms). These are split so it doesn't take as long to run.
include:
@@ -61,9 +67,5 @@ matrix:
env: CONFIG=ruby22
- os: osx
env: CONFIG=jruby
- # Travis seems be flaky in letting the iOS simulator launch, so keep that
- # flake from failing builds by marking that build as an allowed failure.
- - os: osx
- env: CONFIG=objectivec_ios
notifications:
email: false
diff --git a/BUILD b/BUILD
index 97e3f45b..3cac4a86 100644
--- a/BUILD
+++ b/BUILD
@@ -54,9 +54,9 @@ cc_library(
"src/google/protobuf/stubs/time.cc",
"src/google/protobuf/wire_format_lite.cc",
],
+ hdrs = glob(["src/google/protobuf/**/*.h"]),
copts = COPTS,
includes = ["src/"],
- hdrs = glob(["src/google/protobuf/**/*.h"]),
linkopts = LINK_OPTS,
visibility = ["//visibility:public"],
)
@@ -106,6 +106,7 @@ cc_library(
"src/google/protobuf/util/internal/json_objectwriter.cc",
"src/google/protobuf/util/internal/json_stream_parser.cc",
"src/google/protobuf/util/internal/object_writer.cc",
+ "src/google/protobuf/util/internal/proto_writer.cc",
"src/google/protobuf/util/internal/protostream_objectsource.cc",
"src/google/protobuf/util/internal/protostream_objectwriter.cc",
"src/google/protobuf/util/internal/type_info.cc",
@@ -156,9 +157,9 @@ cc_proto_library(
name = "cc_wkt_protos",
srcs = WELL_KNOWN_PROTOS,
include = "src",
+ default_runtime = ":protobuf",
internal_bootstrap_hack = 1,
protoc = ":protoc",
- default_runtime = ":protobuf",
visibility = ["//visibility:public"],
)
@@ -241,6 +242,7 @@ cc_library(
"src/google/protobuf/compiler/javanano/javanano_message.cc",
"src/google/protobuf/compiler/javanano/javanano_message_field.cc",
"src/google/protobuf/compiler/javanano/javanano_primitive_field.cc",
+ "src/google/protobuf/compiler/js/js_generator.cc",
"src/google/protobuf/compiler/objectivec/objectivec_enum.cc",
"src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc",
"src/google/protobuf/compiler/objectivec/objectivec_extension.cc",
@@ -328,6 +330,7 @@ RELATIVE_TEST_PROTOS = [
"google/protobuf/util/internal/testdata/struct.proto",
"google/protobuf/util/internal/testdata/timestamp_duration.proto",
"google/protobuf/util/json_format_proto3.proto",
+ "google/protobuf/util/message_differencer_unittest.proto",
]
TEST_PROTOS = ["src/" + s for s in RELATIVE_TEST_PROTOS]
@@ -336,8 +339,8 @@ cc_proto_library(
name = "cc_test_protos",
srcs = LITE_TEST_PROTOS + TEST_PROTOS,
include = "src",
- protoc = ":protoc",
default_runtime = ":protobuf",
+ protoc = ":protoc",
deps = [":cc_wkt_protos"],
)
@@ -428,6 +431,7 @@ cc_test(
"src/google/protobuf/util/internal/protostream_objectwriter_test.cc",
"src/google/protobuf/util/internal/type_info_test_helper.cc",
"src/google/protobuf/util/json_util_test.cc",
+ "src/google/protobuf/util/message_differencer_unittest.cc",
"src/google/protobuf/util/time_util_test.cc",
"src/google/protobuf/util/type_resolver_util_test.cc",
"src/google/protobuf/well_known_types_unittest.cc",
@@ -455,19 +459,23 @@ cc_test(
# Java support
################################################################################
genrule(
- name = "generate_java_descriptor_proto",
- srcs = ["src/google/protobuf/descriptor.proto"],
- outs = ["com/google/protobuf/DescriptorProtos.java"],
- cmd = "$(location :protoc) --java_out=$(@D)/../../.. $<",
+ name = "gen_well_known_protos_java",
+ srcs = WELL_KNOWN_PROTOS,
+ outs = [
+ "wellknown.srcjar"
+ ],
+ cmd = "$(location :protoc) --java_out=$(@D)/wellknown.jar" +
+ " -Isrc $(SRCS) " +
+ " && mv $(@D)/wellknown.jar $(@D)/wellknown.srcjar",
tools = [":protoc"],
)
java_library(
name = "protobuf_java",
srcs = glob([
- "java/src/main/java/com/google/protobuf/*.java",
+ "java/core/src/main/java/com/google/protobuf/*.java",
]) + [
- ":generate_java_descriptor_proto",
+ ":gen_well_known_protos_java",
],
visibility = ["//visibility:public"],
)
@@ -509,14 +517,66 @@ internal_copied_filegroup(
include = "python",
)
+cc_binary(
+ name = "internal/_api_implementation.so",
+ srcs = ["python/google/protobuf/internal/api_implementation.cc"],
+ copts = COPTS + [
+ "-DPYTHON_PROTO2_CPP_IMPL_V2",
+ ],
+ linkshared = 1,
+ linkstatic = 1,
+ deps = select({
+ "//conditions:default": [],
+ ":use_fast_cpp_protos": ["//util/python:python_headers"],
+ }),
+)
+
+cc_binary(
+ name = "pyext/_message.so",
+ srcs = glob([
+ "python/google/protobuf/pyext/*.cc",
+ "python/google/protobuf/pyext/*.h",
+ ]),
+ copts = COPTS + [
+ "-DGOOGLE_PROTOBUF_HAS_ONEOF=1",
+ ],
+ includes = [
+ "python/",
+ "src/",
+ ],
+ linkshared = 1,
+ linkstatic = 1,
+ deps = [
+ ":protobuf",
+ ] + select({
+ "//conditions:default": [],
+ ":use_fast_cpp_protos": ["//util/python:python_headers"],
+ }),
+)
+
+config_setting(
+ name = "use_fast_cpp_protos",
+ values = {
+ "define": "use_fast_cpp_protos=true",
+ },
+)
+
py_proto_library(
name = "protobuf_python",
srcs = WELL_KNOWN_PROTOS,
include = "src",
+ data = select({
+ "//conditions:default": [],
+ ":use_fast_cpp_protos": [
+ ":internal/_api_implementation.so",
+ ":pyext/_message.so",
+ ],
+ }),
+ default_runtime = "",
protoc = ":protoc",
py_extra_srcs = [":python_srcs"],
py_libs = ["//external:six"],
- default_runtime = "",
+ srcs_version = "PY2AND3",
visibility = ["//visibility:public"],
)
@@ -535,9 +595,9 @@ py_proto_library(
name = "python_common_test_protos",
srcs = LITE_TEST_PROTOS + TEST_PROTOS,
include = "src",
+ default_runtime = "",
protoc = ":protoc",
deps = [":protobuf_python"],
- default_runtime = "",
)
py_proto_library(
@@ -547,14 +607,15 @@ py_proto_library(
"python/google/protobuf/internal/import_test_package/*.proto",
]),
include = "python",
+ default_runtime = ":protobuf_python",
protoc = ":protoc",
deps = [":python_common_test_protos"],
- default_runtime = ":protobuf_python",
)
py_library(
name = "python_tests",
srcs = [":python_test_srcs"],
+ srcs_version = "PY2AND3",
deps = [
":protobuf_python",
":python_common_test_protos",
diff --git a/CHANGES.txt b/CHANGES.txt
index ca3078c3..3023c812 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,64 @@
+2015-08-26 version 3.0.0-beta-1 (C++/Java/Python/Ruby/Nano/Objective-C/C#/JavaScript)
+ General
+ * Introduced a new language implementation: JavaScript.
+ * Added a new field option "json_name". By default proto field names are
+ converted to "lowerCamelCase" in proto3 JSON format. This option can be
+ used to override this behavior and specify a different JSON name for the
+ field.
+ * Added conformance tests to ensure implementations are following proto3 JSON
+ specification.
+
+ C++ (Beta)
+ * Various bug fixes and improvements to the JSON support utility:
+ - Duplicate map keys in JSON are now rejected (i.e., translation will
+ fail).
+ - Fixed wire-format for google.protobuf.Value/ListValue.
+ - Fixed precision loss when converting google.protobuf.Timestamp.
+ - Fixed a bug when parsing invalid UTF-8 code points.
+ - Fixed a memory leak.
+ - Reduced call stack usage.
+
+ Java (Beta)
+ * Cleaned up some unused methods on CodedOutputStream.
+ * Presized lists for packed fields during parsing in the lite runtime to
+ reduce allocations and improve performance.
+ * Improved the performance of unknown fields in the lite runtime.
+ * Introduced UnsafeByteStrings to support zero-copy ByteString creation.
+ * Various bug fixes and improvements to the JSON support utility:
+ - Fixed a thread-safety bug.
+ - Added a new option “preservingProtoFieldNames” to JsonFormat.
+ - Added a new option “includingDefaultValueFields” to JsonFormat.
+ - Updated the JSON utility to comply with proto3 JSON specification.
+
+ Python (Beta)
+ * Added proto3 JSON format utility. It includes support for all field types
+ and a few well-known types except for Any and Struct.
+ * Added runtime support for Any, Timestamp, Duration and FieldMask.
+ * [ ] is now accepted for repeated scalar fields in text format parser.
+
+ Objective-C (Beta)
+ * Various bug-fixes and code tweaks to pass more strict compiler warnings.
+ * Now has conformance test coverage and is passing all tests.
+
+ C# (Beta)
+ * Various bug-fixes.
+ * Code generation: Files generated in directories based on namespace.
+ * Code generation: Include comments from .proto files in XML doc
+ comments (naively)
+ * Code generation: Change organization/naming of "reflection class" (access
+ to file descriptor)
+ * Code generation and library: Add Parser property to MessageDescriptor,
+ and introduce a non-generic parser type.
+ * Library: Added TypeRegistry to support JSON parsing/formatting of Any.
+ * Library: Added Any.Pack/Unpack support.
+ * Library: Implemented JSON parsing.
+
+ Javascript (Alpha)
+ * Added proto3 support for JavaScript. The runtime is written in pure
+ JavaScript and works in browsers and in Node.js. To generate JavaScript
+ code for your proto, invoke protoc with "--js_out". See js/README.md
+ for more build instructions.
+
2015-08-26 version 3.0.0-beta-1 (C++/Java/Python/Ruby/Nano/Objective-C/C#)
About Beta
* This is the first beta release of protobuf v3.0.0. Not all languages
@@ -115,7 +176,7 @@
still disable packed serialization by setting packed to false for now.
* Added well-known type protos (any.proto, empty.proto, timestamp.proto,
duration.proto, etc.). Users can import and use these protos just like
- regular proto files. Addtional runtime support will be added for them in
+ regular proto files. Additional runtime support will be added for them in
future releases (in the form of utility helper functions, or having them
replaced by language specific types in generated code).
* Added a "reserved" keyword in both proto2 and proto3 syntax. User can use
@@ -157,7 +218,7 @@
https://github.com/jskeet/protobuf-csharp-port. The original project was
frozen and all the new development will happen here.
* Codegen plugin for C# was completely rewritten to C++ and is now an
- intergral part of protoc.
+ integral part of protoc.
* Some refactorings and cleanup has been applied to the C# runtime library.
* Only proto2 is supported in C# at the moment, proto3 support is in
progress and will likely bring significant breaking changes to the API.
@@ -321,7 +382,7 @@
This release (v3.0.0-alpha-1) includes partial proto3 support for C++ and
Java. Items 6 (well-known types) and 7 (JSON format) in the above feature
- list are not impelmented.
+ list are not implemented.
A new notion "syntax" is introduced to specify whether a .proto file
uses proto2 or proto3:
@@ -422,7 +483,7 @@
}
* Files, services, enums, messages, methods and enum values can be marked
as deprecated now.
- * Added Support for list values, including lists of mesaages, when
+ * Added Support for list values, including lists of messages, when
parsing text-formatted protos in C++ and Java.
For example: foo: [1, 2, 3]
@@ -502,7 +563,7 @@
Python
* Added support for dynamic message creation. DescriptorDatabase,
- DescriptorPool, and MessageFactory work like their C++ couterparts to
+ DescriptorPool, and MessageFactory work like their C++ counterparts to
simplify Descriptor construction from *DescriptorProtos, and MessageFactory
provides a message instance from a Descriptor.
* Added pickle support for protobuf messages.
@@ -516,7 +577,7 @@
2011-05-01 version 2.4.1:
C++
- * Fixed the frendship problem for old compilers to make the library now gcc 3
+ * Fixed the friendship problem for old compilers to make the library now gcc 3
compatible again.
* Fixed vcprojects/extract_includes.bat to extract compiler/plugin.h.
@@ -783,7 +844,7 @@
* Fixed tendency for TextFormat's parsing to overflow the stack when
parsing large string values. The underlying problem is with Java's
regex implementation (which unfortunately uses recursive backtracking
- rather than building an NFA). Worked around by making use of possesive
+ rather than building an NFA). Worked around by making use of possessive
quantifiers.
* Generated service classes now also generate pure interfaces. For a service
Foo, Foo.Interface is a pure interface containing all of the service's
@@ -797,7 +858,7 @@
RPC implementations will have to implement the new interfaces in order to
support blocking mode.
* New I/O methods parseDelimitedFrom(), mergeDelimitedFrom(), and
- writeDelimitedTo() read and write "delemited" messages from/to a stream,
+ writeDelimitedTo() read and write "delimited" messages from/to a stream,
meaning that the message size precedes the data. This way, you can write
multiple messages to a stream without having to worry about delimiting
them yourself.
diff --git a/INSTALL.txt b/INSTALL.txt
deleted file mode 100644
index ce3b0949..00000000
--- a/INSTALL.txt
+++ /dev/null
@@ -1,237 +0,0 @@
-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
index 8657f821..fbd27fc5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -92,6 +92,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.Test/Properties/AssemblyInfo.cs \
csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs \
csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs \
+ csharp/src/Google.Protobuf.Test/Reflection/TypeRegistryTest.cs \
csharp/src/Google.Protobuf.Test/SampleEnum.cs \
csharp/src/Google.Protobuf.Test/SampleMessages.cs \
csharp/src/Google.Protobuf.Test/TestCornerCases.cs \
@@ -132,7 +133,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/LimitedInputStream.cs \
csharp/src/Google.Protobuf/MessageExtensions.cs \
csharp/src/Google.Protobuf/MessageParser.cs \
- csharp/src/Google.Protobuf/Preconditions.cs \
+ csharp/src/Google.Protobuf/ProtoPreconditions.cs \
csharp/src/Google.Protobuf/Properties/AssemblyInfo.cs \
csharp/src/Google.Protobuf/Reflection/Descriptor.cs \
csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs \
@@ -145,7 +146,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/FieldType.cs \
csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs \
- csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs \
+ csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs \
csharp/src/Google.Protobuf/Reflection/IDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs \
csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs \
@@ -159,6 +160,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs \
csharp/src/Google.Protobuf/Reflection/ServiceDescriptor.cs \
csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs \
+ csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs \
csharp/src/Google.Protobuf/WellKnownTypes/Any.cs \
csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs \
csharp/src/Google.Protobuf/WellKnownTypes/Api.cs \
@@ -172,157 +174,167 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs \
csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs \
csharp/src/Google.Protobuf/WellKnownTypes/Type.cs \
+ csharp/src/Google.Protobuf/WellKnownTypes/ValuePartial.cs \
csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs \
+ csharp/src/Google.Protobuf/WellKnownTypes/WrappersPartial.cs \
csharp/src/Google.Protobuf/WireFormat.cs \
+ csharp/src/Google.Protobuf/packages.config \
csharp/src/packages/repositories.config
-java_EXTRA_DIST= \
- java/src/main/java/com/google/protobuf/AbstractMessage.java \
- java/src/main/java/com/google/protobuf/AbstractMessageLite.java \
- java/src/main/java/com/google/protobuf/AbstractParser.java \
- java/src/main/java/com/google/protobuf/AbstractProtobufList.java \
- java/src/main/java/com/google/protobuf/BlockingRpcChannel.java \
- java/src/main/java/com/google/protobuf/BlockingService.java \
- java/src/main/java/com/google/protobuf/BooleanArrayList.java \
- java/src/main/java/com/google/protobuf/BoundedByteString.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/DoubleArrayList.java \
- java/src/main/java/com/google/protobuf/DynamicMessage.java \
- java/src/main/java/com/google/protobuf/Extension.java \
- java/src/main/java/com/google/protobuf/ExtensionLite.java \
- java/src/main/java/com/google/protobuf/ExtensionRegistry.java \
- java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \
- java/src/main/java/com/google/protobuf/FieldSet.java \
- java/src/main/java/com/google/protobuf/FloatArrayList.java \
- java/src/main/java/com/google/protobuf/GeneratedMessage.java \
- java/src/main/java/com/google/protobuf/GeneratedMessageLite.java \
- java/src/main/java/com/google/protobuf/IntArrayList.java \
- java/src/main/java/com/google/protobuf/Internal.java \
- java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \
- java/src/main/java/com/google/protobuf/LazyField.java \
- java/src/main/java/com/google/protobuf/LazyFieldLite.java \
- java/src/main/java/com/google/protobuf/LazyStringArrayList.java \
- java/src/main/java/com/google/protobuf/LazyStringList.java \
- java/src/main/java/com/google/protobuf/LiteralByteString.java \
- java/src/main/java/com/google/protobuf/LongArrayList.java \
- java/src/main/java/com/google/protobuf/MapEntry.java \
- java/src/main/java/com/google/protobuf/MapEntryLite.java \
- java/src/main/java/com/google/protobuf/MapField.java \
- java/src/main/java/com/google/protobuf/MapFieldLite.java \
- java/src/main/java/com/google/protobuf/Message.java \
- java/src/main/java/com/google/protobuf/MessageLite.java \
- java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \
- java/src/main/java/com/google/protobuf/MessageOrBuilder.java \
- java/src/main/java/com/google/protobuf/MessageReflection.java \
- java/src/main/java/com/google/protobuf/MutabilityOracle.java \
- java/src/main/java/com/google/protobuf/Parser.java \
- java/src/main/java/com/google/protobuf/ProtobufArrayList.java \
- java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \
- java/src/main/java/com/google/protobuf/ProtocolStringList.java \
- java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \
- java/src/main/java/com/google/protobuf/RopeByteString.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/ServiceException.java \
- java/src/main/java/com/google/protobuf/SingleFieldBuilder.java \
- java/src/main/java/com/google/protobuf/SmallSortedMap.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/UnknownFieldSetLite.java \
- java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java \
- java/src/main/java/com/google/protobuf/Utf8.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/AnyTest.java \
- java/src/test/java/com/google/protobuf/BooleanArrayListTest.java \
- java/src/test/java/com/google/protobuf/BoundedByteStringTest.java \
- java/src/test/java/com/google/protobuf/ByteStringTest.java \
- java/src/test/java/com/google/protobuf/CheckUtf8Test.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/DeprecatedFieldTest.java \
- java/src/test/java/com/google/protobuf/DescriptorsTest.java \
- java/src/test/java/com/google/protobuf/DoubleArrayListTest.java \
- java/src/test/java/com/google/protobuf/DynamicMessageTest.java \
- java/src/test/java/com/google/protobuf/FieldPresenceTest.java \
- java/src/test/java/com/google/protobuf/FloatArrayListTest.java \
- java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \
- java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \
- java/src/test/java/com/google/protobuf/IntArrayListTest.java \
- java/src/test/java/com/google/protobuf/IsValidUtf8Test.java \
- java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java \
- java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java \
- java/src/test/java/com/google/protobuf/LazyFieldTest.java \
- java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java \
- java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java \
- java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java \
- java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java \
- java/src/test/java/com/google/protobuf/LiteTest.java \
- java/src/test/java/com/google/protobuf/LiteralByteStringTest.java \
- java/src/test/java/com/google/protobuf/LongArrayListTest.java \
- java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java \
- java/src/test/java/com/google/protobuf/MapForProto2Test.java \
- java/src/test/java/com/google/protobuf/MapTest.java \
- java/src/test/java/com/google/protobuf/MessageTest.java \
- java/src/test/java/com/google/protobuf/NestedBuildersTest.java \
- java/src/test/java/com/google/protobuf/ParserTest.java \
- java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java \
- java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \
- java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java \
- java/src/test/java/com/google/protobuf/RopeByteStringTest.java \
- java/src/test/java/com/google/protobuf/ServiceTest.java \
- java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java \
- java/src/test/java/com/google/protobuf/SmallSortedMapTest.java \
- java/src/test/java/com/google/protobuf/TestBadIdentifiers.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/UnknownEnumValueTest.java \
- java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java \
- java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \
- java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \
- java/src/test/java/com/google/protobuf/WellKnownTypesTest.java \
- java/src/test/java/com/google/protobuf/WireFormatTest.java \
- java/src/test/java/com/google/protobuf/any_test.proto \
- java/src/test/java/com/google/protobuf/field_presence_test.proto \
- java/src/test/java/com/google/protobuf/lazy_fields_lite.proto \
- java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto \
- java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto \
- java/src/test/java/com/google/protobuf/map_for_proto2_test.proto \
- java/src/test/java/com/google/protobuf/map_initialization_order_test.proto \
- java/src/test/java/com/google/protobuf/map_test.proto \
- java/src/test/java/com/google/protobuf/multiple_files_test.proto \
- java/src/test/java/com/google/protobuf/nested_builders_test.proto \
- java/src/test/java/com/google/protobuf/nested_extension.proto \
- java/src/test/java/com/google/protobuf/nested_extension_lite.proto \
- java/src/test/java/com/google/protobuf/non_nested_extension.proto \
- java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto \
- java/src/test/java/com/google/protobuf/outer_class_name_test.proto \
- java/src/test/java/com/google/protobuf/outer_class_name_test2.proto \
- java/src/test/java/com/google/protobuf/outer_class_name_test3.proto \
- java/src/test/java/com/google/protobuf/test_bad_identifiers.proto \
- java/src/test/java/com/google/protobuf/test_check_utf8.proto \
- java/src/test/java/com/google/protobuf/test_check_utf8_size.proto \
- java/src/test/java/com/google/protobuf/test_custom_options.proto \
- java/src/test/java/com/google/protobuf/test_extra_interfaces.proto \
- java/util/pom.xml \
- java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java \
- java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java \
- java/util/src/main/java/com/google/protobuf/util/JsonFormat.java \
- java/util/src/main/java/com/google/protobuf/util/TimeUtil.java \
- java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java \
- java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java \
- java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java \
- java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java \
- java/util/src/test/java/com/google/protobuf/util/json_test.proto \
- java/pom.xml \
- java/README.md
+java_EXTRA_DIST= \
+ java/README.md \
+ java/core/generate-sources-build.xml \
+ java/core/generate-test-sources-build.xml \
+ java/core/pom.xml \
+ java/core/src/main/java/com/google/protobuf/AbstractMessage.java \
+ java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java \
+ java/core/src/main/java/com/google/protobuf/AbstractParser.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java \
+ java/core/src/main/java/com/google/protobuf/BlockingRpcChannel.java \
+ java/core/src/main/java/com/google/protobuf/BlockingService.java \
+ java/core/src/main/java/com/google/protobuf/BooleanArrayList.java \
+ java/core/src/main/java/com/google/protobuf/ByteString.java \
+ java/core/src/main/java/com/google/protobuf/CodedInputStream.java \
+ java/core/src/main/java/com/google/protobuf/CodedOutputStream.java \
+ java/core/src/main/java/com/google/protobuf/Descriptors.java \
+ java/core/src/main/java/com/google/protobuf/DoubleArrayList.java \
+ java/core/src/main/java/com/google/protobuf/DynamicMessage.java \
+ java/core/src/main/java/com/google/protobuf/Extension.java \
+ java/core/src/main/java/com/google/protobuf/ExtensionLite.java \
+ java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java \
+ java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \
+ java/core/src/main/java/com/google/protobuf/FieldSet.java \
+ java/core/src/main/java/com/google/protobuf/FloatArrayList.java \
+ java/core/src/main/java/com/google/protobuf/GeneratedMessage.java \
+ java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java \
+ java/core/src/main/java/com/google/protobuf/IntArrayList.java \
+ java/core/src/main/java/com/google/protobuf/Internal.java \
+ java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \
+ java/core/src/main/java/com/google/protobuf/LazyField.java \
+ java/core/src/main/java/com/google/protobuf/LazyFieldLite.java \
+ java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java \
+ java/core/src/main/java/com/google/protobuf/LazyStringList.java \
+ java/core/src/main/java/com/google/protobuf/LongArrayList.java \
+ java/core/src/main/java/com/google/protobuf/MapEntry.java \
+ java/core/src/main/java/com/google/protobuf/MapEntryLite.java \
+ java/core/src/main/java/com/google/protobuf/MapField.java \
+ java/core/src/main/java/com/google/protobuf/MapFieldLite.java \
+ java/core/src/main/java/com/google/protobuf/Message.java \
+ java/core/src/main/java/com/google/protobuf/MessageLite.java \
+ java/core/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \
+ java/core/src/main/java/com/google/protobuf/MessageLiteToString.java \
+ java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java \
+ java/core/src/main/java/com/google/protobuf/MessageReflection.java \
+ java/core/src/main/java/com/google/protobuf/MutabilityOracle.java \
+ java/core/src/main/java/com/google/protobuf/NioByteString.java \
+ java/core/src/main/java/com/google/protobuf/Parser.java \
+ java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java \
+ java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \
+ java/core/src/main/java/com/google/protobuf/ProtocolStringList.java \
+ java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \
+ java/core/src/main/java/com/google/protobuf/RopeByteString.java \
+ java/core/src/main/java/com/google/protobuf/RpcCallback.java \
+ java/core/src/main/java/com/google/protobuf/RpcChannel.java \
+ java/core/src/main/java/com/google/protobuf/RpcController.java \
+ java/core/src/main/java/com/google/protobuf/RpcUtil.java \
+ java/core/src/main/java/com/google/protobuf/Service.java \
+ java/core/src/main/java/com/google/protobuf/ServiceException.java \
+ java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java \
+ java/core/src/main/java/com/google/protobuf/SmallSortedMap.java \
+ java/core/src/main/java/com/google/protobuf/TextFormat.java \
+ java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java \
+ java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java \
+ java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java \
+ java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java \
+ java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java \
+ java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java \
+ java/core/src/main/java/com/google/protobuf/Utf8.java \
+ java/core/src/main/java/com/google/protobuf/WireFormat.java \
+ java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java \
+ java/core/src/test/java/com/google/protobuf/AnyTest.java \
+ java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java \
+ java/core/src/test/java/com/google/protobuf/ByteStringTest.java \
+ java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java \
+ java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java \
+ java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \
+ java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java \
+ java/core/src/test/java/com/google/protobuf/DescriptorsTest.java \
+ java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java \
+ java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java \
+ java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \
+ java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java \
+ java/core/src/test/java/com/google/protobuf/IntArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java \
+ java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java \
+ java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java \
+ java/core/src/test/java/com/google/protobuf/LazyFieldTest.java \
+ java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java \
+ java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java \
+ java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java \
+ java/core/src/test/java/com/google/protobuf/LiteTest.java \
+ java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java \
+ java/core/src/test/java/com/google/protobuf/LongArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java \
+ java/core/src/test/java/com/google/protobuf/MapForProto2Test.java \
+ java/core/src/test/java/com/google/protobuf/MapTest.java \
+ java/core/src/test/java/com/google/protobuf/MessageTest.java \
+ java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java \
+ java/core/src/test/java/com/google/protobuf/NioByteStringTest.java \
+ java/core/src/test/java/com/google/protobuf/ParserTest.java \
+ java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java \
+ java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \
+ java/core/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java \
+ java/core/src/test/java/com/google/protobuf/RopeByteStringTest.java \
+ java/core/src/test/java/com/google/protobuf/ServiceTest.java \
+ java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java \
+ java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java \
+ java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java \
+ java/core/src/test/java/com/google/protobuf/TestUtil.java \
+ java/core/src/test/java/com/google/protobuf/TextFormatTest.java \
+ java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java \
+ java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java \
+ java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \
+ java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \
+ java/core/src/test/java/com/google/protobuf/WellKnownTypesTest.java \
+ java/core/src/test/java/com/google/protobuf/WireFormatTest.java \
+ java/core/src/test/proto/com/google/protobuf/any_test.proto \
+ java/core/src/test/proto/com/google/protobuf/field_presence_test.proto \
+ java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto \
+ java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto \
+ java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto \
+ java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto \
+ java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto \
+ java/core/src/test/proto/com/google/protobuf/map_test.proto \
+ java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto \
+ java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto \
+ java/core/src/test/proto/com/google/protobuf/nested_extension.proto \
+ java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto \
+ java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto \
+ java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto \
+ java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto \
+ java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto \
+ java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto \
+ java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto \
+ java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto \
+ java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto \
+ java/core/src/test/proto/com/google/protobuf/test_custom_options.proto \
+ java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto \
+ java/lite/pom.xml \
+ java/pom.xml \
+ java/util/pom.xml \
+ java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java \
+ java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java \
+ java/util/src/main/java/com/google/protobuf/util/JsonFormat.java \
+ java/util/src/main/java/com/google/protobuf/util/TimeUtil.java \
+ java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java \
+ java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java \
+ java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java \
+ java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java \
+ java/util/src/test/proto/com/google/protobuf/util/json_test.proto
javanano_EXTRA_DIST= \
javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java \
@@ -468,7 +480,6 @@ objectivec_EXTRA_DIST= \
objectivec/Tests/GPBMessageTests.m \
objectivec/Tests/GPBObjectiveCPlusPlusTest.mm \
objectivec/Tests/GPBPerfTests.m \
- objectivec/Tests/GPBStringTests.m \
objectivec/Tests/GPBSwiftTests.swift \
objectivec/Tests/GPBTestUtilities.h \
objectivec/Tests/GPBTestUtilities.m \
@@ -504,6 +515,15 @@ objectivec_EXTRA_DIST= \
python_EXTRA_DIST= \
python/MANIFEST.in \
+ python/google/__init__.py \
+ python/google/protobuf/__init__.py \
+ python/google/protobuf/descriptor.py \
+ python/google/protobuf/descriptor_database.py \
+ python/google/protobuf/descriptor_pool.py \
+ python/google/protobuf/internal/__init__.py \
+ python/google/protobuf/internal/_parameterized.py \
+ python/google/protobuf/internal/any_test.proto \
+ python/google/protobuf/internal/any_test.proto \
python/google/protobuf/internal/api_implementation.cc \
python/google/protobuf/internal/api_implementation.py \
python/google/protobuf/internal/containers.py \
@@ -518,17 +538,19 @@ python_EXTRA_DIST= \
python/google/protobuf/internal/factory_test1.proto \
python/google/protobuf/internal/factory_test2.proto \
python/google/protobuf/internal/generator_test.py \
+ python/google/protobuf/internal/import_test_package/__init__.py \
+ python/google/protobuf/internal/import_test_package/inner.proto \
+ python/google/protobuf/internal/import_test_package/outer.proto \
python/google/protobuf/internal/json_format_test.py \
python/google/protobuf/internal/message_factory_test.py \
python/google/protobuf/internal/message_listener.py \
python/google/protobuf/internal/message_set_extensions.proto \
python/google/protobuf/internal/message_test.py \
python/google/protobuf/internal/missing_enum_values.proto \
- python/google/protobuf/internal/more_extensions_dynamic.proto \
python/google/protobuf/internal/more_extensions.proto \
+ python/google/protobuf/internal/more_extensions_dynamic.proto \
python/google/protobuf/internal/more_messages.proto \
python/google/protobuf/internal/packed_field_test.proto \
- python/google/protobuf/internal/_parameterized.py \
python/google/protobuf/internal/proto_builder_test.py \
python/google/protobuf/internal/python_message.py \
python/google/protobuf/internal/reflection_test.py \
@@ -540,52 +562,47 @@ python_EXTRA_DIST= \
python/google/protobuf/internal/text_format_test.py \
python/google/protobuf/internal/type_checkers.py \
python/google/protobuf/internal/unknown_fields_test.py \
+ python/google/protobuf/internal/well_known_types.py \
+ python/google/protobuf/internal/well_known_types.py \
+ python/google/protobuf/internal/well_known_types_test.py \
+ python/google/protobuf/internal/well_known_types_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/internal/import_test_package/__init__.py \
- python/google/protobuf/internal/import_test_package/inner.proto \
- python/google/protobuf/internal/import_test_package/outer.proto \
+ python/google/protobuf/json_format.py \
+ python/google/protobuf/message.py \
+ python/google/protobuf/message_factory.py \
+ python/google/protobuf/proto_builder.py \
python/google/protobuf/pyext/README \
+ python/google/protobuf/pyext/__init__.py \
python/google/protobuf/pyext/cpp_message.py \
- python/google/protobuf/pyext/descriptor.h \
python/google/protobuf/pyext/descriptor.cc \
- python/google/protobuf/pyext/descriptor_pool.h \
- python/google/protobuf/pyext/descriptor_pool.cc \
- python/google/protobuf/pyext/descriptor_containers.h \
+ python/google/protobuf/pyext/descriptor.h \
python/google/protobuf/pyext/descriptor_containers.cc \
- python/google/protobuf/pyext/extension_dict.h \
+ python/google/protobuf/pyext/descriptor_containers.h \
+ python/google/protobuf/pyext/descriptor_database.cc \
+ python/google/protobuf/pyext/descriptor_database.h \
+ python/google/protobuf/pyext/descriptor_pool.cc \
+ python/google/protobuf/pyext/descriptor_pool.h \
python/google/protobuf/pyext/extension_dict.cc \
- python/google/protobuf/pyext/message.h \
+ python/google/protobuf/pyext/extension_dict.h \
+ python/google/protobuf/pyext/map_container.cc \
+ python/google/protobuf/pyext/map_container.h \
python/google/protobuf/pyext/message.cc \
- python/google/protobuf/pyext/message_map_container.cc \
- python/google/protobuf/pyext/message_map_container.h \
+ python/google/protobuf/pyext/message.h \
python/google/protobuf/pyext/proto2_api_test.proto \
python/google/protobuf/pyext/python.proto \
python/google/protobuf/pyext/python_protobuf.h \
- python/google/protobuf/pyext/repeated_composite_container.h \
python/google/protobuf/pyext/repeated_composite_container.cc \
- python/google/protobuf/pyext/repeated_scalar_container.h \
+ python/google/protobuf/pyext/repeated_composite_container.h \
python/google/protobuf/pyext/repeated_scalar_container.cc \
- python/google/protobuf/pyext/scalar_map_container.cc \
- python/google/protobuf/pyext/scalar_map_container.h \
+ python/google/protobuf/pyext/repeated_scalar_container.h \
python/google/protobuf/pyext/scoped_pyobject_ptr.h \
- python/google/protobuf/pyext/__init__.py \
- python/google/protobuf/descriptor.py \
- python/google/protobuf/descriptor_database.py \
- python/google/protobuf/descriptor_pool.py \
- python/google/protobuf/json_format.py \
- python/google/protobuf/message.py \
- python/google/protobuf/message_factory.py \
- python/google/protobuf/proto_builder.py \
python/google/protobuf/reflection.py \
python/google/protobuf/service.py \
python/google/protobuf/service_reflection.py \
python/google/protobuf/symbol_database.py \
python/google/protobuf/text_encoding.py \
python/google/protobuf/text_format.py \
- python/google/protobuf/__init__.py \
- python/google/__init__.py \
python/mox.py \
python/setup.py \
python/stubout.py \
@@ -640,13 +657,46 @@ ruby_EXTRA_DIST= \
ruby/tests/generated_code_test.rb \
ruby/travis-test.sh
-all_EXTRA_DIST=$(csharp_EXTRA_DIST) $(java_EXTRA_DIST) $(javanano_EXTRA_DIST) $(objectivec_EXTRA_DIST) $(python_EXTRA_DIST) $(ruby_EXTRA_DIST)
+js_EXTRA_DIST= \
+ js/README.md \
+ js/binary/arith.js \
+ js/binary/arith_test.js \
+ js/binary/constants.js \
+ js/binary/decoder.js \
+ js/binary/decoder_test.js \
+ js/binary/proto_test.js \
+ js/binary/reader.js \
+ js/binary/reader_test.js \
+ js/binary/utils.js \
+ js/binary/utils_test.js \
+ js/binary/writer.js \
+ js/binary/writer_test.js \
+ js/data.proto \
+ js/debug.js \
+ js/debug_test.js \
+ js/gulpfile.js \
+ js/jasmine.json \
+ js/message.js \
+ js/message_test.js \
+ js/node_loader.js \
+ js/package.json \
+ js/proto3_test.js \
+ js/proto3_test.proto \
+ js/test.proto \
+ js/test2.proto \
+ js/test3.proto \
+ js/test4.proto \
+ js/test5.proto \
+ js/test_bootstrap.js \
+ js/testbinary.proto \
+ js/testempty.proto
+
+all_EXTRA_DIST=$(csharp_EXTRA_DIST) $(java_EXTRA_DIST) $(javanano_EXTRA_DIST) $(objectivec_EXTRA_DIST) $(python_EXTRA_DIST) $(ruby_EXTRA_DIST) $(js_EXTRA_DIST)
EXTRA_DIST = $(@DIST_LANG@_EXTRA_DIST) \
autogen.sh \
generate_descriptor_proto.sh \
README.md \
- INSTALL.txt \
LICENSE \
CONTRIBUTORS.txt \
CHANGES.txt \
@@ -673,15 +723,23 @@ EXTRA_DIST = $(@DIST_LANG@_EXTRA_DIST) \
examples/Makefile \
examples/addressbook.proto \
examples/add_person.cc \
+ examples/add_person.go \
+ examples/add_person_test.go \
examples/list_people.cc \
+ examples/list_people.go \
examples/AddPerson.java \
examples/ListPeople.java \
examples/add_person.py \
- examples/list_people.py
+ examples/list_people.py \
+ examples/list_people_test.go \
+ protobuf.bzl \
+ six.BUILD \
+ util/python/BUILD
# Deletes all the files generated by autogen.sh.
MAINTAINERCLEANFILES = \
aclocal.m4 \
+ ar-lib \
config.guess \
config.sub \
configure \
diff --git a/Protobuf.podspec b/Protobuf.podspec
index 92ff2904..73a60222 100644
--- a/Protobuf.podspec
+++ b/Protobuf.podspec
@@ -5,7 +5,7 @@
# dependent projects use the :git notation to refer to the library.
Pod::Spec.new do |s|
s.name = 'Protobuf'
- s.version = '3.0.0-alpha-4.1'
+ s.version = '3.0.0-beta-2'
s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.'
s.homepage = 'https://github.com/google/protobuf'
s.license = 'New BSD'
diff --git a/README.md b/README.md
index a974d301..ba9c589d 100644
--- a/README.md
+++ b/README.md
@@ -7,182 +7,66 @@ Copyright 2008 Google Inc.
https://developers.google.com/protocol-buffers/
-C++ Installation - Unix
------------------------
+Overview
+--------
-If you get the source from github, you need to generate the configure script
-first:
+Protocol Buffers (a.k.a., protobuf) are Google's language-neutral,
+platform-neutral, extensible mechanism for serializing structured data. You
+can find [protobuf's documentation on the Google Developers site](https://developers.google.com/protocol-buffers/).
- $ ./autogen.sh
+This README file contains protobuf installation instructions. To install
+protobuf, you need to install the protocol compiler (used to compile .proto
+files) and the protobuf runtime for your chosen programming language.
-This will download gmock source (which is used for C++ Protocol Buffer
-unit-tests) to the current directory and run automake, autoconf, etc.
-to generate the configure script and various template makefiles.
+Protocol Compiler Installation
+------------------------------
-You can skip this step if you are using a release package (which already
-contains gmock and the configure script).
+The protocol compiler is written in C++. If you are using C++, please follow
+the [C++ Installation Instructions](src/README.md) to install protoc along
+with the C++ runtime.
-To build and install the C++ Protocol Buffer runtime and the Protocol
-Buffer compiler (protoc) execute the following:
+For non-C++ users, the simplest way to install the protocol compiler is to
+download a pre-built binary from our release page:
- $ ./configure
- $ make
- $ make check
- $ make install
+ [https://github.com/google/protobuf/releases](https://github.com/google/protobuf/releases)
-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.
+In the downloads section of each release, you can find pre-built binaries in
+zip packages: protoc-$VERSION-$PLATFORM.zip. It contains the protoc binary
+as well as a set of standard .proto files distributed along with protobuf.
-"make install" may require superuser privileges.
+If you are looking for an old version that is not available in the release
+page, check out the maven repo here:
-For advanced usage information on configure and make, see INSTALL.txt.
+ [http://repo1.maven.org/maven2/com/google/protobuf/protoc/](http://repo1.maven.org/maven2/com/google/protobuf/protoc/)
-**Hint on install location**
+These pre-built binaries are only provided for released versions. If you want
+to use the github master version at HEAD, or you need to modify protobuf code,
+or you are using C++, it's recommended to build your own protoc binary from
+source.
- By default, the package will be installed to /usr/local. However,
- on many platforms, /usr/local/lib is not part of LD_LIBRARY_PATH.
- You can add it, but it may be easier to just install to /usr
- instead. To do this, invoke configure as follows:
+If you would like to build protoc binary from source, see the [C++ Installation
+Instructions](src/README.md).
- ./configure --prefix=/usr
+Protobuf Runtime Installation
+-----------------------------
- If you already built the package with a different prefix, make sure
- to run "make clean" before building again.
+Protobuf supports several different programming languages. For each programming
+language, you can find instructions in the corresponding source directory about
+how to install protobuf runtime for that specific language:
-**Compiling dependent packages**
+| Language | Source |
+|--------------------------------------|-------------------------------------------------------|
+| C++ (include C++ runtime and protoc) | [src](src) |
+| Java | [java](java) |
+| Python | [python](python) |
+| Objective-C | [objectivec](objectivec) |
+| C# | [csharp](csharp) |
+| JavaNano | [javanano](javanano) |
+| JavaScript | [js](js) |
+| Ruby | [ruby](ruby) |
+| Go | [golang/protobuf](https://github.com/golang/protobuf) |
+| PHP | TBD |
- To compile a package that uses Protocol Buffers, you need to pass
- various flags to your compiler and linker. As of version 2.2.0,
- Protocol Buffers integrates with pkg-config to manage this. If you
- have pkg-config installed, then you can invoke it to get a list of
- flags like so:
-
- pkg-config --cflags protobuf # print compiler flags
- pkg-config --libs protobuf # print linker flags
- pkg-config --cflags --libs protobuf # print both
-
- For example:
-
- c++ my_program.cc my_proto.pb.cc `pkg-config --cflags --libs protobuf`
-
- Note that packages written prior to the 2.2.0 release of Protocol
- Buffers may not yet integrate with pkg-config to get flags, and may
- not pass the correct set of flags to correctly link against
- libprotobuf. If the package in question uses autoconf, you can
- often fix the problem by invoking its configure script like:
-
- configure CXXFLAGS="$(pkg-config --cflags protobuf)" \
- LIBS="$(pkg-config --libs protobuf)"
-
- This will force it to use the correct flags.
-
- If you are writing an autoconf-based package that uses Protocol
- Buffers, you should probably use the PKG_CHECK_MODULES macro in your
- configure script like:
-
- PKG_CHECK_MODULES([protobuf], [protobuf])
-
- See the pkg-config man page for more info.
-
- If you only want protobuf-lite, substitute "protobuf-lite" in place
- of "protobuf" in these examples.
-
-**Note for Mac users**
-
- For a Mac system, Unix tools are not available by default. You will first need
- to install Xcode from the Mac AppStore and then run the following command from
- a terminal:
-
- $ sudo xcode-select --install
-
- To install Unix tools, you can install "port" following the instructions at
- https://www.macports.org . This will reside in /opt/local/bin/port for most
- Mac installations.
-
- $ sudo /opt/local/bin/port install autoconf automake libtool
-
- Then follow the Unix instructions above.
-
-**Note for cross-compiling**
-
- The makefiles normally invoke the protoc executable that they just
- built in order to build tests. When cross-compiling, the protoc
- executable may not be executable on the host machine. In this case,
- you must build a copy of protoc for the host machine first, then use
- the --with-protoc option to tell configure to use it instead. For
- example:
-
- ./configure --with-protoc=protoc
-
- This will use the installed protoc (found in your $PATH) instead of
- trying to execute the one built during the build process. You can
- also use an executable that hasn't been installed. For example, if
- you built the protobuf package for your host machine in ../host,
- you might do:
-
- ./configure --with-protoc=../host/src/protoc
-
- Either way, you must make sure that the protoc executable you use
- has the same version as the protobuf source code you are trying to
- use it with.
-
-**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.
-
-**Note for HP C++ Tru64 users**
-
- To compile invoke configure as follows:
-
- ./configure CXXFLAGS="-O -std ansi -ieee -D__USE_STD_IOSTREAM"
-
- Also, you will need to use gmake instead of make.
-
-**Note for AIX users**
-
- Compile using the IBM xlC C++ compiler as follows:
-
- ./configure CXX=xlC
-
- Also, you will need to use GNU `make` (`gmake`) instead of AIX `make`.
-
-C++ Installation - Windows
---------------------------
-
-If you are using Microsoft Visual C++, see cmake/README.md.
-
-If you are using Cygwin or MinGW, follow the Unix installation
-instructions, above.
-
-Binary Compatibility Warning
-----------------------------
-
-Due to the nature of C++, it is unlikely that any two versions of the
-Protocol Buffers C++ runtime libraries will have compatible ABIs.
-That is, if you linked an executable against an older version of
-libprotobuf, it is unlikely to work with a newer version without
-re-compiling. This problem, when it occurs, will normally be detected
-immediately on startup of your app. Still, you may want to consider
-using static linkage. You can configure this package to install
-static libraries only using:
-
- ./configure --disable-shared
-
-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
-----
diff --git a/appveyor.yml b/appveyor.yml
index 3447602e..c84ecae2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,32 +1,32 @@
-# Only test one combination: "Visual Studio 12 + Win64 + Debug + DLL". We can
-# test more combinations but AppVeyor just takes too long to finish (each
-# combination takes ~15mins).
-platform:
- - Win64
-
-configuration:
- - Debug
-
-environment:
- matrix:
- - language: cpp
- BUILD_DLL: ON
-
- - language: csharp
-
-install:
- - ps: Start-FileDownload https://googlemock.googlecode.com/files/gmock-1.7.0.zip
- - 7z x gmock-1.7.0.zip
- - rename gmock-1.7.0 gmock
-
-before_build:
- - if %platform%==Win32 set generator=Visual Studio 12
- - if %platform%==Win64 set generator=Visual Studio 12 Win64
- - if %platform%==Win32 set vcplatform=Win32
- - if %platform%==Win64 set vcplatform=x64
-
-build_script:
- - CALL appveyor.bat
-
-skip_commits:
- message: /.*\[skip appveyor\].*/
+# Only test one combination: "Visual Studio 12 + Win64 + Debug + DLL". We can
+# test more combinations but AppVeyor just takes too long to finish (each
+# combination takes ~15mins).
+platform:
+ - Win64
+
+configuration:
+ - Debug
+
+environment:
+ matrix:
+ - language: cpp
+ BUILD_DLL: ON
+
+ - language: csharp
+
+install:
+ - ps: Start-FileDownload https://googlemock.googlecode.com/files/gmock-1.7.0.zip
+ - 7z x gmock-1.7.0.zip
+ - rename gmock-1.7.0 gmock
+
+before_build:
+ - if %platform%==Win32 set generator=Visual Studio 12
+ - if %platform%==Win64 set generator=Visual Studio 12 Win64
+ - if %platform%==Win32 set vcplatform=Win32
+ - if %platform%==Win64 set vcplatform=x64
+
+build_script:
+ - CALL appveyor.bat
+
+skip_commits:
+ message: /.*\[skip appveyor\].*/
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index 9de6341a..15ae457a 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -115,6 +115,8 @@ else (protobuf_BUILD_SHARED_LIBS)
endif (protobuf_BUILD_SHARED_LIBS)
if (MSVC)
+ # Build with multiple processes
+ add_definitions(/MP)
add_definitions(/wd4244 /wd4267 /wd4018 /wd4355 /wd4800 /wd4251 /wd4996 /wd4146 /wd4305)
string(REPLACE "/" "\\" PROTOBUF_SOURCE_WIN32_PATH ${protobuf_SOURCE_DIR})
string(REPLACE "/" "\\" PROTOBUF_BINARY_WIN32_PATH ${protobuf_BINARY_DIR})
diff --git a/cmake/README.md b/cmake/README.md
index b3042e6c..1e7410d8 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -28,11 +28,11 @@ Create a folder where protobuf headers/libraries/binaries will be installed afte
C:\Path\to>mkdir install
-If *cmake* command is not avaliable from *Command Prompt*, add it to system *PATH* variable:
+If *cmake* command is not available from *Command Prompt*, add it to system *PATH* variable:
C:\Path\to>set PATH=%PATH%;C:\Program Files (x86)\CMake\bin
-If *git* command is not avaliable from *Command Prompt*, add it to system *PATH* variable:
+If *git* command is not available from *Command Prompt*, add it to system *PATH* variable:
C:\Path\to>set PATH=%PATH%;C:\Program Files\Git\cmd
@@ -157,7 +157,8 @@ And wait for the compilation to finish.
Testing
=======
-To run unit-tests:
+To run unit-tests, first you must compile protobuf as described above.
+Then run:
C:\Path\to\protobuf\cmake\build\release>nmake check
@@ -218,7 +219,7 @@ It sounds not so strange and it works.
This will create the following folders under the *install* location:
* bin - that contains protobuf *protoc.exe* compiler;
- * inclue - that contains C++ headers and protobuf *.proto files;
+ * include - that contains C++ headers and protobuf *.proto files;
* lib - that contains linking libraries and *CMake* configuration files for *protobuf* package.
Now you can if needed:
diff --git a/cmake/extract_includes.bat.in b/cmake/extract_includes.bat.in
index f6d8b893..b593e0c9 100644
--- a/cmake/extract_includes.bat.in
+++ b/cmake/extract_includes.bat.in
@@ -6,6 +6,7 @@ mkdir include\google\protobuf\compiler\cpp
mkdir include\google\protobuf\compiler\csharp
mkdir include\google\protobuf\compiler\java
mkdir include\google\protobuf\compiler\javanano
+mkdir include\google\protobuf\compiler\js
mkdir include\google\protobuf\compiler\objectivec
mkdir include\google\protobuf\compiler\python
mkdir include\google\protobuf\compiler\ruby
@@ -26,6 +27,7 @@ copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\importer.h in
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\java_generator.h include\google\protobuf\compiler\java\java_generator.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\java_names.h include\google\protobuf\compiler\java\java_names.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\javanano\javanano_generator.h include\google\protobuf\compiler\javanano\javanano_generator.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\js\js_generator.h include\google\protobuf\compiler\js\js_generator.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_generator.h include\google\protobuf\compiler\objectivec\objectivec_generator.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_helpers.h include\google\protobuf\compiler\objectivec\objectivec_helpers.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\parser.h include\google\protobuf\compiler\parser.h
@@ -72,7 +74,6 @@ copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\source_context.pb.h in
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\struct.pb.h include\google\protobuf\struct.pb.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomic_sequence_num.h include\google\protobuf\stubs\atomic_sequence_num.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops.h include\google\protobuf\stubs\atomicops.h
-copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_power.h include\google\protobuf\stubs\atomicops_internals_power.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_arm64_gcc.h include\google\protobuf\stubs\atomicops_internals_arm64_gcc.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_arm_gcc.h include\google\protobuf\stubs\atomicops_internals_arm_gcc.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_arm_qnx.h include\google\protobuf\stubs\atomicops_internals_arm_qnx.h
@@ -81,6 +82,7 @@ copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_intern
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_macosx.h include\google\protobuf\stubs\atomicops_internals_macosx.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_mips_gcc.h include\google\protobuf\stubs\atomicops_internals_mips_gcc.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_pnacl.h include\google\protobuf\stubs\atomicops_internals_pnacl.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_power.h include\google\protobuf\stubs\atomicops_internals_power.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_solaris.h include\google\protobuf\stubs\atomicops_internals_solaris.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_tsan.h include\google\protobuf\stubs\atomicops_internals_tsan.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\atomicops_internals_x86_gcc.h include\google\protobuf\stubs\atomicops_internals_x86_gcc.h
diff --git a/cmake/libprotobuf.cmake b/cmake/libprotobuf.cmake
index a6ee8f2e..8930c1ca 100644
--- a/cmake/libprotobuf.cmake
+++ b/cmake/libprotobuf.cmake
@@ -40,6 +40,7 @@ set(libprotobuf_files
${protobuf_source_dir}/src/google/protobuf/util/internal/json_objectwriter.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/json_stream_parser.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/object_writer.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/proto_writer.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/type_info.cc
diff --git a/cmake/libprotoc.cmake b/cmake/libprotoc.cmake
index 4e628905..8df8986a 100644
--- a/cmake/libprotoc.cmake
+++ b/cmake/libprotoc.cmake
@@ -37,6 +37,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_enum_field_lite.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_enum_lite.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_extension.cc
+ ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_extension_lite.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_file.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_generator.cc
@@ -70,6 +71,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/javanano/javanano_message.cc
${protobuf_source_dir}/src/google/protobuf/compiler/javanano/javanano_message_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
+ ${protobuf_source_dir}/src/google/protobuf/compiler/js/js_generator.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_enum.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_extension.cc
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index 06d98860..941f33d8 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -63,6 +63,7 @@ set(tests_protos
google/protobuf/util/internal/testdata/struct.proto
google/protobuf/util/internal/testdata/timestamp_duration.proto
google/protobuf/util/json_format_proto3.proto
+ google/protobuf/util/message_differencer_unittest.proto
)
macro(compile_proto_file filename)
@@ -99,8 +100,6 @@ set(common_test_files
${protobuf_source_dir}/src/google/protobuf/test_util.cc
${protobuf_source_dir}/src/google/protobuf/testing/file.cc
${protobuf_source_dir}/src/google/protobuf/testing/googletest.cc
- ${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
- ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.cc
)
set(common_lite_test_files
@@ -121,6 +120,7 @@ set(tests_files
${protobuf_source_dir}/src/google/protobuf/compiler/importer_unittest.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_plugin_unittest.cc
+ ${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc
${protobuf_source_dir}/src/google/protobuf/compiler/parser_unittest.cc
${protobuf_source_dir}/src/google/protobuf/compiler/python/python_plugin_unittest.cc
@@ -166,7 +166,9 @@ set(tests_files
${protobuf_source_dir}/src/google/protobuf/util/internal/json_stream_parser_test.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource_test.cc
${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.cc
${protobuf_source_dir}/src/google/protobuf/util/json_util_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/message_differencer_unittest.cc
${protobuf_source_dir}/src/google/protobuf/util/time_util_test.cc
${protobuf_source_dir}/src/google/protobuf/util/type_resolver_util_test.cc
${protobuf_source_dir}/src/google/protobuf/well_known_types_unittest.cc
diff --git a/configure.ac b/configure.ac
index 0e587893..33a6c64d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,7 +12,7 @@ AC_PREREQ(2.59)
# In the SVN trunk, the version should always be the next anticipated release
# version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed
# the size of one file name in the dist tarfile over the 99-char limit.)
-AC_INIT([Protocol Buffers],[3.0.0-beta-1],[protobuf@googlegroups.com],[protobuf])
+AC_INIT([Protocol Buffers],[3.0.0-beta-2],[protobuf@googlegroups.com],[protobuf])
AM_MAINTAINER_MODE([enable])
@@ -26,7 +26,7 @@ AC_CONFIG_MACRO_DIR([m4])
AC_ARG_VAR(DIST_LANG, [language to include in the distribution package (i.e., make dist)])
case "$DIST_LANG" in
"") DIST_LANG=all ;;
- all | cpp | csharp | java | python | javanano | objectivec | ruby) ;;
+ all | cpp | csharp | java | python | javanano | objectivec | ruby | js) ;;
*) AC_MSG_FAILURE([unknown language: $DIST_LANG]) ;;
esac
AC_SUBST(DIST_LANG)
@@ -59,6 +59,7 @@ AC_LANG([C++])
ACX_USE_SYSTEM_EXTENSIONS
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
+AC_PROG_OBJC
# test_util.cc takes forever to compile with GCC and optimization turned on.
AC_MSG_CHECKING([C++ compiler flags...])
@@ -163,6 +164,15 @@ case "$target_os" in
;;
esac
+# Enable ObjC support for conformance directory on OS X.
+OBJC_CONFORMANCE_TEST=0
+case "$target_os" in
+ darwin*)
+ OBJC_CONFORMANCE_TEST=1
+ ;;
+esac
+AM_CONDITIONAL([OBJC_CONFORMANCE_TEST], [test $OBJC_CONFORMANCE_TEST = 1])
+
# HACK: Make gmock's configure script pick up our copy of CFLAGS and CXXFLAGS,
# since the flags added by ACX_CHECK_SUNCC must be used when compiling gmock
# too.
diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java
index 25646b85..a983ba3c 100644
--- a/conformance/ConformanceJava.java
+++ b/conformance/ConformanceJava.java
@@ -1,9 +1,12 @@
import com.google.protobuf.conformance.Conformance;
+import com.google.protobuf.util.JsonFormat;
+import com.google.protobuf.util.JsonFormat.TypeRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
class ConformanceJava {
private int testCount = 0;
+ private TypeRegistry typeRegistry;
private boolean readFromStdin(byte[] buf, int len) throws Exception {
int ofs = 0;
@@ -29,7 +32,10 @@ class ConformanceJava {
if (!readFromStdin(buf, 4)) {
return -1;
}
- return buf[0] | (buf[1] << 1) | (buf[2] << 2) | (buf[3] << 3);
+ return (buf[0] & 0xff)
+ | ((buf[1] & 0xff) << 8)
+ | ((buf[2] & 0xff) << 16)
+ | ((buf[3] & 0xff) << 24);
}
private void writeLittleEndianIntToStdout(int val) throws Exception {
@@ -54,7 +60,15 @@ class ConformanceJava {
break;
}
case JSON_PAYLOAD: {
- return Conformance.ConformanceResponse.newBuilder().setSkipped("JSON not yet supported.").build();
+ try {
+ Conformance.TestAllTypes.Builder builder = Conformance.TestAllTypes.newBuilder();
+ JsonFormat.parser().usingTypeRegistry(typeRegistry)
+ .merge(request.getJsonPayload(), builder);
+ testMessage = builder.build();
+ } catch (InvalidProtocolBufferException e) {
+ return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
+ }
+ break;
}
case PAYLOAD_NOT_SET: {
throw new RuntimeException("Request didn't have payload.");
@@ -73,7 +87,13 @@ class ConformanceJava {
return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build();
case JSON:
- return Conformance.ConformanceResponse.newBuilder().setSkipped("JSON not yet supported.").build();
+ try {
+ return Conformance.ConformanceResponse.newBuilder().setJsonPayload(
+ JsonFormat.printer().usingTypeRegistry(typeRegistry).print(testMessage)).build();
+ } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
+ return Conformance.ConformanceResponse.newBuilder().setSerializeError(
+ e.getMessage()).build();
+ }
default: {
throw new RuntimeException("Unexpected request output.");
@@ -106,6 +126,8 @@ class ConformanceJava {
}
public void run() throws Exception {
+ typeRegistry = TypeRegistry.newBuilder().add(
+ Conformance.TestAllTypes.getDescriptor()).build();
while (doTestIo()) {
// Empty.
}
diff --git a/conformance/Makefile.am b/conformance/Makefile.am
index b6fda2a8..2a43113b 100644
--- a/conformance/Makefile.am
+++ b/conformance/Makefile.am
@@ -1,32 +1,93 @@
## Process this file with automake to produce Makefile.in
-protoc_inputs = \
+conformance_protoc_inputs = \
conformance.proto
+well_known_type_protoc_inputs = \
+ $(top_srcdir)/src/google/protobuf/any.proto \
+ $(top_srcdir)/src/google/protobuf/duration.proto \
+ $(top_srcdir)/src/google/protobuf/field_mask.proto \
+ $(top_srcdir)/src/google/protobuf/struct.proto \
+ $(top_srcdir)/src/google/protobuf/timestamp.proto \
+ $(top_srcdir)/src/google/protobuf/wrappers.proto
+
+
protoc_outputs = \
conformance.pb.cc \
conformance.pb.h
other_language_protoc_outputs = \
conformance.rb \
- com/google/protobuf/conformance/Conformance.java
+ com/google/protobuf/conformance/Conformance.java \
+ conformance_pb2.py \
+ Conformance.pbobjc.h \
+ Conformance.pbobjc.m
bin_PROGRAMS = conformance-test-runner conformance-cpp
+# All source files excepet C++/Objective-C ones should be explicitly listed
+# here because the autoconf tools don't include files of other languages
+# automatically.
+EXTRA_DIST = \
+ ConformanceJava.java \
+ README.md \
+ conformance.proto \
+ conformance_python.py \
+ conformance_ruby.rb \
+ failure_list_cpp.txt \
+ failure_list_csharp.txt \
+ failure_list_java.txt \
+ failure_list_objc.txt \
+ failure_list_python.txt \
+ failure_list_python_cpp.txt \
+ failure_list_python-post26.txt \
+ failure_list_ruby.txt
+
conformance_test_runner_LDADD = $(top_srcdir)/src/libprotobuf.la
-conformance_test_runner_SOURCES = conformance_test.cc conformance_test_runner.cc
+conformance_test_runner_SOURCES = conformance_test.h conformance_test.cc \
+ conformance_test_runner.cc \
+ third_party/jsoncpp/json.h \
+ third_party/jsoncpp/jsoncpp.cpp
nodist_conformance_test_runner_SOURCES = conformance.pb.cc
-conformance_test_runner_CPPFLAGS = -I$(top_srcdir)/src
+conformance_test_runner_CPPFLAGS = -I$(top_srcdir)/src -I$(srcdir)
+conformance_test_runner_CXXFLAGS = -std=c++11
+# Explicit deps beacuse BUILT_SOURCES are only done before a "make all/check"
+# so a direct "make test_cpp" could fail if parallel enough.
+conformance_test_runner-conformance_test.$(OBJEXT): conformance.pb.h
+conformance_test_runner-conformance_test_runner.$(OBJEXT): conformance.pb.h
conformance_cpp_LDADD = $(top_srcdir)/src/libprotobuf.la
conformance_cpp_SOURCES = conformance_cpp.cc
nodist_conformance_cpp_SOURCES = conformance.pb.cc
conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src
+# Explicit dep beacuse BUILT_SOURCES are only done before a "make all/check"
+# so a direct "make test_cpp" could fail if parallel enough.
+conformance_cpp-conformance_cpp.$(OBJEXT): conformance.pb.h
+
+if OBJC_CONFORMANCE_TEST
+
+bin_PROGRAMS += conformance-objc
+
+conformance_objc_SOURCES = conformance_objc.m ../objectivec/GPBProtocolBuffers.m
+nodist_conformance_objc_SOURCES = Conformance.pbobjc.m
+# On travis, the build fails without the isysroot because whatever system
+# headers are being found don't include generics support for
+# NSArray/NSDictionary, the only guess is their image at one time had an odd
+# setup for Xcode and old frameworks are being found.
+conformance_objc_CPPFLAGS = -I$(top_srcdir)/objectivec -isysroot `xcrun --sdk macosx --show-sdk-path`
+conformance_objc_LDFLAGS = -framework Foundation
+# Explicit dep beacuse BUILT_SOURCES are only done before a "make all/check"
+# so a direct "make test_objc" could fail if parallel enough.
+conformance_objc-conformance_objc.$(OBJEXT): Conformance.pbobjc.h
+
+endif
if USE_EXTERNAL_PROTOC
-protoc_middleman: $(protoc_inputs)
- $(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. --ruby_out=. $^
+# Some implementations include pre-generated versions of well-known types.
+protoc_middleman: $(conformance_protoc_inputs) $(well_known_type_protoc_inputs)
+ $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --objc_out=. --python_out=. $(conformance_protoc_inputs)
+ $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --python_out=. $(well_known_type_protoc_inputs)
touch protoc_middleman
else
@@ -34,8 +95,9 @@ else
# We have to cd to $(srcdir) before executing protoc because $(protoc_inputs) is
# relative to srcdir, which may not be the same as the current directory when
# building out-of-tree.
-protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs)
- oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd $(protoc_inputs) )
+protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(conformance_protoc_inputs) $(well_known_type_protoc_inputs)
+ oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --objc_out=$$oldpwd --python_out=$$oldpwd $(conformance_protoc_inputs) )
+ oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --python_out=$$oldpwd $(well_known_type_protoc_inputs) )
touch protoc_middleman
endif
@@ -44,7 +106,7 @@ $(protoc_outputs): protoc_middleman
$(other_language_protoc_outputs): protoc_middleman
-BUILT_SOURCES = $(protoc_outputs)
+BUILT_SOURCES = $(protoc_outputs) $(other_language_protoc_outputs)
CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java conformance-csharp $(other_language_protoc_outputs)
@@ -52,13 +114,13 @@ MAINTAINERCLEANFILES = \
Makefile.in
javac_middleman: ConformanceJava.java protoc_middleman $(other_language_protoc_outputs)
- javac -classpath ../java/target/classes ConformanceJava.java com/google/protobuf/conformance/Conformance.java
+ jar=`ls ../java/util/target/*jar-with-dependencies.jar` && javac -classpath ../java/target/classes:$$jar ConformanceJava.java com/google/protobuf/conformance/Conformance.java
@touch javac_middleman
conformance-java: javac_middleman
@echo "Writing shortcut script conformance-java..."
@echo '#! /bin/sh' > conformance-java
- @echo 'java -classpath .:../java/target/classes ConformanceJava "$$@"' >> conformance-java
+ @jar=`ls ../java/util/target/*jar-with-dependencies.jar` && echo java -classpath .:../java/target/classes:$$jar ConformanceJava '$$@' >> conformance-java
@chmod +x conformance-java
# Currently the conformance code is alongside the rest of the C#
@@ -75,10 +137,25 @@ test_cpp: protoc_middleman conformance-test-runner conformance-cpp
./conformance-test-runner --failure_list failure_list_cpp.txt ./conformance-cpp
test_java: protoc_middleman conformance-test-runner conformance-java
- ./conformance-test-runner ./conformance-java
+ ./conformance-test-runner --failure_list failure_list_java.txt ./conformance-java
test_csharp: protoc_middleman conformance-test-runner conformance-csharp
./conformance-test-runner --failure_list failure_list_csharp.txt ./conformance-csharp
test_ruby: protoc_middleman conformance-test-runner $(other_language_protoc_outputs)
RUBYLIB=../ruby/lib:. ./conformance-test-runner --failure_list failure_list_ruby.txt ./conformance_ruby.rb
+
+# These depend on library paths being properly set up. The easiest way to
+# run them is to just use "tox" from the python dir.
+test_python: protoc_middleman conformance-test-runner
+ ./conformance-test-runner --failure_list failure_list_python.txt $(CONFORMANCE_PYTHON_EXTRA_FAILURES) ./conformance_python.py
+
+test_python_cpp: protoc_middleman conformance-test-runner
+ ./conformance-test-runner --failure_list failure_list_python_cpp.txt $(CONFORMANCE_PYTHON_EXTRA_FAILURES) ./conformance_python.py
+
+if OBJC_CONFORMANCE_TEST
+
+test_objc: protoc_middleman conformance-test-runner conformance-objc
+ ./conformance-test-runner --failure_list failure_list_objc.txt ./conformance-objc
+
+endif
diff --git a/conformance/conformance.proto b/conformance/conformance.proto
index 714cbe78..fc96074a 100644
--- a/conformance/conformance.proto
+++ b/conformance/conformance.proto
@@ -32,6 +32,13 @@ syntax = "proto3";
package conformance;
option java_package = "com.google.protobuf.conformance";
+import "google/protobuf/any.proto";
+import "google/protobuf/duration.proto";
+import "google/protobuf/field_mask.proto";
+import "google/protobuf/struct.proto";
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/wrappers.proto";
+
// This defines the conformance testing protocol. This protocol exists between
// the conformance test suite itself and the code being tested. For each test,
// the suite will send a ConformanceRequest message and expect a
@@ -84,6 +91,11 @@ message ConformanceResponse {
// test. Some of the test cases are intentionally invalid input.
string parse_error = 1;
+ // If the input was successfully parsed but errors occurred when
+ // serializing it to the requested output format, set the error message in
+ // this field.
+ string serialize_error = 6;
+
// This should be set if some other error occurred. This will always
// indicate that the test failed. The string can provide more information
// about the failure.
@@ -199,6 +211,55 @@ message TestAllTypes {
string oneof_string = 113;
bytes oneof_bytes = 114;
}
+
+ // Well-known types
+ google.protobuf.BoolValue optional_bool_wrapper = 201;
+ google.protobuf.Int32Value optional_int32_wrapper = 202;
+ google.protobuf.Int64Value optional_int64_wrapper = 203;
+ google.protobuf.UInt32Value optional_uint32_wrapper = 204;
+ google.protobuf.UInt64Value optional_uint64_wrapper = 205;
+ google.protobuf.FloatValue optional_float_wrapper = 206;
+ google.protobuf.DoubleValue optional_double_wrapper = 207;
+ google.protobuf.StringValue optional_string_wrapper = 208;
+ google.protobuf.BytesValue optional_bytes_wrapper = 209;
+
+ repeated google.protobuf.BoolValue repeated_bool_wrapper = 211;
+ repeated google.protobuf.Int32Value repeated_int32_wrapper = 212;
+ repeated google.protobuf.Int64Value repeated_int64_wrapper = 213;
+ repeated google.protobuf.UInt32Value repeated_uint32_wrapper = 214;
+ repeated google.protobuf.UInt64Value repeated_uint64_wrapper = 215;
+ repeated google.protobuf.FloatValue repeated_float_wrapper = 216;
+ repeated google.protobuf.DoubleValue repeated_double_wrapper = 217;
+ repeated google.protobuf.StringValue repeated_string_wrapper = 218;
+ repeated google.protobuf.BytesValue repeated_bytes_wrapper = 219;
+
+ google.protobuf.Duration optional_duration = 301;
+ google.protobuf.Timestamp optional_timestamp = 302;
+ google.protobuf.FieldMask optional_field_mask = 303;
+ google.protobuf.Struct optional_struct = 304;
+ google.protobuf.Any optional_any = 305;
+ google.protobuf.Value optional_value = 306;
+
+ repeated google.protobuf.Duration repeated_duration = 311;
+ repeated google.protobuf.Timestamp repeated_timestamp = 312;
+ repeated google.protobuf.FieldMask repeated_fieldmask = 313;
+ repeated google.protobuf.Struct repeated_struct = 324;
+ repeated google.protobuf.Any repeated_any = 315;
+ repeated google.protobuf.Value repeated_value = 316;
+
+ // Test field-name-to-JSON-name convention.
+ int32 fieldname1 = 401;
+ int32 field_name2 = 402;
+ int32 _field_name3 = 403;
+ int32 field__name4_ = 404;
+ int32 field0name5 = 405;
+ int32 field_0_name6 = 406;
+ int32 fieldName7 = 407;
+ int32 FieldName8 = 408;
+ int32 field_Name9 = 409;
+ int32 Field_Name10 = 410;
+ int32 FIELD_NAME11 = 411;
+ int32 FIELD_name12 = 412;
}
message ForeignMessage {
diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc
index 863b6a5b..1a265493 100644
--- a/conformance/conformance_cpp.cc
+++ b/conformance/conformance_cpp.cc
@@ -108,7 +108,11 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
return;
}
- GOOGLE_CHECK(test_message.ParseFromString(proto_binary));
+ if (!test_message.ParseFromString(proto_binary)) {
+ response->set_runtime_error(
+ "Parsing JSON generates invalid proto output.");
+ return;
+ }
break;
}
@@ -132,9 +136,18 @@ void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
GOOGLE_CHECK(test_message.SerializeToString(&proto_binary));
Status status = BinaryToJsonString(type_resolver, *type_url, proto_binary,
response->mutable_json_payload());
- GOOGLE_CHECK(status.ok());
+ if (!status.ok()) {
+ response->set_serialize_error(
+ string("Failed to serialize JSON output: ") +
+ status.error_message().as_string());
+ return;
+ }
break;
}
+
+ default:
+ GOOGLE_LOG(FATAL) << "Unknown output format: "
+ << request.requested_output_format();
}
}
diff --git a/conformance/conformance_objc.m b/conformance/conformance_objc.m
new file mode 100644
index 00000000..1124bfeb
--- /dev/null
+++ b/conformance/conformance_objc.m
@@ -0,0 +1,179 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "Conformance.pbobjc.h"
+
+static void Die(NSString *format, ...) __dead2;
+
+static BOOL verbose = NO;
+static int32_t testCount = 0;
+
+static void Die(NSString *format, ...) {
+ va_list args;
+ va_start(args, format);
+ NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
+ NSLog(@"%@", msg);
+ va_end(args);
+ [msg release];
+ exit(66);
+}
+
+static NSData *CheckedReadDataOfLength(NSFileHandle *handle, NSUInteger numBytes) {
+ NSData *data = [handle readDataOfLength:numBytes];
+ NSUInteger dataLen = data.length;
+ if (dataLen == 0) {
+ return nil; // EOF.
+ }
+ if (dataLen != numBytes) {
+ Die(@"Failed to read the request length (%d), only got: %@",
+ numBytes, data);
+ }
+ return data;
+}
+
+static ConformanceResponse *DoTest(ConformanceRequest *request) {
+ ConformanceResponse *response = [ConformanceResponse message];
+ TestAllTypes *testMessage = nil;
+
+ switch (request.payloadOneOfCase) {
+ case ConformanceRequest_Payload_OneOfCase_GPBUnsetOneOfCase:
+ Die(@"Request didn't have a payload: %@", request);
+ break;
+
+ case ConformanceRequest_Payload_OneOfCase_ProtobufPayload: {
+ NSError *error = nil;
+ testMessage = [TestAllTypes parseFromData:request.protobufPayload
+ error:&error];
+ if (!testMessage) {
+ response.parseError =
+ [NSString stringWithFormat:@"Parse error: %@", error];
+ }
+ break;
+ }
+
+ case ConformanceRequest_Payload_OneOfCase_JsonPayload:
+ response.skipped = @"ObjC doesn't support parsing JSON";
+ break;
+ }
+
+ if (testMessage) {
+ switch (request.requestedOutputFormat) {
+ case WireFormat_GPBUnrecognizedEnumeratorValue:
+ case WireFormat_Unspecified:
+ Die(@"Unrecognized/unspecified output format: %@", request);
+ break;
+
+ case WireFormat_Protobuf:
+ response.protobufPayload = testMessage.data;
+ if (!response.protobufPayload) {
+ response.serializeError =
+ [NSString stringWithFormat:@"Failed to make data from: %@", testMessage];
+ }
+ break;
+
+ case WireFormat_Json:
+ response.skipped = @"ObjC doesn't support generating JSON";
+ break;
+ }
+ }
+
+ return response;
+}
+
+static uint32_t UInt32FromLittleEndianData(NSData *data) {
+ if (data.length != sizeof(uint32_t)) {
+ Die(@"Data not the right size for uint32_t: %@", data);
+ }
+ uint32_t value;
+ memcpy(&value, data.bytes, sizeof(uint32_t));
+ return CFSwapInt32LittleToHost(value);
+}
+
+static NSData *UInt32ToLittleEndianData(uint32_t num) {
+ uint32_t value = CFSwapInt32HostToLittle(num);
+ return [NSData dataWithBytes:&value length:sizeof(uint32_t)];
+}
+
+static BOOL DoTestIo(NSFileHandle *input, NSFileHandle *output) {
+ // See conformance_test_runner.cc for the wire format.
+ NSData *data = CheckedReadDataOfLength(input, sizeof(uint32_t));
+ if (!data) {
+ // EOF.
+ return NO;
+ }
+ uint32_t numBytes = UInt32FromLittleEndianData(data);
+ data = CheckedReadDataOfLength(input, numBytes);
+ if (!data) {
+ Die(@"Failed to read request");
+ }
+
+ NSError *error = nil;
+ ConformanceRequest *request = [ConformanceRequest parseFromData:data
+ error:&error];
+ if (!request) {
+ Die(@"Failed to parse the message data: %@", error);
+ }
+
+ ConformanceResponse *response = DoTest(request);
+ if (!response) {
+ Die(@"Failed to make a reply from %@", request);
+ }
+
+ data = response.data;
+ [output writeData:UInt32ToLittleEndianData((int32_t)data.length)];
+ [output writeData:data];
+
+ if (verbose) {
+ NSLog(@"Request: %@", request);
+ NSLog(@"Response: %@", response);
+ }
+
+ ++testCount;
+ return YES;
+}
+
+int main(int argc, const char *argv[]) {
+ @autoreleasepool {
+ NSFileHandle *input = [[NSFileHandle fileHandleWithStandardInput] retain];
+ NSFileHandle *output = [[NSFileHandle fileHandleWithStandardOutput] retain];
+
+ BOOL notDone = YES;
+ while (notDone) {
+ @autoreleasepool {
+ notDone = DoTestIo(input, output);
+ }
+ }
+
+ NSLog(@"Received EOF from test runner after %d tests, exiting.", testCount);
+ }
+ return 0;
+}
diff --git a/conformance/conformance_python.py b/conformance/conformance_python.py
new file mode 100755
index 00000000..a490c8e8
--- /dev/null
+++ b/conformance/conformance_python.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+#
+# Protocol Buffers - Google's data interchange format
+# Copyright 2008 Google Inc. All rights reserved.
+# https://developers.google.com/protocol-buffers/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""A conformance test implementation for the Python protobuf library.
+
+See conformance.proto for more information.
+"""
+
+import struct
+import sys
+import os
+from google.protobuf import message
+from google.protobuf import json_format
+import conformance_pb2
+
+sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)
+sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0)
+
+test_count = 0
+verbose = False
+
+class ProtocolError(Exception):
+ pass
+
+def do_test(request):
+ test_message = conformance_pb2.TestAllTypes()
+ response = conformance_pb2.ConformanceResponse()
+ test_message = conformance_pb2.TestAllTypes()
+
+ try:
+ if request.WhichOneof('payload') == 'protobuf_payload':
+ try:
+ test_message.ParseFromString(request.protobuf_payload)
+ except message.DecodeError as e:
+ response.parse_error = str(e)
+ return response
+
+ elif request.WhichOneof('payload') == 'json_payload':
+ try:
+ json_format.Parse(request.json_payload, test_message)
+ except json_format.ParseError as e:
+ response.parse_error = str(e)
+ return response
+
+ else:
+ raise ProtocolError("Request didn't have payload.")
+
+ if request.requested_output_format == conformance_pb2.UNSPECIFIED:
+ raise ProtocolError("Unspecified output format")
+
+ elif request.requested_output_format == conformance_pb2.PROTOBUF:
+ response.protobuf_payload = test_message.SerializeToString()
+
+ elif request.requested_output_format == conformance_pb2.JSON:
+ response.json_payload = json_format.MessageToJson(test_message)
+
+ except Exception as e:
+ response.runtime_error = str(e)
+
+ return response
+
+def do_test_io():
+ length_bytes = sys.stdin.read(4)
+ if len(length_bytes) == 0:
+ return False # EOF
+ elif len(length_bytes) != 4:
+ raise IOError("I/O error")
+
+ # "I" is "unsigned int", so this depends on running on a platform with
+ # 32-bit "unsigned int" type. The Python struct module unfortunately
+ # has no format specifier for uint32_t.
+ length = struct.unpack("<I", length_bytes)[0]
+ serialized_request = sys.stdin.read(length)
+ if len(serialized_request) != length:
+ raise IOError("I/O error")
+
+ request = conformance_pb2.ConformanceRequest()
+ request.ParseFromString(serialized_request)
+
+ response = do_test(request)
+
+ serialized_response = response.SerializeToString()
+ sys.stdout.write(struct.pack("<I", len(serialized_response)))
+ sys.stdout.write(serialized_response)
+ sys.stdout.flush()
+
+ if verbose:
+ sys.stderr.write("conformance_python: request=%s, response=%s\n" % (
+ request.ShortDebugString().c_str(),
+ response.ShortDebugString().c_str()))
+
+ global test_count
+ test_count += 1
+
+ return True
+
+while True:
+ if not do_test_io():
+ sys.stderr.write("conformance_python: received EOF from test runner " +
+ "after %s tests, exiting\n" % (test_count))
+ sys.exit(0)
diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc
index 0ee201f3..fc0605bf 100644
--- a/conformance/conformance_test.cc
+++ b/conformance/conformance_test.cc
@@ -37,10 +37,13 @@
#include <google/protobuf/stubs/stringprintf.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/util/json_util.h>
+#include <google/protobuf/util/field_comparator.h>
#include <google/protobuf/util/message_differencer.h>
#include <google/protobuf/util/type_resolver_util.h>
#include <google/protobuf/wire_format_lite.h>
+#include "third_party/jsoncpp/json.h"
+
using conformance::ConformanceRequest;
using conformance::ConformanceResponse;
using conformance::TestAllTypes;
@@ -49,6 +52,7 @@ using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::internal::WireFormatLite;
using google::protobuf::TextFormat;
+using google::protobuf::util::DefaultFieldComparator;
using google::protobuf::util::JsonToBinaryString;
using google::protobuf::util::MessageDifferencer;
using google::protobuf::util::NewTypeResolverForDescriptorPool;
@@ -220,7 +224,7 @@ void ConformanceTestSuite::RunTest(const string& test_name,
string serialized_response;
request.SerializeToString(&serialized_request);
- runner_->RunTest(serialized_request, &serialized_response);
+ runner_->RunTest(test_name, serialized_request, &serialized_response);
if (!response->ParseFromString(serialized_response)) {
response->Clear();
@@ -240,7 +244,9 @@ void ConformanceTestSuite::RunValidInputTest(
const string& equivalent_text_format, WireFormat requested_output) {
TestAllTypes reference_message;
GOOGLE_CHECK(
- TextFormat::ParseFromString(equivalent_text_format, &reference_message));
+ TextFormat::ParseFromString(equivalent_text_format, &reference_message))
+ << "Failed to parse data for test case: " << test_name
+ << ", data: " << equivalent_text_format;
ConformanceRequest request;
ConformanceResponse response;
@@ -254,9 +260,8 @@ void ConformanceTestSuite::RunValidInputTest(
request.set_json_payload(input);
break;
- case conformance::UNSPECIFIED:
+ default:
GOOGLE_LOG(FATAL) << "Unspecified input format";
-
}
request.set_requested_output_format(requested_output);
@@ -268,8 +273,9 @@ void ConformanceTestSuite::RunValidInputTest(
switch (response.result_case()) {
case ConformanceResponse::kParseError:
case ConformanceResponse::kRuntimeError:
+ case ConformanceResponse::kSerializeError:
ReportFailure(test_name, request, response,
- "Failed to parse valid JSON input.");
+ "Failed to parse JSON input or produce JSON output.");
return;
case ConformanceResponse::kSkipped:
@@ -293,7 +299,13 @@ void ConformanceTestSuite::RunValidInputTest(
return;
}
- GOOGLE_CHECK(test_message.ParseFromString(binary_protobuf));
+ if (!test_message.ParseFromString(binary_protobuf)) {
+ ReportFailure(test_name, request, response,
+ "INTERNAL ERROR: internal JSON->protobuf transcode "
+ "yielded unparseable proto.");
+ return;
+ }
+
break;
}
@@ -313,13 +325,20 @@ void ConformanceTestSuite::RunValidInputTest(
break;
}
+
+ default:
+ GOOGLE_LOG(FATAL) << test_name << ": unknown payload type: "
+ << response.result_case();
}
MessageDifferencer differencer;
+ DefaultFieldComparator field_comparator;
+ field_comparator.set_treat_nan_as_equal(true);
+ differencer.set_field_comparator(&field_comparator);
string differences;
differencer.ReportDifferencesToString(&differences);
- if (differencer.Equals(reference_message, test_message)) {
+ if (differencer.Compare(reference_message, test_message)) {
ReportSuccess(test_name);
} else {
ReportFailure(test_name, request, response,
@@ -343,6 +362,8 @@ void ConformanceTestSuite::ExpectParseFailureForProto(
RunTest(effective_test_name, request, &response);
if (response.result_case() == ConformanceResponse::kParseError) {
ReportSuccess(effective_test_name);
+ } else if (response.result_case() == ConformanceResponse::kSkipped) {
+ ReportSkip(effective_test_name, request, response);
} else {
ReportFailure(effective_test_name, request, response,
"Should have failed to parse, but didn't.");
@@ -362,13 +383,112 @@ void ConformanceTestSuite::ExpectHardParseFailureForProto(
void ConformanceTestSuite::RunValidJsonTest(
const string& test_name, const string& input_json,
const string& equivalent_text_format) {
- RunValidInputTest("JsonInput." + test_name + ".JsonOutput", input_json,
+ RunValidInputTest("JsonInput." + test_name + ".ProtobufOutput", input_json,
conformance::JSON, equivalent_text_format,
conformance::PROTOBUF);
- RunValidInputTest("JsonInput." + test_name + ".ProtobufOutput", input_json, conformance::JSON,
+ RunValidInputTest("JsonInput." + test_name + ".JsonOutput", input_json,
+ conformance::JSON, equivalent_text_format,
+ conformance::JSON);
+}
+
+void ConformanceTestSuite::RunValidJsonTestWithProtobufInput(
+ const string& test_name, const TestAllTypes& input,
+ const string& equivalent_text_format) {
+ RunValidInputTest("ProtobufInput." + test_name + ".JsonOutput",
+ input.SerializeAsString(), conformance::PROTOBUF,
equivalent_text_format, conformance::JSON);
}
+// According to proto3 JSON specification, JSON serializers follow more strict
+// rules than parsers (e.g., a serializer must serialize int32 values as JSON
+// numbers while the parser is allowed to accept them as JSON strings). This
+// method allows strict checking on a proto3 JSON serializer by inspecting
+// the JSON output directly.
+void ConformanceTestSuite::RunValidJsonTestWithValidator(
+ const string& test_name, const string& input_json,
+ const Validator& validator) {
+ ConformanceRequest request;
+ ConformanceResponse response;
+ request.set_json_payload(input_json);
+ request.set_requested_output_format(conformance::JSON);
+
+ string effective_test_name = "JsonInput." + test_name + ".Validator";
+
+ RunTest(effective_test_name, request, &response);
+
+ if (response.result_case() == ConformanceResponse::kSkipped) {
+ ReportSkip(effective_test_name, request, response);
+ return;
+ }
+
+ if (response.result_case() != ConformanceResponse::kJsonPayload) {
+ ReportFailure(effective_test_name, request, response,
+ "Expected JSON payload but got type %d.",
+ response.result_case());
+ return;
+ }
+ Json::Reader reader;
+ Json::Value value;
+ if (!reader.parse(response.json_payload(), value)) {
+ ReportFailure(effective_test_name, request, response,
+ "JSON payload cannot be parsed as valid JSON: %s",
+ reader.getFormattedErrorMessages().c_str());
+ return;
+ }
+ if (!validator(value)) {
+ ReportFailure(effective_test_name, request, response,
+ "JSON payload validation failed.");
+ return;
+ }
+ ReportSuccess(effective_test_name);
+}
+
+void ConformanceTestSuite::ExpectParseFailureForJson(
+ const string& test_name, const string& input_json) {
+ ConformanceRequest request;
+ ConformanceResponse response;
+ request.set_json_payload(input_json);
+ string effective_test_name = "JsonInput." + test_name;
+
+ // We don't expect output, but if the program erroneously accepts the protobuf
+ // we let it send its response as this. We must not leave it unspecified.
+ request.set_requested_output_format(conformance::JSON);
+
+ RunTest(effective_test_name, request, &response);
+ if (response.result_case() == ConformanceResponse::kParseError) {
+ ReportSuccess(effective_test_name);
+ } else if (response.result_case() == ConformanceResponse::kSkipped) {
+ ReportSkip(effective_test_name, request, response);
+ } else {
+ ReportFailure(effective_test_name, request, response,
+ "Should have failed to parse, but didn't.");
+ }
+}
+
+void ConformanceTestSuite::ExpectSerializeFailureForJson(
+ const string& test_name, const string& text_format) {
+ TestAllTypes payload_message;
+ GOOGLE_CHECK(
+ TextFormat::ParseFromString(text_format, &payload_message))
+ << "Failed to parse: " << text_format;
+
+ ConformanceRequest request;
+ ConformanceResponse response;
+ request.set_protobuf_payload(payload_message.SerializeAsString());
+ string effective_test_name = test_name + ".JsonOutput";
+ request.set_requested_output_format(conformance::JSON);
+
+ RunTest(effective_test_name, request, &response);
+ if (response.result_case() == ConformanceResponse::kSerializeError) {
+ ReportSuccess(effective_test_name);
+ } else if (response.result_case() == ConformanceResponse::kSkipped) {
+ ReportSkip(effective_test_name, request, response);
+ } else {
+ ReportFailure(effective_test_name, request, response,
+ "Should have failed to serialize, but didn't.");
+ }
+}
+
void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) {
// Incomplete values for each wire type.
static const string incompletes[6] = {
@@ -500,23 +620,1375 @@ bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
RunValidJsonTest("HelloWorld", "{\"optionalString\":\"Hello, World!\"}",
"optional_string: 'Hello, World!'");
- bool ok =
- CheckSetEmpty(expected_to_fail_,
- "These tests were listed in the failure list, but they "
- "don't exist. Remove them from the failure list") &&
+ // Test field name conventions.
+ RunValidJsonTest(
+ "FieldNameInSnakeCase",
+ R"({
+ "fieldname1": 1,
+ "fieldName2": 2,
+ "FieldName3": 3
+ })",
+ R"(
+ fieldname1: 1
+ field_name2: 2
+ _field_name3: 3
+ )");
+ RunValidJsonTest(
+ "FieldNameWithNumbers",
+ R"({
+ "field0name5": 5,
+ "field0Name6": 6
+ })",
+ R"(
+ field0name5: 5
+ field_0_name6: 6
+ )");
+ RunValidJsonTest(
+ "FieldNameWithMixedCases",
+ R"({
+ "fieldName7": 7,
+ "fieldName8": 8,
+ "fieldName9": 9,
+ "fieldName10": 10,
+ "fIELDNAME11": 11,
+ "fIELDName12": 12
+ })",
+ R"(
+ fieldName7: 7
+ FieldName8: 8
+ field_Name9: 9
+ Field_Name10: 10
+ FIELD_NAME11: 11
+ FIELD_name12: 12
+ )");
+ // Using the original proto field name in JSON is also allowed.
+ RunValidJsonTest(
+ "OriginalProtoFieldName",
+ R"({
+ "fieldname1": 1,
+ "field_name2": 2,
+ "_field_name3": 3,
+ "field0name5": 5,
+ "field_0_name6": 6,
+ "fieldName7": 7,
+ "FieldName8": 8,
+ "field_Name9": 9,
+ "Field_Name10": 10,
+ "FIELD_NAME11": 11,
+ "FIELD_name12": 12
+ })",
+ R"(
+ fieldname1: 1
+ field_name2: 2
+ _field_name3: 3
+ field0name5: 5
+ field_0_name6: 6
+ fieldName7: 7
+ FieldName8: 8
+ field_Name9: 9
+ Field_Name10: 10
+ FIELD_NAME11: 11
+ FIELD_name12: 12
+ )");
+ // Field names can be escaped.
+ RunValidJsonTest(
+ "FieldNameEscaped",
+ R"({"fieldn\u0061me1": 1})",
+ "fieldname1: 1");
+ // Field names must be quoted (or it's not valid JSON).
+ ExpectParseFailureForJson(
+ "FieldNameNotQuoted",
+ "{fieldname1: 1}");
+ // Trailing comma is not allowed (not valid JSON).
+ ExpectParseFailureForJson(
+ "TrailingCommaInAnObject",
+ R"({"fieldname1":1,})");
+ // JSON doesn't support comments.
+ ExpectParseFailureForJson(
+ "JsonWithComments",
+ R"({
+ // This is a comment.
+ "fieldname1": 1
+ })");
+ // Duplicated field names are not allowed.
+ ExpectParseFailureForJson(
+ "FieldNameDuplicate",
+ R"({
+ "optionalNestedMessage": {a: 1},
+ "optionalNestedMessage": {}
+ })");
+ ExpectParseFailureForJson(
+ "FieldNameDuplicateDifferentCasing1",
+ R"({
+ "optional_nested_message": {a: 1},
+ "optionalNestedMessage": {}
+ })");
+ ExpectParseFailureForJson(
+ "FieldNameDuplicateDifferentCasing2",
+ R"({
+ "optionalNestedMessage": {a: 1},
+ "optional_nested_message": {}
+ })");
+ // Serializers should use lowerCamelCase by default.
+ RunValidJsonTestWithValidator(
+ "FieldNameInLowerCamelCase",
+ R"({
+ "fieldname1": 1,
+ "fieldName2": 2,
+ "FieldName3": 3
+ })",
+ [](const Json::Value& value) {
+ return value.isMember("fieldname1") &&
+ value.isMember("fieldName2") &&
+ value.isMember("FieldName3");
+ });
+ RunValidJsonTestWithValidator(
+ "FieldNameWithNumbers",
+ R"({
+ "field0name5": 5,
+ "field0Name6": 6
+ })",
+ [](const Json::Value& value) {
+ return value.isMember("field0name5") &&
+ value.isMember("field0Name6");
+ });
+ RunValidJsonTestWithValidator(
+ "FieldNameWithMixedCases",
+ R"({
+ "fieldName7": 7,
+ "fieldName8": 8,
+ "fieldName9": 9,
+ "fieldName10": 10,
+ "fIELDNAME11": 11,
+ "fIELDName12": 12
+ })",
+ [](const Json::Value& value) {
+ return value.isMember("fieldName7") &&
+ value.isMember("fieldName8") &&
+ value.isMember("fieldName9") &&
+ value.isMember("fieldName10") &&
+ value.isMember("fIELDNAME11") &&
+ value.isMember("fIELDName12");
+ });
+
+ // Integer fields.
+ RunValidJsonTest(
+ "Int32FieldMaxValue",
+ R"({"optionalInt32": 2147483647})",
+ "optional_int32: 2147483647");
+ RunValidJsonTest(
+ "Int32FieldMinValue",
+ R"({"optionalInt32": -2147483648})",
+ "optional_int32: -2147483648");
+ RunValidJsonTest(
+ "Uint32FieldMaxValue",
+ R"({"optionalUint32": 4294967295})",
+ "optional_uint32: 4294967295");
+ RunValidJsonTest(
+ "Int64FieldMaxValue",
+ R"({"optionalInt64": "9223372036854775807"})",
+ "optional_int64: 9223372036854775807");
+ RunValidJsonTest(
+ "Int64FieldMinValue",
+ R"({"optionalInt64": "-9223372036854775808"})",
+ "optional_int64: -9223372036854775808");
+ RunValidJsonTest(
+ "Uint64FieldMaxValue",
+ R"({"optionalUint64": "18446744073709551615"})",
+ "optional_uint64: 18446744073709551615");
+ RunValidJsonTest(
+ "Int64FieldMaxValueNotQuoted",
+ R"({"optionalInt64": 9223372036854775807})",
+ "optional_int64: 9223372036854775807");
+ RunValidJsonTest(
+ "Int64FieldMinValueNotQuoted",
+ R"({"optionalInt64": -9223372036854775808})",
+ "optional_int64: -9223372036854775808");
+ RunValidJsonTest(
+ "Uint64FieldMaxValueNotQuoted",
+ R"({"optionalUint64": 18446744073709551615})",
+ "optional_uint64: 18446744073709551615");
+ // Values can be represented as JSON strings.
+ RunValidJsonTest(
+ "Int32FieldStringValue",
+ R"({"optionalInt32": "2147483647"})",
+ "optional_int32: 2147483647");
+ RunValidJsonTest(
+ "Int32FieldStringValueEscaped",
+ R"({"optionalInt32": "2\u003147483647"})",
+ "optional_int32: 2147483647");
+
+ // Parsers reject out-of-bound integer values.
+ ExpectParseFailureForJson(
+ "Int32FieldTooLarge",
+ R"({"optionalInt32": 2147483648})");
+ ExpectParseFailureForJson(
+ "Int32FieldTooSmall",
+ R"({"optionalInt32": -2147483649})");
+ ExpectParseFailureForJson(
+ "Uint32FieldTooLarge",
+ R"({"optionalUint32": 4294967296})");
+ ExpectParseFailureForJson(
+ "Int64FieldTooLarge",
+ R"({"optionalInt64": "9223372036854775808"})");
+ ExpectParseFailureForJson(
+ "Int64FieldTooSmall",
+ R"({"optionalInt64": "-9223372036854775809"})");
+ ExpectParseFailureForJson(
+ "Uint64FieldTooLarge",
+ R"({"optionalUint64": "18446744073709551616"})");
+ // Parser reject non-integer numeric values as well.
+ ExpectParseFailureForJson(
+ "Int32FieldNotInteger",
+ R"({"optionalInt32": 0.5})");
+ ExpectParseFailureForJson(
+ "Uint32FieldNotInteger",
+ R"({"optionalUint32": 0.5})");
+ ExpectParseFailureForJson(
+ "Int64FieldNotInteger",
+ R"({"optionalInt64": "0.5"})");
+ ExpectParseFailureForJson(
+ "Uint64FieldNotInteger",
+ R"({"optionalUint64": "0.5"})");
+
+ // Integers but represented as float values are accepted.
+ RunValidJsonTest(
+ "Int32FieldFloatTrailingZero",
+ R"({"optionalInt32": 100000.000})",
+ "optional_int32: 100000");
+ RunValidJsonTest(
+ "Int32FieldExponentialFormat",
+ R"({"optionalInt32": 1e5})",
+ "optional_int32: 100000");
+ RunValidJsonTest(
+ "Int32FieldMaxFloatValue",
+ R"({"optionalInt32": 2.147483647e9})",
+ "optional_int32: 2147483647");
+ RunValidJsonTest(
+ "Int32FieldMinFloatValue",
+ R"({"optionalInt32": -2.147483648e9})",
+ "optional_int32: -2147483648");
+ RunValidJsonTest(
+ "Uint32FieldMaxFloatValue",
+ R"({"optionalUint32": 4.294967295e9})",
+ "optional_uint32: 4294967295");
+
+ // Parser reject non-numeric values.
+ ExpectParseFailureForJson(
+ "Int32FieldNotNumber",
+ R"({"optionalInt32": "3x3"})");
+ ExpectParseFailureForJson(
+ "Uint32FieldNotNumber",
+ R"({"optionalUint32": "3x3"})");
+ ExpectParseFailureForJson(
+ "Int64FieldNotNumber",
+ R"({"optionalInt64": "3x3"})");
+ ExpectParseFailureForJson(
+ "Uint64FieldNotNumber",
+ R"({"optionalUint64": "3x3"})");
+ // JSON does not allow "+" on numric values.
+ ExpectParseFailureForJson(
+ "Int32FieldPlusSign",
+ R"({"optionalInt32": +1})");
+ // JSON doesn't allow leading 0s.
+ ExpectParseFailureForJson(
+ "Int32FieldLeadingZero",
+ R"({"optionalInt32": 01})");
+ ExpectParseFailureForJson(
+ "Int32FieldNegativeWithLeadingZero",
+ R"({"optionalInt32": -01})");
+ // String values must follow the same syntax rule. Specifically leading
+ // or traling spaces are not allowed.
+ ExpectParseFailureForJson(
+ "Int32FieldLeadingSpace",
+ R"({"optionalInt32": " 1"})");
+ ExpectParseFailureForJson(
+ "Int32FieldTrailingSpace",
+ R"({"optionalInt32": "1 "})");
+
+ // 64-bit values are serialized as strings.
+ RunValidJsonTestWithValidator(
+ "Int64FieldBeString",
+ R"({"optionalInt64": 1})",
+ [](const Json::Value& value) {
+ return value["optionalInt64"].type() == Json::stringValue &&
+ value["optionalInt64"].asString() == "1";
+ });
+ RunValidJsonTestWithValidator(
+ "Uint64FieldBeString",
+ R"({"optionalUint64": 1})",
+ [](const Json::Value& value) {
+ return value["optionalUint64"].type() == Json::stringValue &&
+ value["optionalUint64"].asString() == "1";
+ });
+
+ // Bool fields.
+ RunValidJsonTest(
+ "BoolFieldTrue",
+ R"({"optionalBool":true})",
+ "optional_bool: true");
+ RunValidJsonTest(
+ "BoolFieldFalse",
+ R"({"optionalBool":false})",
+ "optional_bool: false");
+
+ // Other forms are not allowed.
+ ExpectParseFailureForJson(
+ "BoolFieldIntegerZero",
+ R"({"optionalBool":0})");
+ ExpectParseFailureForJson(
+ "BoolFieldIntegerOne",
+ R"({"optionalBool":1})");
+ ExpectParseFailureForJson(
+ "BoolFieldCamelCaseTrue",
+ R"({"optionalBool":True})");
+ ExpectParseFailureForJson(
+ "BoolFieldCamelCaseFalse",
+ R"({"optionalBool":False})");
+ ExpectParseFailureForJson(
+ "BoolFieldAllCapitalTrue",
+ R"({"optionalBool":TRUE})");
+ ExpectParseFailureForJson(
+ "BoolFieldAllCapitalFalse",
+ R"({"optionalBool":FALSE})");
+ ExpectParseFailureForJson(
+ "BoolFieldDoubleQuotedTrue",
+ R"({"optionalBool":"true"})");
+ ExpectParseFailureForJson(
+ "BoolFieldDoubleQuotedFalse",
+ R"({"optionalBool":"false"})");
+
+ // Float fields.
+ RunValidJsonTest(
+ "FloatFieldMinPositiveValue",
+ R"({"optionalFloat": 1.175494e-38})",
+ "optional_float: 1.175494e-38");
+ RunValidJsonTest(
+ "FloatFieldMaxNegativeValue",
+ R"({"optionalFloat": -1.175494e-38})",
+ "optional_float: -1.175494e-38");
+ RunValidJsonTest(
+ "FloatFieldMaxPositiveValue",
+ R"({"optionalFloat": 3.402823e+38})",
+ "optional_float: 3.402823e+38");
+ RunValidJsonTest(
+ "FloatFieldMinNegativeValue",
+ R"({"optionalFloat": 3.402823e+38})",
+ "optional_float: 3.402823e+38");
+ // Values can be quoted.
+ RunValidJsonTest(
+ "FloatFieldQuotedValue",
+ R"({"optionalFloat": "1"})",
+ "optional_float: 1");
+ // Special values.
+ RunValidJsonTest(
+ "FloatFieldNan",
+ R"({"optionalFloat": "NaN"})",
+ "optional_float: nan");
+ RunValidJsonTest(
+ "FloatFieldInfinity",
+ R"({"optionalFloat": "Infinity"})",
+ "optional_float: inf");
+ RunValidJsonTest(
+ "FloatFieldNegativeInfinity",
+ R"({"optionalFloat": "-Infinity"})",
+ "optional_float: -inf");
+ // Non-cannonical Nan will be correctly normalized.
+ {
+ TestAllTypes message;
+ // IEEE floating-point standard 32-bit quiet NaN:
+ // 0111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
+ message.set_optional_float(
+ WireFormatLite::DecodeFloat(0x7FA12345));
+ RunValidJsonTestWithProtobufInput(
+ "FloatFieldNormalizeQuietNan", message,
+ "optional_float: nan");
+ // IEEE floating-point standard 64-bit signaling NaN:
+ // 1111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
+ message.set_optional_float(
+ WireFormatLite::DecodeFloat(0xFFB54321));
+ RunValidJsonTestWithProtobufInput(
+ "FloatFieldNormalizeSignalingNan", message,
+ "optional_float: nan");
+ }
+
+ // Special values must be quoted.
+ ExpectParseFailureForJson(
+ "FloatFieldNanNotQuoted",
+ R"({"optionalFloat": NaN})");
+ ExpectParseFailureForJson(
+ "FloatFieldInfinityNotQuoted",
+ R"({"optionalFloat": Infinity})");
+ ExpectParseFailureForJson(
+ "FloatFieldNegativeInfinityNotQuoted",
+ R"({"optionalFloat": -Infinity})");
+ // Parsers should reject out-of-bound values.
+ ExpectParseFailureForJson(
+ "FloatFieldTooSmall",
+ R"({"optionalFloat": -3.502823e+38})");
+ ExpectParseFailureForJson(
+ "FloatFieldTooLarge",
+ R"({"optionalFloat": 3.502823e+38})");
+
+ // Double fields.
+ RunValidJsonTest(
+ "DoubleFieldMinPositiveValue",
+ R"({"optionalDouble": 2.22507e-308})",
+ "optional_double: 2.22507e-308");
+ RunValidJsonTest(
+ "DoubleFieldMaxNegativeValue",
+ R"({"optionalDouble": -2.22507e-308})",
+ "optional_double: -2.22507e-308");
+ RunValidJsonTest(
+ "DoubleFieldMaxPositiveValue",
+ R"({"optionalDouble": 1.79769e+308})",
+ "optional_double: 1.79769e+308");
+ RunValidJsonTest(
+ "DoubleFieldMinNegativeValue",
+ R"({"optionalDouble": -1.79769e+308})",
+ "optional_double: -1.79769e+308");
+ // Values can be quoted.
+ RunValidJsonTest(
+ "DoubleFieldQuotedValue",
+ R"({"optionalDouble": "1"})",
+ "optional_double: 1");
+ // Speical values.
+ RunValidJsonTest(
+ "DoubleFieldNan",
+ R"({"optionalDouble": "NaN"})",
+ "optional_double: nan");
+ RunValidJsonTest(
+ "DoubleFieldInfinity",
+ R"({"optionalDouble": "Infinity"})",
+ "optional_double: inf");
+ RunValidJsonTest(
+ "DoubleFieldNegativeInfinity",
+ R"({"optionalDouble": "-Infinity"})",
+ "optional_double: -inf");
+ // Non-cannonical Nan will be correctly normalized.
+ {
+ TestAllTypes message;
+ message.set_optional_double(
+ WireFormatLite::DecodeDouble(0x7FFA123456789ABCLL));
+ RunValidJsonTestWithProtobufInput(
+ "DoubleFieldNormalizeQuietNan", message,
+ "optional_double: nan");
+ message.set_optional_double(
+ WireFormatLite::DecodeDouble(0xFFFBCBA987654321LL));
+ RunValidJsonTestWithProtobufInput(
+ "DoubleFieldNormalizeSignalingNan", message,
+ "optional_double: nan");
+ }
- CheckSetEmpty(unexpected_failing_tests_,
- "These tests failed. If they can't be fixed right now, "
- "you can add them to the failure list so the overall "
- "suite can succeed") &&
+ // Special values must be quoted.
+ ExpectParseFailureForJson(
+ "DoubleFieldNanNotQuoted",
+ R"({"optionalDouble": NaN})");
+ ExpectParseFailureForJson(
+ "DoubleFieldInfinityNotQuoted",
+ R"({"optionalDouble": Infinity})");
+ ExpectParseFailureForJson(
+ "DoubleFieldNegativeInfinityNotQuoted",
+ R"({"optionalDouble": -Infinity})");
+
+ // Parsers should reject out-of-bound values.
+ ExpectParseFailureForJson(
+ "DoubleFieldTooSmall",
+ R"({"optionalDouble": -1.89769e+308})");
+ ExpectParseFailureForJson(
+ "DoubleFieldTooLarge",
+ R"({"optionalDouble": +1.89769e+308})");
+
+ // Enum fields.
+ RunValidJsonTest(
+ "EnumField",
+ R"({"optionalNestedEnum": "FOO"})",
+ "optional_nested_enum: FOO");
+ // Enum values must be represented as strings.
+ ExpectParseFailureForJson(
+ "EnumFieldNotQuoted",
+ R"({"optionalNestedEnum": FOO})");
+ // Numeric values are allowed.
+ RunValidJsonTest(
+ "EnumFieldNumericValueZero",
+ R"({"optionalNestedEnum": 0})",
+ "optional_nested_enum: FOO");
+ RunValidJsonTest(
+ "EnumFieldNumericValueNonZero",
+ R"({"optionalNestedEnum": 1})",
+ "optional_nested_enum: BAR");
+ // Unknown enum values are represented as numeric values.
+ RunValidJsonTestWithValidator(
+ "EnumFieldUnknownValue",
+ R"({"optionalNestedEnum": 123})",
+ [](const Json::Value& value) {
+ return value["optionalNestedEnum"].type() == Json::intValue &&
+ value["optionalNestedEnum"].asInt() == 123;
+ });
+
+ // String fields.
+ RunValidJsonTest(
+ "StringField",
+ R"({"optionalString": "Hello world!"})",
+ "optional_string: \"Hello world!\"");
+ RunValidJsonTest(
+ "StringFieldUnicode",
+ // Google in Chinese.
+ R"({"optionalString": "谷歌"})",
+ R"(optional_string: "谷歌")");
+ RunValidJsonTest(
+ "StringFieldEscape",
+ R"({"optionalString": "\"\\\/\b\f\n\r\t"})",
+ R"(optional_string: "\"\\/\b\f\n\r\t")");
+ RunValidJsonTest(
+ "StringFieldUnicodeEscape",
+ R"({"optionalString": "\u8C37\u6B4C"})",
+ R"(optional_string: "谷歌")");
+ RunValidJsonTest(
+ "StringFieldUnicodeEscapeWithLowercaseHexLetters",
+ R"({"optionalString": "\u8c37\u6b4c"})",
+ R"(optional_string: "谷歌")");
+ RunValidJsonTest(
+ "StringFieldSurrogatePair",
+ // The character is an emoji: grinning face with smiling eyes. 😁
+ R"({"optionalString": "\uD83D\uDE01"})",
+ R"(optional_string: "\xF0\x9F\x98\x81")");
+
+ // Unicode escapes must start with "\u" (lowercase u).
+ ExpectParseFailureForJson(
+ "StringFieldUppercaseEscapeLetter",
+ R"({"optionalString": "\U8C37\U6b4C"})");
+ ExpectParseFailureForJson(
+ "StringFieldInvalidEscape",
+ R"({"optionalString": "\uXXXX\u6B4C"})");
+ ExpectParseFailureForJson(
+ "StringFieldUnterminatedEscape",
+ R"({"optionalString": "\u8C3"})");
+ ExpectParseFailureForJson(
+ "StringFieldUnpairedHighSurrogate",
+ R"({"optionalString": "\uD800"})");
+ ExpectParseFailureForJson(
+ "StringFieldUnpairedLowSurrogate",
+ R"({"optionalString": "\uDC00"})");
+ ExpectParseFailureForJson(
+ "StringFieldSurrogateInWrongOrder",
+ R"({"optionalString": "\uDE01\uD83D"})");
+ ExpectParseFailureForJson(
+ "StringFieldNotAString",
+ R"({"optionalString": 12345})");
+
+ // Bytes fields.
+ RunValidJsonTest(
+ "BytesField",
+ R"({"optionalBytes": "AQI="})",
+ R"(optional_bytes: "\x01\x02")");
+ ExpectParseFailureForJson(
+ "BytesFieldNoPadding",
+ R"({"optionalBytes": "AQI"})");
+ ExpectParseFailureForJson(
+ "BytesFieldInvalidBase64Characters",
+ R"({"optionalBytes": "-_=="})");
+
+ // Message fields.
+ RunValidJsonTest(
+ "MessageField",
+ R"({"optionalNestedMessage": {"a": 1234}})",
+ "optional_nested_message: {a: 1234}");
+
+ // Oneof fields.
+ ExpectParseFailureForJson(
+ "OneofFieldDuplicate",
+ R"({"oneofUint32": 1, "oneofString": "test"})");
+
+ // Repeated fields.
+ RunValidJsonTest(
+ "PrimitiveRepeatedField",
+ R"({"repeatedInt32": [1, 2, 3, 4]})",
+ "repeated_int32: [1, 2, 3, 4]");
+ RunValidJsonTest(
+ "EnumRepeatedField",
+ R"({"repeatedNestedEnum": ["FOO", "BAR", "BAZ"]})",
+ "repeated_nested_enum: [FOO, BAR, BAZ]");
+ RunValidJsonTest(
+ "StringRepeatedField",
+ R"({"repeatedString": ["Hello", "world"]})",
+ R"(repeated_string: ["Hello", "world"])");
+ RunValidJsonTest(
+ "BytesRepeatedField",
+ R"({"repeatedBytes": ["AAEC", "AQI="]})",
+ R"(repeated_bytes: ["\x00\x01\x02", "\x01\x02"])");
+ RunValidJsonTest(
+ "MessageRepeatedField",
+ R"({"repeatedNestedMessage": [{"a": 1234}, {"a": 5678}]})",
+ "repeated_nested_message: {a: 1234}"
+ "repeated_nested_message: {a: 5678}");
+
+ // Repeated field elements are of incorrect type.
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingIntegersGotBool",
+ R"({"repeatedInt32": [1, false, 3, 4]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingIntegersGotString",
+ R"({"repeatedInt32": [1, 2, "name", 4]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingIntegersGotMessage",
+ R"({"repeatedInt32": [1, 2, 3, {"a": 4}]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingStringsGotInt",
+ R"({"repeatedString": ["1", 2, "3", "4"]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingStringsGotBool",
+ R"({"repeatedString": ["1", "2", false, "4"]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingStringsGotMessage",
+ R"({"repeatedString": ["1", 2, "3", {"a": 4}]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingMessagesGotInt",
+ R"({"repeatedNestedMessage": [{"a": 1}, 2]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingMessagesGotBool",
+ R"({"repeatedNestedMessage": [{"a": 1}, false]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldWrongElementTypeExpectingMessagesGotString",
+ R"({"repeatedNestedMessage": [{"a": 1}, "2"]})");
+ // Trailing comma in the repeated field is not allowed.
+ ExpectParseFailureForJson(
+ "RepeatedFieldTrailingComma",
+ R"({"repeatedInt32": [1, 2, 3, 4,]})");
+
+ // Map fields.
+ RunValidJsonTest(
+ "Int32MapField",
+ R"({"mapInt32Int32": {"1": 2, "3": 4}})",
+ "map_int32_int32: {key: 1 value: 2}"
+ "map_int32_int32: {key: 3 value: 4}");
+ ExpectParseFailureForJson(
+ "Int32MapFieldKeyNotQuoted",
+ R"({"mapInt32Int32": {1: 2, 3: 4}})");
+ RunValidJsonTest(
+ "Uint32MapField",
+ R"({"mapUint32Uint32": {"1": 2, "3": 4}})",
+ "map_uint32_uint32: {key: 1 value: 2}"
+ "map_uint32_uint32: {key: 3 value: 4}");
+ ExpectParseFailureForJson(
+ "Uint32MapFieldKeyNotQuoted",
+ R"({"mapUint32Uint32": {1: 2, 3: 4}})");
+ RunValidJsonTest(
+ "Int64MapField",
+ R"({"mapInt64Int64": {"1": 2, "3": 4}})",
+ "map_int64_int64: {key: 1 value: 2}"
+ "map_int64_int64: {key: 3 value: 4}");
+ ExpectParseFailureForJson(
+ "Int64MapFieldKeyNotQuoted",
+ R"({"mapInt64Int64": {1: 2, 3: 4}})");
+ RunValidJsonTest(
+ "Uint64MapField",
+ R"({"mapUint64Uint64": {"1": 2, "3": 4}})",
+ "map_uint64_uint64: {key: 1 value: 2}"
+ "map_uint64_uint64: {key: 3 value: 4}");
+ ExpectParseFailureForJson(
+ "Uint64MapFieldKeyNotQuoted",
+ R"({"mapUint64Uint64": {1: 2, 3: 4}})");
+ RunValidJsonTest(
+ "BoolMapField",
+ R"({"mapBoolBool": {"true": true, "false": false}})",
+ "map_bool_bool: {key: true value: true}"
+ "map_bool_bool: {key: false value: false}");
+ ExpectParseFailureForJson(
+ "BoolMapFieldKeyNotQuoted",
+ R"({"mapBoolBool": {true: true, false: false}})");
+ RunValidJsonTest(
+ "MessageMapField",
+ R"({
+ "mapStringNestedMessage": {
+ "hello": {"a": 1234},
+ "world": {"a": 5678}
+ }
+ })",
+ R"(
+ map_string_nested_message: {
+ key: "hello"
+ value: {a: 1234}
+ }
+ map_string_nested_message: {
+ key: "world"
+ value: {a: 5678}
+ }
+ )");
+ // Since Map keys are represented as JSON strings, escaping should be allowed.
+ RunValidJsonTest(
+ "Int32MapEscapedKey",
+ R"({"mapInt32Int32": {"\u0031": 2}})",
+ "map_int32_int32: {key: 1 value: 2}");
+ RunValidJsonTest(
+ "Int64MapEscapedKey",
+ R"({"mapInt64Int64": {"\u0031": 2}})",
+ "map_int64_int64: {key: 1 value: 2}");
+ RunValidJsonTest(
+ "BoolMapEscapedKey",
+ R"({"mapBoolBool": {"tr\u0075e": true}})",
+ "map_bool_bool: {key: true value: true}");
+
+ // "null" is accepted for all fields types.
+ RunValidJsonTest(
+ "AllFieldAcceptNull",
+ R"({
+ "optionalInt32": null,
+ "optionalInt64": null,
+ "optionalUint32": null,
+ "optionalUint64": null,
+ "optionalBool": null,
+ "optionalString": null,
+ "optionalBytes": null,
+ "optionalNestedEnum": null,
+ "optionalNestedMessage": null,
+ "repeatedInt32": null,
+ "repeatedInt64": null,
+ "repeatedUint32": null,
+ "repeatedUint64": null,
+ "repeatedBool": null,
+ "repeatedString": null,
+ "repeatedBytes": null,
+ "repeatedNestedEnum": null,
+ "repeatedNestedMessage": null,
+ "mapInt32Int32": null,
+ "mapBoolBool": null,
+ "mapStringNestedMessage": null
+ })",
+ "");
+
+ // Repeated field elements cannot be null.
+ ExpectParseFailureForJson(
+ "RepeatedFieldPrimitiveElementIsNull",
+ R"({"repeatedInt32": [1, null, 2]})");
+ ExpectParseFailureForJson(
+ "RepeatedFieldMessageElementIsNull",
+ R"({"repeatedNestedMessage": [{"a":1}, null, {"a":2}]})");
+ // Map field keys cannot be null.
+ ExpectParseFailureForJson(
+ "MapFieldKeyIsNull",
+ R"({"mapInt32Int32": {null: 1}})");
+ // Map field values cannot be null.
+ ExpectParseFailureForJson(
+ "MapFieldValueIsNull",
+ R"({"mapInt32Int32": {"0": null}})");
+
+ // Wrapper types.
+ RunValidJsonTest(
+ "OptionalBoolWrapper",
+ R"({"optionalBoolWrapper": false})",
+ "optional_bool_wrapper: {value: false}");
+ RunValidJsonTest(
+ "OptionalInt32Wrapper",
+ R"({"optionalInt32Wrapper": 0})",
+ "optional_int32_wrapper: {value: 0}");
+ RunValidJsonTest(
+ "OptionalUint32Wrapper",
+ R"({"optionalUint32Wrapper": 0})",
+ "optional_uint32_wrapper: {value: 0}");
+ RunValidJsonTest(
+ "OptionalInt64Wrapper",
+ R"({"optionalInt64Wrapper": 0})",
+ "optional_int64_wrapper: {value: 0}");
+ RunValidJsonTest(
+ "OptionalUint64Wrapper",
+ R"({"optionalUint64Wrapper": 0})",
+ "optional_uint64_wrapper: {value: 0}");
+ RunValidJsonTest(
+ "OptionalFloatWrapper",
+ R"({"optionalFloatWrapper": 0})",
+ "optional_float_wrapper: {value: 0}");
+ RunValidJsonTest(
+ "OptionalDoubleWrapper",
+ R"({"optionalDoubleWrapper": 0})",
+ "optional_double_wrapper: {value: 0}");
+ RunValidJsonTest(
+ "OptionalStringWrapper",
+ R"({"optionalStringWrapper": ""})",
+ R"(optional_string_wrapper: {value: ""})");
+ RunValidJsonTest(
+ "OptionalBytesWrapper",
+ R"({"optionalBytesWrapper": ""})",
+ R"(optional_bytes_wrapper: {value: ""})");
+ RunValidJsonTest(
+ "OptionalWrapperTypesWithNonDefaultValue",
+ R"({
+ "optionalBoolWrapper": true,
+ "optionalInt32Wrapper": 1,
+ "optionalUint32Wrapper": 1,
+ "optionalInt64Wrapper": "1",
+ "optionalUint64Wrapper": "1",
+ "optionalFloatWrapper": 1,
+ "optionalDoubleWrapper": 1,
+ "optionalStringWrapper": "1",
+ "optionalBytesWrapper": "AQI="
+ })",
+ R"(
+ optional_bool_wrapper: {value: true}
+ optional_int32_wrapper: {value: 1}
+ optional_uint32_wrapper: {value: 1}
+ optional_int64_wrapper: {value: 1}
+ optional_uint64_wrapper: {value: 1}
+ optional_float_wrapper: {value: 1}
+ optional_double_wrapper: {value: 1}
+ optional_string_wrapper: {value: "1"}
+ optional_bytes_wrapper: {value: "\x01\x02"}
+ )");
+ RunValidJsonTest(
+ "RepeatedBoolWrapper",
+ R"({"repeatedBoolWrapper": [true, false]})",
+ "repeated_bool_wrapper: {value: true}"
+ "repeated_bool_wrapper: {value: false}");
+ RunValidJsonTest(
+ "RepeatedInt32Wrapper",
+ R"({"repeatedInt32Wrapper": [0, 1]})",
+ "repeated_int32_wrapper: {value: 0}"
+ "repeated_int32_wrapper: {value: 1}");
+ RunValidJsonTest(
+ "RepeatedUint32Wrapper",
+ R"({"repeatedUint32Wrapper": [0, 1]})",
+ "repeated_uint32_wrapper: {value: 0}"
+ "repeated_uint32_wrapper: {value: 1}");
+ RunValidJsonTest(
+ "RepeatedInt64Wrapper",
+ R"({"repeatedInt64Wrapper": [0, 1]})",
+ "repeated_int64_wrapper: {value: 0}"
+ "repeated_int64_wrapper: {value: 1}");
+ RunValidJsonTest(
+ "RepeatedUint64Wrapper",
+ R"({"repeatedUint64Wrapper": [0, 1]})",
+ "repeated_uint64_wrapper: {value: 0}"
+ "repeated_uint64_wrapper: {value: 1}");
+ RunValidJsonTest(
+ "RepeatedFloatWrapper",
+ R"({"repeatedFloatWrapper": [0, 1]})",
+ "repeated_float_wrapper: {value: 0}"
+ "repeated_float_wrapper: {value: 1}");
+ RunValidJsonTest(
+ "RepeatedDoubleWrapper",
+ R"({"repeatedDoubleWrapper": [0, 1]})",
+ "repeated_double_wrapper: {value: 0}"
+ "repeated_double_wrapper: {value: 1}");
+ RunValidJsonTest(
+ "RepeatedStringWrapper",
+ R"({"repeatedStringWrapper": ["", "AQI="]})",
+ R"(
+ repeated_string_wrapper: {value: ""}
+ repeated_string_wrapper: {value: "AQI="}
+ )");
+ RunValidJsonTest(
+ "RepeatedBytesWrapper",
+ R"({"repeatedBytesWrapper": ["", "AQI="]})",
+ R"(
+ repeated_bytes_wrapper: {value: ""}
+ repeated_bytes_wrapper: {value: "\x01\x02"}
+ )");
+ RunValidJsonTest(
+ "WrapperTypesWithNullValue",
+ R"({
+ "optionalBoolWrapper": null,
+ "optionalInt32Wrapper": null,
+ "optionalUint32Wrapper": null,
+ "optionalInt64Wrapper": null,
+ "optionalUint64Wrapper": null,
+ "optionalFloatWrapper": null,
+ "optionalDoubleWrapper": null,
+ "optionalStringWrapper": null,
+ "optionalBytesWrapper": null,
+ "repeatedBoolWrapper": null,
+ "repeatedInt32Wrapper": null,
+ "repeatedUint32Wrapper": null,
+ "repeatedInt64Wrapper": null,
+ "repeatedUint64Wrapper": null,
+ "repeatedFloatWrapper": null,
+ "repeatedDoubleWrapper": null,
+ "repeatedStringWrapper": null,
+ "repeatedBytesWrapper": null
+ })",
+ "");
+
+ // Duration
+ RunValidJsonTest(
+ "DurationMinValue",
+ R"({"optionalDuration": "-315576000000.999999999s"})",
+ "optional_duration: {seconds: -315576000000 nanos: -999999999}");
+ RunValidJsonTest(
+ "DurationMaxValue",
+ R"({"optionalDuration": "315576000000.999999999s"})",
+ "optional_duration: {seconds: 315576000000 nanos: 999999999}");
+ RunValidJsonTest(
+ "DurationRepeatedValue",
+ R"({"repeatedDuration": ["1.5s", "-1.5s"]})",
+ "repeated_duration: {seconds: 1 nanos: 500000000}"
+ "repeated_duration: {seconds: -1 nanos: -500000000}");
+
+ ExpectParseFailureForJson(
+ "DurationMissingS",
+ R"({"optionalDuration": "1"})");
+ ExpectParseFailureForJson(
+ "DurationJsonInputTooSmall",
+ R"({"optionalDuration": "-315576000001.000000000s"})");
+ ExpectParseFailureForJson(
+ "DurationJsonInputTooLarge",
+ R"({"optionalDuration": "315576000001.000000000s"})");
+ ExpectSerializeFailureForJson(
+ "DurationProtoInputTooSmall",
+ "optional_duration: {seconds: -315576000001 nanos: 0}");
+ ExpectSerializeFailureForJson(
+ "DurationProtoInputTooLarge",
+ "optional_duration: {seconds: 315576000001 nanos: 0}");
+
+ RunValidJsonTestWithValidator(
+ "DurationHasZeroFractionalDigit",
+ R"({"optionalDuration": "1.000000000s"})",
+ [](const Json::Value& value) {
+ return value["optionalDuration"].asString() == "1s";
+ });
+ RunValidJsonTestWithValidator(
+ "DurationHas3FractionalDigits",
+ R"({"optionalDuration": "1.010000000s"})",
+ [](const Json::Value& value) {
+ return value["optionalDuration"].asString() == "1.010s";
+ });
+ RunValidJsonTestWithValidator(
+ "DurationHas6FractionalDigits",
+ R"({"optionalDuration": "1.000010000s"})",
+ [](const Json::Value& value) {
+ return value["optionalDuration"].asString() == "1.000010s";
+ });
+ RunValidJsonTestWithValidator(
+ "DurationHas9FractionalDigits",
+ R"({"optionalDuration": "1.000000010s"})",
+ [](const Json::Value& value) {
+ return value["optionalDuration"].asString() == "1.000000010s";
+ });
+
+ // Timestamp
+ RunValidJsonTest(
+ "TimestampMinValue",
+ R"({"optionalTimestamp": "0001-01-01T00:00:00Z"})",
+ "optional_timestamp: {seconds: -62135596800}");
+ RunValidJsonTest(
+ "TimestampMaxValue",
+ R"({"optionalTimestamp": "9999-12-31T23:59:59.999999999Z"})",
+ "optional_timestamp: {seconds: 253402300799 nanos: 999999999}");
+ RunValidJsonTest(
+ "TimestampRepeatedValue",
+ R"({
+ "repeatedTimestamp": [
+ "0001-01-01T00:00:00Z",
+ "9999-12-31T23:59:59.999999999Z"
+ ]
+ })",
+ "repeated_timestamp: {seconds: -62135596800}"
+ "repeated_timestamp: {seconds: 253402300799 nanos: 999999999}");
+ RunValidJsonTest(
+ "TimestampWithPositiveOffset",
+ R"({"optionalTimestamp": "1970-01-01T08:00:00+08:00"})",
+ "optional_timestamp: {seconds: 0}");
+ RunValidJsonTest(
+ "TimestampWithNegativeOffset",
+ R"({"optionalTimestamp": "1969-12-31T16:00:00-08:00"})",
+ "optional_timestamp: {seconds: 0}");
+
+ ExpectParseFailureForJson(
+ "TimestampJsonInputTooSmall",
+ R"({"optionalTimestamp": "0000-01-01T00:00:00Z"})");
+ ExpectParseFailureForJson(
+ "TimestampJsonInputTooLarge",
+ R"({"optionalTimestamp": "10000-01-01T00:00:00Z"})");
+ ExpectParseFailureForJson(
+ "TimestampJsonInputMissingZ",
+ R"({"optionalTimestamp": "0001-01-01T00:00:00"})");
+ ExpectParseFailureForJson(
+ "TimestampJsonInputMissingT",
+ R"({"optionalTimestamp": "0001-01-01 00:00:00Z"})");
+ ExpectParseFailureForJson(
+ "TimestampJsonInputLowercaseZ",
+ R"({"optionalTimestamp": "0001-01-01T00:00:00z"})");
+ ExpectParseFailureForJson(
+ "TimestampJsonInputLowercaseT",
+ R"({"optionalTimestamp": "0001-01-01t00:00:00Z"})");
+ ExpectSerializeFailureForJson(
+ "TimestampProtoInputTooSmall",
+ "optional_timestamp: {seconds: -62135596801}");
+ ExpectSerializeFailureForJson(
+ "TimestampProtoInputTooLarge",
+ "optional_timestamp: {seconds: 253402300800}");
+ RunValidJsonTestWithValidator(
+ "TimestampZeroNormalized",
+ R"({"optionalTimestamp": "1969-12-31T16:00:00-08:00"})",
+ [](const Json::Value& value) {
+ return value["optionalTimestamp"].asString() ==
+ "1970-01-01T00:00:00Z";
+ });
+ RunValidJsonTestWithValidator(
+ "TimestampHasZeroFractionalDigit",
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.000000000Z"})",
+ [](const Json::Value& value) {
+ return value["optionalTimestamp"].asString() ==
+ "1970-01-01T00:00:00Z";
+ });
+ RunValidJsonTestWithValidator(
+ "TimestampHas3FractionalDigits",
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.010000000Z"})",
+ [](const Json::Value& value) {
+ return value["optionalTimestamp"].asString() ==
+ "1970-01-01T00:00:00.010Z";
+ });
+ RunValidJsonTestWithValidator(
+ "TimestampHas6FractionalDigits",
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.000010000Z"})",
+ [](const Json::Value& value) {
+ return value["optionalTimestamp"].asString() ==
+ "1970-01-01T00:00:00.000010Z";
+ });
+ RunValidJsonTestWithValidator(
+ "TimestampHas9FractionalDigits",
+ R"({"optionalTimestamp": "1970-01-01T00:00:00.000000010Z"})",
+ [](const Json::Value& value) {
+ return value["optionalTimestamp"].asString() ==
+ "1970-01-01T00:00:00.000000010Z";
+ });
+
+ // FieldMask
+ RunValidJsonTest(
+ "FieldMask",
+ R"({"optionalFieldMask": "foo,barBaz"})",
+ R"(optional_field_mask: {paths: "foo" paths: "bar_baz"})");
+ ExpectParseFailureForJson(
+ "FieldMaskInvalidCharacter",
+ R"({"optionalFieldMask": "foo,bar_bar"})");
+ ExpectSerializeFailureForJson(
+ "FieldMaskPathsDontRoundTrip",
+ R"(optional_field_mask: {paths: "fooBar"})");
+ ExpectSerializeFailureForJson(
+ "FieldMaskNumbersDontRoundTrip",
+ R"(optional_field_mask: {paths: "foo_3_bar"})");
+ ExpectSerializeFailureForJson(
+ "FieldMaskTooManyUnderscore",
+ R"(optional_field_mask: {paths: "foo__bar"})");
+
+ // Struct
+ RunValidJsonTest(
+ "Struct",
+ R"({
+ "optionalStruct": {
+ "nullValue": null,
+ "intValue": 1234,
+ "boolValue": true,
+ "doubleValue": 1234.5678,
+ "stringValue": "Hello world!",
+ "listValue": [1234, "5678"],
+ "objectValue": {
+ "value": 0
+ }
+ }
+ })",
+ R"(
+ optional_struct: {
+ fields: {
+ key: "nullValue"
+ value: {null_value: NULL_VALUE}
+ }
+ fields: {
+ key: "intValue"
+ value: {number_value: 1234}
+ }
+ fields: {
+ key: "boolValue"
+ value: {bool_value: true}
+ }
+ fields: {
+ key: "doubleValue"
+ value: {number_value: 1234.5678}
+ }
+ fields: {
+ key: "stringValue"
+ value: {string_value: "Hello world!"}
+ }
+ fields: {
+ key: "listValue"
+ value: {
+ list_value: {
+ values: {
+ number_value: 1234
+ }
+ values: {
+ string_value: "5678"
+ }
+ }
+ }
+ }
+ fields: {
+ key: "objectValue"
+ value: {
+ struct_value: {
+ fields: {
+ key: "value"
+ value: {
+ number_value: 0
+ }
+ }
+ }
+ }
+ }
+ }
+ )");
+ // Value
+ RunValidJsonTest(
+ "ValueAcceptInteger",
+ R"({"optionalValue": 1})",
+ "optional_value: { number_value: 1}");
+ RunValidJsonTest(
+ "ValueAcceptFloat",
+ R"({"optionalValue": 1.5})",
+ "optional_value: { number_value: 1.5}");
+ RunValidJsonTest(
+ "ValueAcceptBool",
+ R"({"optionalValue": false})",
+ "optional_value: { bool_value: false}");
+ RunValidJsonTest(
+ "ValueAcceptNull",
+ R"({"optionalValue": null})",
+ "optional_value: { null_value: NULL_VALUE}");
+ RunValidJsonTest(
+ "ValueAcceptString",
+ R"({"optionalValue": "hello"})",
+ R"(optional_value: { string_value: "hello"})");
+ RunValidJsonTest(
+ "ValueAcceptList",
+ R"({"optionalValue": [0, "hello"]})",
+ R"(
+ optional_value: {
+ list_value: {
+ values: {
+ number_value: 0
+ }
+ values: {
+ string_value: "hello"
+ }
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "ValueAcceptObject",
+ R"({"optionalValue": {"value": 1}})",
+ R"(
+ optional_value: {
+ struct_value: {
+ fields: {
+ key: "value"
+ value: {
+ number_value: 1
+ }
+ }
+ }
+ }
+ )");
+
+ // Any
+ RunValidJsonTest(
+ "Any",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/conformance.TestAllTypes",
+ "optionalInt32": 12345
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/conformance.TestAllTypes] {
+ optional_int32: 12345
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyNested",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Any",
+ "value": {
+ "@type": "type.googleapis.com/conformance.TestAllTypes",
+ "optionalInt32": 12345
+ }
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Any] {
+ [type.googleapis.com/conformance.TestAllTypes] {
+ optional_int32: 12345
+ }
+ }
+ }
+ )");
+ // The special "@type" tag is not required to appear first.
+ RunValidJsonTest(
+ "AnyUnorderedTypeTag",
+ R"({
+ "optionalAny": {
+ "optionalInt32": 12345,
+ "@type": "type.googleapis.com/conformance.TestAllTypes"
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/conformance.TestAllTypes] {
+ optional_int32: 12345
+ }
+ }
+ )");
+ // Well-known types in Any.
+ RunValidJsonTest(
+ "AnyWithInt32ValueWrapper",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Int32Value",
+ "value": 12345
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Int32Value] {
+ value: 12345
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyWithDuration",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Duration",
+ "value": "1.5s"
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Duration] {
+ seconds: 1
+ nanos: 500000000
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyWithTimestamp",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Timestamp",
+ "value": "1970-01-01T00:00:00Z"
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Timestamp] {
+ seconds: 0
+ nanos: 0
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyWithFieldMask",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.FieldMask",
+ "value": "foo,barBaz"
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.FieldMask] {
+ paths: ["foo", "bar_baz"]
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyWithStruct",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Struct",
+ "value": {
+ "foo": 1
+ }
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Struct] {
+ fields: {
+ key: "foo"
+ value: {
+ number_value: 1
+ }
+ }
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyWithValueForJsonObject",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Value",
+ "value": {
+ "foo": 1
+ }
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Value] {
+ struct_value: {
+ fields: {
+ key: "foo"
+ value: {
+ number_value: 1
+ }
+ }
+ }
+ }
+ }
+ )");
+ RunValidJsonTest(
+ "AnyWithValueForInteger",
+ R"({
+ "optionalAny": {
+ "@type": "type.googleapis.com/google.protobuf.Value",
+ "value": 1
+ }
+ })",
+ R"(
+ optional_any: {
+ [type.googleapis.com/google.protobuf.Value] {
+ number_value: 1
+ }
+ }
+ )");
+
+ bool ok = true;
+ if (!CheckSetEmpty(expected_to_fail_,
+ "These tests were listed in the failure list, but they "
+ "don't exist. Remove them from the failure list")) {
+ ok = false;
+ }
+ if (!CheckSetEmpty(unexpected_failing_tests_,
+ "These tests failed. If they can't be fixed right now, "
+ "you can add them to the failure list so the overall "
+ "suite can succeed")) {
+ ok = false;
+ }
- CheckSetEmpty(unexpected_succeeding_tests_,
- "These tests succeeded, even though they were listed in "
- "the failure list. Remove them from the failure list");
+ // Sometimes the testee may be fixed before we update the failure list (e.g.,
+ // the testee is from a different component). We warn about this case but
+ // don't consider it an overall test failure.
+ CheckSetEmpty(unexpected_succeeding_tests_,
+ "These tests succeeded, even though they were listed in "
+ "the failure list. Remove them from the failure list");
- CheckSetEmpty(skipped_,
- "These tests were skipped (probably because support for some "
- "features is not implemented)");
+ if (verbose_) {
+ CheckSetEmpty(skipped_,
+ "These tests were skipped (probably because support for some "
+ "features is not implemented)");
+ }
StringAppendF(&output_,
"CONFORMANCE SUITE %s: %d successes, %d skipped, "
diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h
index 9e6cdaee..75fc97bc 100644
--- a/conformance/conformance_test.h
+++ b/conformance/conformance_test.h
@@ -38,14 +38,18 @@
#ifndef CONFORMANCE_CONFORMANCE_TEST_H
#define CONFORMANCE_CONFORMANCE_TEST_H
+#include <functional>
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/wire_format_lite.h>
+#include "third_party/jsoncpp/json.h"
+
namespace conformance {
class ConformanceRequest;
class ConformanceResponse;
+class TestAllTypes;
} // namespace conformance
namespace google {
@@ -53,6 +57,8 @@ namespace protobuf {
class ConformanceTestRunner {
public:
+ virtual ~ConformanceTestRunner() {}
+
// Call to run a single conformance test.
//
// "input" is a serialized conformance.ConformanceRequest.
@@ -60,7 +66,9 @@ class ConformanceTestRunner {
//
// If there is any error in running the test itself, set "runtime_error" in
// the response.
- virtual void RunTest(const std::string& input, std::string* output) = 0;
+ virtual void RunTest(const std::string& test_name,
+ const std::string& input,
+ std::string* output) = 0;
};
// Class representing the test suite itself. To run it, implement your own
@@ -118,6 +126,18 @@ class ConformanceTestSuite {
conformance::WireFormat requested_output);
void RunValidJsonTest(const string& test_name, const string& input_json,
const string& equivalent_text_format);
+ void RunValidJsonTestWithProtobufInput(const string& test_name,
+ const conformance::TestAllTypes& input,
+ const string& equivalent_text_format);
+
+ typedef std::function<bool(const Json::Value&)> Validator;
+ void RunValidJsonTestWithValidator(const string& test_name,
+ const string& input_json,
+ const Validator& validator);
+ void ExpectParseFailureForJson(const string& test_name,
+ const string& input_json);
+ void ExpectSerializeFailureForJson(const string& test_name,
+ const string& text_format);
void ExpectParseFailureForProto(const std::string& proto,
const std::string& test_name);
void ExpectHardParseFailureForProto(const std::string& proto,
diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc
index 780e1c44..376a60b9 100644
--- a/conformance/conformance_test_runner.cc
+++ b/conformance/conformance_test_runner.cc
@@ -53,17 +53,23 @@
// 3. testee sends 4-byte length M (little endian)
// 4. testee sends M bytes representing a ConformanceResponse proto
+#include <algorithm>
#include <errno.h>
-#include <unistd.h>
#include <fstream>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
#include <vector>
+#include <google/protobuf/stubs/stringprintf.h>
+
#include "conformance.pb.h"
#include "conformance_test.h"
using conformance::ConformanceRequest;
using conformance::ConformanceResponse;
using google::protobuf::internal::scoped_array;
+using google::protobuf::StringAppendF;
using std::string;
using std::vector;
@@ -80,17 +86,47 @@ using std::vector;
class ForkPipeRunner : public google::protobuf::ConformanceTestRunner {
public:
ForkPipeRunner(const std::string &executable)
- : executable_(executable), running_(false) {}
+ : child_pid_(-1), executable_(executable) {}
- void RunTest(const std::string& request, std::string* response) {
- if (!running_) {
+ virtual ~ForkPipeRunner() {}
+
+ void RunTest(const std::string& test_name,
+ const std::string& request,
+ std::string* response) {
+ if (child_pid_ < 0) {
SpawnTestProgram();
}
+ current_test_name_ = test_name;
+
uint32_t len = request.size();
CheckedWrite(write_fd_, &len, sizeof(uint32_t));
CheckedWrite(write_fd_, request.c_str(), request.size());
- CheckedRead(read_fd_, &len, sizeof(uint32_t));
+
+ if (!TryRead(read_fd_, &len, sizeof(uint32_t))) {
+ // We failed to read from the child, assume a crash and try to reap.
+ GOOGLE_LOG(INFO) << "Trying to reap child, pid=" << child_pid_;
+
+ int status;
+ waitpid(child_pid_, &status, WEXITED);
+
+ string error_msg;
+ if (WIFEXITED(status)) {
+ StringAppendF(&error_msg,
+ "child exited, status=%d", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ StringAppendF(&error_msg,
+ "child killed by signal %d", WTERMSIG(status));
+ }
+ GOOGLE_LOG(INFO) << error_msg;
+ child_pid_ = -1;
+
+ conformance::ConformanceResponse response_obj;
+ response_obj.set_runtime_error(error_msg);
+ response_obj.SerializeToString(response);
+ return;
+ }
+
response->resize(len);
CheckedRead(read_fd_, (void*)response->c_str(), len);
}
@@ -134,7 +170,7 @@ class ForkPipeRunner : public google::protobuf::ConformanceTestRunner {
CHECK_SYSCALL(close(fromproc_pipe_fd[1]));
write_fd_ = toproc_pipe_fd[1];
read_fd_ = fromproc_pipe_fd[0];
- running_ = true;
+ child_pid_ = pid;
} else {
// Child.
CHECK_SYSCALL(close(STDIN_FILENO));
@@ -158,30 +194,48 @@ class ForkPipeRunner : public google::protobuf::ConformanceTestRunner {
void CheckedWrite(int fd, const void *buf, size_t len) {
if (write(fd, buf, len) != len) {
- GOOGLE_LOG(FATAL) << "Error writing to test program: " << strerror(errno);
+ GOOGLE_LOG(FATAL) << current_test_name_
+ << ": error writing to test program: "
+ << strerror(errno);
}
}
- void CheckedRead(int fd, void *buf, size_t len) {
+ bool TryRead(int fd, void *buf, size_t len) {
size_t ofs = 0;
while (len > 0) {
ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
if (bytes_read == 0) {
- GOOGLE_LOG(FATAL) << "Unexpected EOF from test program";
+ GOOGLE_LOG(ERROR) << current_test_name_
+ << ": unexpected EOF from test program";
+ return false;
} else if (bytes_read < 0) {
- GOOGLE_LOG(FATAL) << "Error reading from test program: " << strerror(errno);
+ GOOGLE_LOG(ERROR) << current_test_name_
+ << ": error reading from test program: "
+ << strerror(errno);
+ return false;
}
len -= bytes_read;
ofs += bytes_read;
}
+
+ return true;
+ }
+
+ void CheckedRead(int fd, void *buf, size_t len) {
+ if (!TryRead(fd, buf, len)) {
+ GOOGLE_LOG(FATAL) << current_test_name_
+ << ": error reading from test program: "
+ << strerror(errno);
+ }
}
int write_fd_;
int read_fd_;
- bool running_;
+ pid_t child_pid_;
std::string executable_;
+ std::string current_test_name_;
};
void UsageError() {
@@ -202,6 +256,12 @@ void UsageError() {
void ParseFailureList(const char *filename, vector<string>* failure_list) {
std::ifstream infile(filename);
+
+ if (!infile.is_open()) {
+ fprintf(stderr, "Couldn't open failure list file: %s\n", filename);
+ exit(1);
+ }
+
for (string line; getline(infile, line);) {
// Remove whitespace.
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
@@ -217,16 +277,15 @@ void ParseFailureList(const char *filename, vector<string>* failure_list) {
}
int main(int argc, char *argv[]) {
- int arg = 1;
char *program;
google::protobuf::ConformanceTestSuite suite;
+ vector<string> failure_list;
+
for (int arg = 1; arg < argc; ++arg) {
if (strcmp(argv[arg], "--failure_list") == 0) {
if (++arg == argc) UsageError();
- vector<string> failure_list;
ParseFailureList(argv[arg], &failure_list);
- suite.SetFailureList(failure_list);
} else if (strcmp(argv[arg], "--verbose") == 0) {
suite.SetVerbose(true);
} else if (argv[arg][0] == '-') {
@@ -241,6 +300,7 @@ int main(int argc, char *argv[]) {
}
}
+ suite.SetFailureList(failure_list);
ForkPipeRunner runner(program);
std::string output;
diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt
index 53d90c99..2ddf831c 100644
--- a/conformance/failure_list_cpp.txt
+++ b/conformance/failure_list_cpp.txt
@@ -7,6 +7,89 @@
# TODO(haberman): insert links to corresponding bugs tracking the issue.
# Should we use GitHub issues or the Google-internal bug tracker?
+FieldMaskNumbersDontRoundTrip.JsonOutput
+FieldMaskPathsDontRoundTrip.JsonOutput
+FieldMaskTooManyUnderscore.JsonOutput
+JsonInput.AnyUnorderedTypeTag.JsonOutput
+JsonInput.AnyUnorderedTypeTag.ProtobufOutput
+JsonInput.AnyWithValueForInteger.JsonOutput
+JsonInput.AnyWithValueForInteger.ProtobufOutput
+JsonInput.AnyWithValueForJsonObject.JsonOutput
+JsonInput.AnyWithValueForJsonObject.ProtobufOutput
+JsonInput.BoolFieldDoubleQuotedFalse
+JsonInput.BoolFieldDoubleQuotedTrue
+JsonInput.BoolFieldIntegerOne
+JsonInput.BoolFieldIntegerZero
+JsonInput.BytesFieldInvalidBase64Characters
+JsonInput.BytesFieldNoPadding
+JsonInput.DoubleFieldTooSmall
+JsonInput.DurationHasZeroFractionalDigit.Validator
+JsonInput.DurationJsonInputTooLarge
+JsonInput.DurationJsonInputTooSmall
+JsonInput.DurationMissingS
+JsonInput.EnumFieldUnknownValue.Validator
+JsonInput.FieldMaskInvalidCharacter
+JsonInput.FieldNameDuplicate
+JsonInput.FieldNameDuplicateDifferentCasing1
+JsonInput.FieldNameDuplicateDifferentCasing2
+JsonInput.FieldNameInLowerCamelCase.Validator
+JsonInput.FieldNameInSnakeCase.JsonOutput
+JsonInput.FieldNameInSnakeCase.ProtobufOutput
+JsonInput.FieldNameNotQuoted
+JsonInput.FloatFieldTooLarge
+JsonInput.FloatFieldTooSmall
+JsonInput.Int32FieldLeadingSpace
+JsonInput.Int32FieldLeadingZero
+JsonInput.Int32FieldMinFloatValue.JsonOutput
+JsonInput.Int32FieldMinFloatValue.ProtobufOutput
+JsonInput.Int32FieldMinValue.JsonOutput
+JsonInput.Int32FieldMinValue.ProtobufOutput
+JsonInput.Int32FieldNegativeWithLeadingZero
+JsonInput.Int32FieldNotInteger
+JsonInput.Int32FieldNotNumber
+JsonInput.Int32FieldTooLarge
+JsonInput.Int32FieldTooSmall
+JsonInput.Int32FieldTrailingSpace
+JsonInput.Int64FieldNotInteger
+JsonInput.Int64FieldNotNumber
+JsonInput.Int64FieldTooLarge
+JsonInput.Int64FieldTooSmall
+JsonInput.MapFieldValueIsNull
+JsonInput.OneofFieldDuplicate
+JsonInput.RepeatedFieldMessageElementIsNull
+JsonInput.RepeatedFieldPrimitiveElementIsNull
+JsonInput.RepeatedFieldTrailingComma
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotMessage
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotString
+JsonInput.RepeatedFieldWrongElementTypeExpectingMessagesGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingMessagesGotInt
+JsonInput.RepeatedFieldWrongElementTypeExpectingMessagesGotString
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotInt
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotMessage
+JsonInput.StringFieldNotAString
+JsonInput.StringFieldSurrogateInWrongOrder
+JsonInput.StringFieldSurrogatePair.JsonOutput
+JsonInput.StringFieldSurrogatePair.ProtobufOutput
+JsonInput.StringFieldUnpairedHighSurrogate
+JsonInput.StringFieldUnpairedLowSurrogate
+JsonInput.StringFieldUppercaseEscapeLetter
+JsonInput.TimestampJsonInputLowercaseT
+JsonInput.TimestampJsonInputLowercaseZ
+JsonInput.TimestampJsonInputMissingT
+JsonInput.TimestampJsonInputMissingZ
+JsonInput.TimestampJsonInputTooLarge
+JsonInput.TimestampJsonInputTooSmall
+JsonInput.TrailingCommaInAnObject
+JsonInput.Uint32FieldNotInteger
+JsonInput.Uint32FieldNotNumber
+JsonInput.Uint32FieldTooLarge
+JsonInput.Uint64FieldNotInteger
+JsonInput.Uint64FieldNotNumber
+JsonInput.Uint64FieldTooLarge
+JsonInput.WrapperTypesWithNullValue.JsonOutput
+JsonInput.WrapperTypesWithNullValue.ProtobufOutput
ProtobufInput.PrematureEofBeforeKnownRepeatedValue.MESSAGE
ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
@@ -19,3 +102,5 @@ ProtobufInput.PrematureEofInPackedField.SINT64
ProtobufInput.PrematureEofInPackedField.UINT32
ProtobufInput.PrematureEofInPackedField.UINT64
ProtobufInput.PrematureEofInsideKnownRepeatedValue.MESSAGE
+TimestampProtoInputTooLarge.JsonOutput
+TimestampProtoInputTooSmall.JsonOutput
diff --git a/conformance/failure_list_csharp.txt b/conformance/failure_list_csharp.txt
index e69de29b..a46cee47 100644
--- a/conformance/failure_list_csharp.txt
+++ b/conformance/failure_list_csharp.txt
@@ -0,0 +1,16 @@
+JsonInput.AnyWithValueForInteger.JsonOutput
+JsonInput.AnyWithValueForJsonObject.JsonOutput
+JsonInput.FieldNameInLowerCamelCase.Validator
+JsonInput.FieldNameInSnakeCase.JsonOutput
+JsonInput.FieldNameInSnakeCase.ProtobufOutput
+JsonInput.FieldNameWithMixedCases.JsonOutput
+JsonInput.FieldNameWithMixedCases.ProtobufOutput
+JsonInput.FieldNameWithMixedCases.Validator
+JsonInput.Int32FieldMinFloatValue.JsonOutput
+JsonInput.Int32FieldMinValue.JsonOutput
+JsonInput.Int64FieldMaxValueNotQuoted.JsonOutput
+JsonInput.Int64FieldMaxValueNotQuoted.ProtobufOutput
+JsonInput.OriginalProtoFieldName.JsonOutput
+JsonInput.StringFieldSurrogatePair.JsonOutput
+JsonInput.Uint64FieldMaxValueNotQuoted.JsonOutput
+JsonInput.Uint64FieldMaxValueNotQuoted.ProtobufOutput
diff --git a/conformance/failure_list_java.txt b/conformance/failure_list_java.txt
new file mode 100644
index 00000000..552c0cc9
--- /dev/null
+++ b/conformance/failure_list_java.txt
@@ -0,0 +1,49 @@
+# This is the list of conformance tests that are known to fail for the Java
+# implementation right now. These should be fixed.
+#
+# By listing them here we can keep tabs on which ones are failing and be sure
+# that we don't introduce regressions in other tests.
+
+FieldMaskNumbersDontRoundTrip.JsonOutput
+FieldMaskPathsDontRoundTrip.JsonOutput
+FieldMaskTooManyUnderscore.JsonOutput
+JsonInput.AnyWithFieldMask.ProtobufOutput
+JsonInput.AnyWithValueForInteger.JsonOutput
+JsonInput.AnyWithValueForJsonObject.JsonOutput
+JsonInput.BoolFieldAllCapitalFalse
+JsonInput.BoolFieldAllCapitalTrue
+JsonInput.BoolFieldCamelCaseFalse
+JsonInput.BoolFieldCamelCaseTrue
+JsonInput.BoolFieldDoubleQuotedFalse
+JsonInput.BoolFieldDoubleQuotedTrue
+JsonInput.BoolMapFieldKeyNotQuoted
+JsonInput.DoubleFieldInfinityNotQuoted
+JsonInput.DoubleFieldNanNotQuoted
+JsonInput.DoubleFieldNegativeInfinityNotQuoted
+JsonInput.EnumFieldNotQuoted
+JsonInput.FieldMask.ProtobufOutput
+JsonInput.FieldMaskInvalidCharacter
+JsonInput.FieldNameDuplicate
+JsonInput.FieldNameInSnakeCase.JsonOutput
+JsonInput.FieldNameNotQuoted
+JsonInput.FloatFieldInfinityNotQuoted
+JsonInput.FloatFieldNanNotQuoted
+JsonInput.FloatFieldNegativeInfinityNotQuoted
+JsonInput.Int32FieldLeadingZero
+JsonInput.Int32FieldMinFloatValue.JsonOutput
+JsonInput.Int32FieldMinValue.JsonOutput
+JsonInput.Int32FieldNegativeWithLeadingZero
+JsonInput.Int32FieldPlusSign
+JsonInput.Int32MapFieldKeyNotQuoted
+JsonInput.Int64MapFieldKeyNotQuoted
+JsonInput.JsonWithComments
+JsonInput.OriginalProtoFieldName.JsonOutput
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotInt
+JsonInput.StringFieldNotAString
+JsonInput.StringFieldSurrogateInWrongOrder
+JsonInput.StringFieldUnpairedHighSurrogate
+JsonInput.StringFieldUnpairedLowSurrogate
+JsonInput.StringFieldUppercaseEscapeLetter
+JsonInput.Uint32MapFieldKeyNotQuoted
+JsonInput.Uint64MapFieldKeyNotQuoted
diff --git a/conformance/failure_list_objc.txt b/conformance/failure_list_objc.txt
new file mode 100644
index 00000000..5dac3501
--- /dev/null
+++ b/conformance/failure_list_objc.txt
@@ -0,0 +1,4 @@
+# No tests currently failing.
+#
+# json input or output tests are skipped (in conformance_objc.m) as mobile
+# platforms don't support json wire format to avoid code bloat.
diff --git a/conformance/failure_list_python-post26.txt b/conformance/failure_list_python-post26.txt
new file mode 100644
index 00000000..19d99b04
--- /dev/null
+++ b/conformance/failure_list_python-post26.txt
@@ -0,0 +1,2 @@
+JsonInput.StringFieldSurrogateInWrongOrder
+JsonInput.StringFieldUnpairedHighSurrogate
diff --git a/conformance/failure_list_python.txt b/conformance/failure_list_python.txt
new file mode 100644
index 00000000..d2e52637
--- /dev/null
+++ b/conformance/failure_list_python.txt
@@ -0,0 +1,85 @@
+DurationProtoInputTooLarge.JsonOutput
+DurationProtoInputTooSmall.JsonOutput
+FieldMaskNumbersDontRoundTrip.JsonOutput
+FieldMaskPathsDontRoundTrip.JsonOutput
+FieldMaskTooManyUnderscore.JsonOutput
+JsonInput.Any.JsonOutput
+JsonInput.Any.ProtobufOutput
+JsonInput.AnyNested.JsonOutput
+JsonInput.AnyNested.ProtobufOutput
+JsonInput.AnyUnorderedTypeTag.JsonOutput
+JsonInput.AnyUnorderedTypeTag.ProtobufOutput
+JsonInput.AnyWithDuration.JsonOutput
+JsonInput.AnyWithDuration.ProtobufOutput
+JsonInput.AnyWithFieldMask.JsonOutput
+JsonInput.AnyWithFieldMask.ProtobufOutput
+JsonInput.AnyWithInt32ValueWrapper.JsonOutput
+JsonInput.AnyWithInt32ValueWrapper.ProtobufOutput
+JsonInput.AnyWithStruct.JsonOutput
+JsonInput.AnyWithStruct.ProtobufOutput
+JsonInput.AnyWithTimestamp.JsonOutput
+JsonInput.AnyWithTimestamp.ProtobufOutput
+JsonInput.AnyWithValueForInteger.JsonOutput
+JsonInput.AnyWithValueForInteger.ProtobufOutput
+JsonInput.AnyWithValueForJsonObject.JsonOutput
+JsonInput.AnyWithValueForJsonObject.ProtobufOutput
+JsonInput.BytesFieldInvalidBase64Characters
+JsonInput.DoubleFieldInfinityNotQuoted
+JsonInput.DoubleFieldNanNotQuoted
+JsonInput.DoubleFieldNegativeInfinityNotQuoted
+JsonInput.DoubleFieldTooSmall
+JsonInput.DurationJsonInputTooLarge
+JsonInput.DurationJsonInputTooSmall
+JsonInput.DurationMissingS
+JsonInput.EnumFieldNumericValueNonZero.JsonOutput
+JsonInput.EnumFieldNumericValueNonZero.ProtobufOutput
+JsonInput.EnumFieldNumericValueZero.JsonOutput
+JsonInput.EnumFieldNumericValueZero.ProtobufOutput
+JsonInput.EnumFieldUnknownValue.Validator
+JsonInput.FieldMask.ProtobufOutput
+JsonInput.FieldMaskInvalidCharacter
+JsonInput.FieldNameInLowerCamelCase.Validator
+JsonInput.FieldNameInSnakeCase.JsonOutput
+JsonInput.FieldNameInSnakeCase.ProtobufOutput
+JsonInput.FloatFieldInfinityNotQuoted
+JsonInput.FloatFieldNanNotQuoted
+JsonInput.FloatFieldNegativeInfinityNotQuoted
+JsonInput.FloatFieldTooLarge
+JsonInput.FloatFieldTooSmall
+JsonInput.Int32FieldExponentialFormat.JsonOutput
+JsonInput.Int32FieldExponentialFormat.ProtobufOutput
+JsonInput.Int32FieldFloatTrailingZero.JsonOutput
+JsonInput.Int32FieldFloatTrailingZero.ProtobufOutput
+JsonInput.Int32FieldMaxFloatValue.JsonOutput
+JsonInput.Int32FieldMaxFloatValue.ProtobufOutput
+JsonInput.Int32FieldMinFloatValue.JsonOutput
+JsonInput.Int32FieldMinFloatValue.ProtobufOutput
+JsonInput.Int32FieldMinValue.JsonOutput
+JsonInput.OriginalProtoFieldName.JsonOutput
+JsonInput.OriginalProtoFieldName.ProtobufOutput
+JsonInput.RepeatedFieldMessageElementIsNull
+JsonInput.RepeatedFieldPrimitiveElementIsNull
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotBool
+JsonInput.StringFieldSurrogatePair.JsonOutput
+JsonInput.StringFieldUnpairedLowSurrogate
+JsonInput.Struct.JsonOutput
+JsonInput.Struct.ProtobufOutput
+JsonInput.TimestampJsonInputLowercaseT
+JsonInput.Uint32FieldMaxFloatValue.JsonOutput
+JsonInput.Uint32FieldMaxFloatValue.ProtobufOutput
+JsonInput.ValueAcceptBool.JsonOutput
+JsonInput.ValueAcceptBool.ProtobufOutput
+JsonInput.ValueAcceptFloat.JsonOutput
+JsonInput.ValueAcceptFloat.ProtobufOutput
+JsonInput.ValueAcceptInteger.JsonOutput
+JsonInput.ValueAcceptInteger.ProtobufOutput
+JsonInput.ValueAcceptList.JsonOutput
+JsonInput.ValueAcceptList.ProtobufOutput
+JsonInput.ValueAcceptNull.JsonOutput
+JsonInput.ValueAcceptNull.ProtobufOutput
+JsonInput.ValueAcceptObject.JsonOutput
+JsonInput.ValueAcceptObject.ProtobufOutput
+JsonInput.ValueAcceptString.JsonOutput
+JsonInput.ValueAcceptString.ProtobufOutput
+TimestampProtoInputTooLarge.JsonOutput
+TimestampProtoInputTooSmall.JsonOutput
diff --git a/conformance/failure_list_python_cpp.txt b/conformance/failure_list_python_cpp.txt
new file mode 100644
index 00000000..7b5e45f9
--- /dev/null
+++ b/conformance/failure_list_python_cpp.txt
@@ -0,0 +1,110 @@
+# This is the list of conformance tests that are known to fail for the
+# Python/C++ implementation right now. These should be fixed.
+#
+# By listing them here we can keep tabs on which ones are failing and be sure
+# that we don't introduce regressions in other tests.
+#
+# TODO(haberman): insert links to corresponding bugs tracking the issue.
+# Should we use GitHub issues or the Google-internal bug tracker?
+
+DurationProtoInputTooLarge.JsonOutput
+DurationProtoInputTooSmall.JsonOutput
+FieldMaskNumbersDontRoundTrip.JsonOutput
+FieldMaskPathsDontRoundTrip.JsonOutput
+FieldMaskTooManyUnderscore.JsonOutput
+JsonInput.Any.JsonOutput
+JsonInput.Any.ProtobufOutput
+JsonInput.AnyNested.JsonOutput
+JsonInput.AnyNested.ProtobufOutput
+JsonInput.AnyUnorderedTypeTag.JsonOutput
+JsonInput.AnyUnorderedTypeTag.ProtobufOutput
+JsonInput.AnyWithDuration.JsonOutput
+JsonInput.AnyWithDuration.ProtobufOutput
+JsonInput.AnyWithFieldMask.JsonOutput
+JsonInput.AnyWithFieldMask.ProtobufOutput
+JsonInput.AnyWithInt32ValueWrapper.JsonOutput
+JsonInput.AnyWithInt32ValueWrapper.ProtobufOutput
+JsonInput.AnyWithStruct.JsonOutput
+JsonInput.AnyWithStruct.ProtobufOutput
+JsonInput.AnyWithTimestamp.JsonOutput
+JsonInput.AnyWithTimestamp.ProtobufOutput
+JsonInput.AnyWithValueForInteger.JsonOutput
+JsonInput.AnyWithValueForInteger.ProtobufOutput
+JsonInput.AnyWithValueForJsonObject.JsonOutput
+JsonInput.AnyWithValueForJsonObject.ProtobufOutput
+JsonInput.BytesFieldInvalidBase64Characters
+JsonInput.DoubleFieldInfinityNotQuoted
+JsonInput.DoubleFieldNanNotQuoted
+JsonInput.DoubleFieldNegativeInfinityNotQuoted
+JsonInput.DoubleFieldTooSmall
+JsonInput.DurationJsonInputTooLarge
+JsonInput.DurationJsonInputTooSmall
+JsonInput.DurationMissingS
+JsonInput.EnumFieldNumericValueNonZero.JsonOutput
+JsonInput.EnumFieldNumericValueNonZero.ProtobufOutput
+JsonInput.EnumFieldNumericValueZero.JsonOutput
+JsonInput.EnumFieldNumericValueZero.ProtobufOutput
+JsonInput.EnumFieldUnknownValue.Validator
+JsonInput.FieldMask.ProtobufOutput
+JsonInput.FieldMaskInvalidCharacter
+JsonInput.FieldNameInLowerCamelCase.Validator
+JsonInput.FieldNameInSnakeCase.JsonOutput
+JsonInput.FieldNameInSnakeCase.ProtobufOutput
+JsonInput.FloatFieldInfinityNotQuoted
+JsonInput.FloatFieldNanNotQuoted
+JsonInput.FloatFieldNegativeInfinityNotQuoted
+JsonInput.FloatFieldTooLarge
+JsonInput.FloatFieldTooSmall
+JsonInput.Int32FieldExponentialFormat.JsonOutput
+JsonInput.Int32FieldExponentialFormat.ProtobufOutput
+JsonInput.Int32FieldFloatTrailingZero.JsonOutput
+JsonInput.Int32FieldFloatTrailingZero.ProtobufOutput
+JsonInput.Int32FieldMaxFloatValue.JsonOutput
+JsonInput.Int32FieldMaxFloatValue.ProtobufOutput
+JsonInput.Int32FieldMinFloatValue.JsonOutput
+JsonInput.Int32FieldMinFloatValue.ProtobufOutput
+JsonInput.Int32FieldMinValue.JsonOutput
+JsonInput.OriginalProtoFieldName.JsonOutput
+JsonInput.OriginalProtoFieldName.ProtobufOutput
+JsonInput.RepeatedFieldMessageElementIsNull
+JsonInput.RepeatedFieldPrimitiveElementIsNull
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotBool
+JsonInput.StringFieldSurrogatePair.JsonOutput
+JsonInput.StringFieldUnpairedLowSurrogate
+JsonInput.Struct.JsonOutput
+JsonInput.Struct.ProtobufOutput
+JsonInput.TimestampJsonInputLowercaseT
+JsonInput.Uint32FieldMaxFloatValue.JsonOutput
+JsonInput.Uint32FieldMaxFloatValue.ProtobufOutput
+JsonInput.ValueAcceptBool.JsonOutput
+JsonInput.ValueAcceptBool.ProtobufOutput
+JsonInput.ValueAcceptFloat.JsonOutput
+JsonInput.ValueAcceptFloat.ProtobufOutput
+JsonInput.ValueAcceptInteger.JsonOutput
+JsonInput.ValueAcceptInteger.ProtobufOutput
+JsonInput.ValueAcceptList.JsonOutput
+JsonInput.ValueAcceptList.ProtobufOutput
+JsonInput.ValueAcceptNull.JsonOutput
+JsonInput.ValueAcceptNull.ProtobufOutput
+JsonInput.ValueAcceptObject.JsonOutput
+JsonInput.ValueAcceptObject.ProtobufOutput
+JsonInput.ValueAcceptString.JsonOutput
+JsonInput.ValueAcceptString.ProtobufOutput
+ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
+ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
+ProtobufInput.PrematureEofInPackedField.BOOL
+ProtobufInput.PrematureEofInPackedField.DOUBLE
+ProtobufInput.PrematureEofInPackedField.ENUM
+ProtobufInput.PrematureEofInPackedField.FIXED32
+ProtobufInput.PrematureEofInPackedField.FIXED64
+ProtobufInput.PrematureEofInPackedField.FLOAT
+ProtobufInput.PrematureEofInPackedField.INT32
+ProtobufInput.PrematureEofInPackedField.INT64
+ProtobufInput.PrematureEofInPackedField.SFIXED32
+ProtobufInput.PrematureEofInPackedField.SFIXED64
+ProtobufInput.PrematureEofInPackedField.SINT32
+ProtobufInput.PrematureEofInPackedField.SINT64
+ProtobufInput.PrematureEofInPackedField.UINT32
+ProtobufInput.PrematureEofInPackedField.UINT64
+TimestampProtoInputTooLarge.JsonOutput
+TimestampProtoInputTooSmall.JsonOutput
diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt
index ff220cb9..1b2e7d94 100644
--- a/conformance/failure_list_ruby.txt
+++ b/conformance/failure_list_ruby.txt
@@ -1,2 +1,337 @@
+# Many of the JSON tests are failing due to a simple bug:
+# fields are not camel-cased at all right now. Once this
+# is fixed, this list should grow a lot shorter.
+
+DurationProtoInputTooLarge.JsonOutput
+DurationProtoInputTooSmall.JsonOutput
+FieldMaskNumbersDontRoundTrip.JsonOutput
+FieldMaskPathsDontRoundTrip.JsonOutput
+FieldMaskTooManyUnderscore.JsonOutput
+JsonInput.AllFieldAcceptNull.JsonOutput
+JsonInput.AllFieldAcceptNull.ProtobufOutput
+JsonInput.Any.JsonOutput
+JsonInput.AnyNested.JsonOutput
+JsonInput.AnyNested.ProtobufOutput
+JsonInput.Any.ProtobufOutput
+JsonInput.AnyUnorderedTypeTag.JsonOutput
+JsonInput.AnyUnorderedTypeTag.ProtobufOutput
+JsonInput.AnyWithDuration.JsonOutput
+JsonInput.AnyWithDuration.ProtobufOutput
+JsonInput.AnyWithFieldMask.JsonOutput
+JsonInput.AnyWithFieldMask.ProtobufOutput
+JsonInput.AnyWithInt32ValueWrapper.JsonOutput
+JsonInput.AnyWithInt32ValueWrapper.ProtobufOutput
+JsonInput.AnyWithStruct.JsonOutput
+JsonInput.AnyWithStruct.ProtobufOutput
+JsonInput.AnyWithTimestamp.JsonOutput
+JsonInput.AnyWithTimestamp.ProtobufOutput
+JsonInput.AnyWithValueForInteger.JsonOutput
+JsonInput.AnyWithValueForInteger.ProtobufOutput
+JsonInput.AnyWithValueForJsonObject.JsonOutput
+JsonInput.AnyWithValueForJsonObject.ProtobufOutput
+JsonInput.BoolFieldAllCapitalFalse
+JsonInput.BoolFieldAllCapitalTrue
+JsonInput.BoolFieldCamelCaseFalse
+JsonInput.BoolFieldCamelCaseTrue
+JsonInput.BoolFieldDoubleQuotedFalse
+JsonInput.BoolFieldDoubleQuotedTrue
+JsonInput.BoolFieldFalse.JsonOutput
+JsonInput.BoolFieldFalse.ProtobufOutput
+JsonInput.BoolFieldIntegerOne
+JsonInput.BoolFieldIntegerZero
+JsonInput.BoolFieldTrue.JsonOutput
+JsonInput.BoolFieldTrue.ProtobufOutput
+JsonInput.BoolMapEscapedKey.JsonOutput
+JsonInput.BoolMapEscapedKey.ProtobufOutput
+JsonInput.BoolMapField.JsonOutput
+JsonInput.BoolMapFieldKeyNotQuoted
+JsonInput.BoolMapField.ProtobufOutput
+JsonInput.BytesFieldInvalidBase64Characters
+JsonInput.BytesField.JsonOutput
+JsonInput.BytesFieldNoPadding
+JsonInput.BytesField.ProtobufOutput
+JsonInput.BytesRepeatedField.JsonOutput
+JsonInput.BytesRepeatedField.ProtobufOutput
+JsonInput.DoubleFieldInfinity.JsonOutput
+JsonInput.DoubleFieldInfinityNotQuoted
+JsonInput.DoubleFieldInfinity.ProtobufOutput
+JsonInput.DoubleFieldMaxNegativeValue.JsonOutput
+JsonInput.DoubleFieldMaxNegativeValue.ProtobufOutput
+JsonInput.DoubleFieldMaxPositiveValue.JsonOutput
+JsonInput.DoubleFieldMaxPositiveValue.ProtobufOutput
+JsonInput.DoubleFieldMinNegativeValue.JsonOutput
+JsonInput.DoubleFieldMinNegativeValue.ProtobufOutput
+JsonInput.DoubleFieldMinPositiveValue.JsonOutput
+JsonInput.DoubleFieldMinPositiveValue.ProtobufOutput
+JsonInput.DoubleFieldNan.JsonOutput
+JsonInput.DoubleFieldNanNotQuoted
+JsonInput.DoubleFieldNan.ProtobufOutput
+JsonInput.DoubleFieldNegativeInfinity.JsonOutput
+JsonInput.DoubleFieldNegativeInfinityNotQuoted
+JsonInput.DoubleFieldNegativeInfinity.ProtobufOutput
+JsonInput.DoubleFieldQuotedValue.JsonOutput
+JsonInput.DoubleFieldQuotedValue.ProtobufOutput
+JsonInput.DoubleFieldTooLarge
+JsonInput.DoubleFieldTooSmall
+JsonInput.DurationHas3FractionalDigits.Validator
+JsonInput.DurationHas6FractionalDigits.Validator
+JsonInput.DurationHas9FractionalDigits.Validator
+JsonInput.DurationHasZeroFractionalDigit.Validator
+JsonInput.DurationJsonInputTooLarge
+JsonInput.DurationJsonInputTooSmall
+JsonInput.DurationMaxValue.JsonOutput
+JsonInput.DurationMaxValue.ProtobufOutput
+JsonInput.DurationMinValue.JsonOutput
+JsonInput.DurationMinValue.ProtobufOutput
+JsonInput.DurationMissingS
+JsonInput.DurationRepeatedValue.JsonOutput
+JsonInput.DurationRepeatedValue.ProtobufOutput
+JsonInput.EnumField.JsonOutput
+JsonInput.EnumFieldNotQuoted
+JsonInput.EnumFieldNumericValueNonZero.JsonOutput
+JsonInput.EnumFieldNumericValueNonZero.ProtobufOutput
+JsonInput.EnumFieldNumericValueZero.JsonOutput
+JsonInput.EnumFieldNumericValueZero.ProtobufOutput
+JsonInput.EnumField.ProtobufOutput
+JsonInput.EnumFieldUnknownValue.Validator
+JsonInput.EnumRepeatedField.JsonOutput
+JsonInput.EnumRepeatedField.ProtobufOutput
+JsonInput.FieldMaskInvalidCharacter
+JsonInput.FieldMask.JsonOutput
+JsonInput.FieldMask.ProtobufOutput
+JsonInput.FieldNameDuplicate
+JsonInput.FieldNameDuplicateDifferentCasing1
+JsonInput.FieldNameDuplicateDifferentCasing2
+JsonInput.FieldNameEscaped.JsonOutput
+JsonInput.FieldNameInLowerCamelCase.Validator
+JsonInput.FieldNameInSnakeCase.JsonOutput
+JsonInput.FieldNameInSnakeCase.ProtobufOutput
+JsonInput.FieldNameNotQuoted
+JsonInput.FieldNameWithMixedCases.JsonOutput
+JsonInput.FieldNameWithMixedCases.ProtobufOutput
+JsonInput.FieldNameWithMixedCases.Validator
+JsonInput.FieldNameWithNumbers.JsonOutput
+JsonInput.FieldNameWithNumbers.ProtobufOutput
+JsonInput.FieldNameWithNumbers.Validator
+JsonInput.FloatFieldInfinity.JsonOutput
+JsonInput.FloatFieldInfinityNotQuoted
+JsonInput.FloatFieldInfinity.ProtobufOutput
+JsonInput.FloatFieldMaxNegativeValue.JsonOutput
+JsonInput.FloatFieldMaxNegativeValue.ProtobufOutput
+JsonInput.FloatFieldMaxPositiveValue.JsonOutput
+JsonInput.FloatFieldMaxPositiveValue.ProtobufOutput
+JsonInput.FloatFieldMinNegativeValue.JsonOutput
+JsonInput.FloatFieldMinNegativeValue.ProtobufOutput
+JsonInput.FloatFieldMinPositiveValue.JsonOutput
+JsonInput.FloatFieldMinPositiveValue.ProtobufOutput
+JsonInput.FloatFieldNan.JsonOutput
+JsonInput.FloatFieldNanNotQuoted
+JsonInput.FloatFieldNan.ProtobufOutput
+JsonInput.FloatFieldNegativeInfinity.JsonOutput
+JsonInput.FloatFieldNegativeInfinityNotQuoted
+JsonInput.FloatFieldNegativeInfinity.ProtobufOutput
+JsonInput.FloatFieldQuotedValue.JsonOutput
+JsonInput.FloatFieldQuotedValue.ProtobufOutput
+JsonInput.FloatFieldTooLarge
+JsonInput.FloatFieldTooSmall
JsonInput.HelloWorld.JsonOutput
JsonInput.HelloWorld.ProtobufOutput
+JsonInput.Int32FieldExponentialFormat.JsonOutput
+JsonInput.Int32FieldExponentialFormat.ProtobufOutput
+JsonInput.Int32FieldFloatTrailingZero.JsonOutput
+JsonInput.Int32FieldFloatTrailingZero.ProtobufOutput
+JsonInput.Int32FieldLeadingSpace
+JsonInput.Int32FieldLeadingZero
+JsonInput.Int32FieldMaxFloatValue.JsonOutput
+JsonInput.Int32FieldMaxFloatValue.ProtobufOutput
+JsonInput.Int32FieldMaxValue.JsonOutput
+JsonInput.Int32FieldMaxValue.ProtobufOutput
+JsonInput.Int32FieldMinFloatValue.JsonOutput
+JsonInput.Int32FieldMinFloatValue.ProtobufOutput
+JsonInput.Int32FieldMinValue.JsonOutput
+JsonInput.Int32FieldMinValue.ProtobufOutput
+JsonInput.Int32FieldNegativeWithLeadingZero
+JsonInput.Int32FieldNotInteger
+JsonInput.Int32FieldNotNumber
+JsonInput.Int32FieldPlusSign
+JsonInput.Int32FieldStringValueEscaped.JsonOutput
+JsonInput.Int32FieldStringValueEscaped.ProtobufOutput
+JsonInput.Int32FieldStringValue.JsonOutput
+JsonInput.Int32FieldStringValue.ProtobufOutput
+JsonInput.Int32FieldTooLarge
+JsonInput.Int32FieldTooSmall
+JsonInput.Int32FieldTrailingSpace
+JsonInput.Int32MapEscapedKey.JsonOutput
+JsonInput.Int32MapEscapedKey.ProtobufOutput
+JsonInput.Int32MapField.JsonOutput
+JsonInput.Int32MapFieldKeyNotQuoted
+JsonInput.Int32MapField.ProtobufOutput
+JsonInput.Int64FieldBeString.Validator
+JsonInput.Int64FieldMaxValue.JsonOutput
+JsonInput.Int64FieldMaxValueNotQuoted.JsonOutput
+JsonInput.Int64FieldMaxValueNotQuoted.ProtobufOutput
+JsonInput.Int64FieldMaxValue.ProtobufOutput
+JsonInput.Int64FieldMinValue.JsonOutput
+JsonInput.Int64FieldMinValueNotQuoted.JsonOutput
+JsonInput.Int64FieldMinValueNotQuoted.ProtobufOutput
+JsonInput.Int64FieldMinValue.ProtobufOutput
+JsonInput.Int64FieldNotInteger
+JsonInput.Int64FieldNotNumber
+JsonInput.Int64FieldTooLarge
+JsonInput.Int64FieldTooSmall
+JsonInput.Int64MapEscapedKey.JsonOutput
+JsonInput.Int64MapEscapedKey.ProtobufOutput
+JsonInput.Int64MapField.JsonOutput
+JsonInput.Int64MapFieldKeyNotQuoted
+JsonInput.Int64MapField.ProtobufOutput
+JsonInput.JsonWithComments
+JsonInput.MapFieldKeyIsNull
+JsonInput.MapFieldValueIsNull
+JsonInput.MessageField.JsonOutput
+JsonInput.MessageField.ProtobufOutput
+JsonInput.MessageMapField.JsonOutput
+JsonInput.MessageMapField.ProtobufOutput
+JsonInput.MessageRepeatedField.JsonOutput
+JsonInput.MessageRepeatedField.ProtobufOutput
+JsonInput.OneofFieldDuplicate
+JsonInput.OptionalBoolWrapper.JsonOutput
+JsonInput.OptionalBoolWrapper.ProtobufOutput
+JsonInput.OptionalBytesWrapper.JsonOutput
+JsonInput.OptionalBytesWrapper.ProtobufOutput
+JsonInput.OptionalDoubleWrapper.JsonOutput
+JsonInput.OptionalDoubleWrapper.ProtobufOutput
+JsonInput.OptionalFloatWrapper.JsonOutput
+JsonInput.OptionalFloatWrapper.ProtobufOutput
+JsonInput.OptionalInt32Wrapper.JsonOutput
+JsonInput.OptionalInt32Wrapper.ProtobufOutput
+JsonInput.OptionalInt64Wrapper.JsonOutput
+JsonInput.OptionalInt64Wrapper.ProtobufOutput
+JsonInput.OptionalStringWrapper.JsonOutput
+JsonInput.OptionalStringWrapper.ProtobufOutput
+JsonInput.OptionalUint32Wrapper.JsonOutput
+JsonInput.OptionalUint32Wrapper.ProtobufOutput
+JsonInput.OptionalUint64Wrapper.JsonOutput
+JsonInput.OptionalUint64Wrapper.ProtobufOutput
+JsonInput.OptionalWrapperTypesWithNonDefaultValue.JsonOutput
+JsonInput.OptionalWrapperTypesWithNonDefaultValue.ProtobufOutput
+JsonInput.OriginalProtoFieldName.JsonOutput
+JsonInput.PrimitiveRepeatedField.JsonOutput
+JsonInput.PrimitiveRepeatedField.ProtobufOutput
+JsonInput.RepeatedBoolWrapper.JsonOutput
+JsonInput.RepeatedBoolWrapper.ProtobufOutput
+JsonInput.RepeatedBytesWrapper.JsonOutput
+JsonInput.RepeatedBytesWrapper.ProtobufOutput
+JsonInput.RepeatedDoubleWrapper.JsonOutput
+JsonInput.RepeatedDoubleWrapper.ProtobufOutput
+JsonInput.RepeatedFieldMessageElementIsNull
+JsonInput.RepeatedFieldPrimitiveElementIsNull
+JsonInput.RepeatedFieldTrailingComma
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotMessage
+JsonInput.RepeatedFieldWrongElementTypeExpectingIntegersGotString
+JsonInput.RepeatedFieldWrongElementTypeExpectingMessagesGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingMessagesGotInt
+JsonInput.RepeatedFieldWrongElementTypeExpectingMessagesGotString
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotBool
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotInt
+JsonInput.RepeatedFieldWrongElementTypeExpectingStringsGotMessage
+JsonInput.RepeatedFloatWrapper.JsonOutput
+JsonInput.RepeatedFloatWrapper.ProtobufOutput
+JsonInput.RepeatedInt32Wrapper.JsonOutput
+JsonInput.RepeatedInt32Wrapper.ProtobufOutput
+JsonInput.RepeatedInt64Wrapper.JsonOutput
+JsonInput.RepeatedInt64Wrapper.ProtobufOutput
+JsonInput.RepeatedStringWrapper.JsonOutput
+JsonInput.RepeatedStringWrapper.ProtobufOutput
+JsonInput.RepeatedUint32Wrapper.JsonOutput
+JsonInput.RepeatedUint32Wrapper.ProtobufOutput
+JsonInput.RepeatedUint64Wrapper.JsonOutput
+JsonInput.RepeatedUint64Wrapper.ProtobufOutput
+JsonInput.StringFieldEscape.JsonOutput
+JsonInput.StringFieldEscape.ProtobufOutput
+JsonInput.StringFieldInvalidEscape
+JsonInput.StringField.JsonOutput
+JsonInput.StringFieldNotAString
+JsonInput.StringField.ProtobufOutput
+JsonInput.StringFieldSurrogateInWrongOrder
+JsonInput.StringFieldSurrogatePair.JsonOutput
+JsonInput.StringFieldSurrogatePair.ProtobufOutput
+JsonInput.StringFieldUnicodeEscape.JsonOutput
+JsonInput.StringFieldUnicodeEscape.ProtobufOutput
+JsonInput.StringFieldUnicodeEscapeWithLowercaseHexLetters.JsonOutput
+JsonInput.StringFieldUnicodeEscapeWithLowercaseHexLetters.ProtobufOutput
+JsonInput.StringFieldUnicode.JsonOutput
+JsonInput.StringFieldUnicode.ProtobufOutput
+JsonInput.StringFieldUnpairedHighSurrogate
+JsonInput.StringFieldUnpairedLowSurrogate
+JsonInput.StringFieldUnterminatedEscape
+JsonInput.StringFieldUppercaseEscapeLetter
+JsonInput.StringRepeatedField.JsonOutput
+JsonInput.StringRepeatedField.ProtobufOutput
+JsonInput.Struct.JsonOutput
+JsonInput.Struct.ProtobufOutput
+JsonInput.TimestampHas3FractionalDigits.Validator
+JsonInput.TimestampHas6FractionalDigits.Validator
+JsonInput.TimestampHas9FractionalDigits.Validator
+JsonInput.TimestampHasZeroFractionalDigit.Validator
+JsonInput.TimestampJsonInputLowercaseT
+JsonInput.TimestampJsonInputLowercaseZ
+JsonInput.TimestampJsonInputMissingT
+JsonInput.TimestampJsonInputMissingZ
+JsonInput.TimestampJsonInputTooLarge
+JsonInput.TimestampJsonInputTooSmall
+JsonInput.TimestampMaxValue.JsonOutput
+JsonInput.TimestampMaxValue.ProtobufOutput
+JsonInput.TimestampMinValue.JsonOutput
+JsonInput.TimestampMinValue.ProtobufOutput
+JsonInput.TimestampRepeatedValue.JsonOutput
+JsonInput.TimestampRepeatedValue.ProtobufOutput
+JsonInput.TimestampWithNegativeOffset.JsonOutput
+JsonInput.TimestampWithNegativeOffset.ProtobufOutput
+JsonInput.TimestampWithPositiveOffset.JsonOutput
+JsonInput.TimestampWithPositiveOffset.ProtobufOutput
+JsonInput.TimestampZeroNormalized.Validator
+JsonInput.TrailingCommaInAnObject
+JsonInput.Uint32FieldMaxFloatValue.JsonOutput
+JsonInput.Uint32FieldMaxFloatValue.ProtobufOutput
+JsonInput.Uint32FieldMaxValue.JsonOutput
+JsonInput.Uint32FieldMaxValue.ProtobufOutput
+JsonInput.Uint32FieldNotInteger
+JsonInput.Uint32FieldNotNumber
+JsonInput.Uint32FieldTooLarge
+JsonInput.Uint32MapField.JsonOutput
+JsonInput.Uint32MapFieldKeyNotQuoted
+JsonInput.Uint32MapField.ProtobufOutput
+JsonInput.Uint64FieldBeString.Validator
+JsonInput.Uint64FieldMaxValue.JsonOutput
+JsonInput.Uint64FieldMaxValueNotQuoted.JsonOutput
+JsonInput.Uint64FieldMaxValueNotQuoted.ProtobufOutput
+JsonInput.Uint64FieldMaxValue.ProtobufOutput
+JsonInput.Uint64FieldNotInteger
+JsonInput.Uint64FieldNotNumber
+JsonInput.Uint64FieldTooLarge
+JsonInput.Uint64MapField.JsonOutput
+JsonInput.Uint64MapFieldKeyNotQuoted
+JsonInput.Uint64MapField.ProtobufOutput
+JsonInput.ValueAcceptBool.JsonOutput
+JsonInput.ValueAcceptBool.ProtobufOutput
+JsonInput.ValueAcceptFloat.JsonOutput
+JsonInput.ValueAcceptFloat.ProtobufOutput
+JsonInput.ValueAcceptInteger.JsonOutput
+JsonInput.ValueAcceptInteger.ProtobufOutput
+JsonInput.ValueAcceptList.JsonOutput
+JsonInput.ValueAcceptList.ProtobufOutput
+JsonInput.ValueAcceptNull.JsonOutput
+JsonInput.ValueAcceptNull.ProtobufOutput
+JsonInput.ValueAcceptObject.JsonOutput
+JsonInput.ValueAcceptObject.ProtobufOutput
+JsonInput.ValueAcceptString.JsonOutput
+JsonInput.ValueAcceptString.ProtobufOutput
+JsonInput.WrapperTypesWithNullValue.JsonOutput
+JsonInput.WrapperTypesWithNullValue.ProtobufOutput
+ProtobufInput.DoubleFieldNormalizeQuietNan.JsonOutput
+ProtobufInput.DoubleFieldNormalizeSignalingNan.JsonOutput
+ProtobufInput.FloatFieldNormalizeQuietNan.JsonOutput
+ProtobufInput.FloatFieldNormalizeSignalingNan.JsonOutput
+TimestampProtoInputTooLarge.JsonOutput
+TimestampProtoInputTooSmall.JsonOutput
diff --git a/conformance/third_party/jsoncpp/json.h b/conformance/third_party/jsoncpp/json.h
new file mode 100644
index 00000000..42e7e7f4
--- /dev/null
+++ b/conformance/third_party/jsoncpp/json.h
@@ -0,0 +1,2075 @@
+/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation,
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all
+jurisdictions which recognize such a disclaimer. In such jurisdictions,
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this
+software may choose to accept it either as 1) Public Domain, 2) under the
+conditions of the MIT License (see below), or 3) under the terms of dual
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+ http://en.wikipedia.org/wiki/MIT_License
+
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef JSON_AMALGATED_H_INCLUDED
+# define JSON_AMALGATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+// DO NOT EDIT. This file (and "version") is generated by CMake.
+// Run CMake configure step to update it.
+#ifndef JSON_VERSION_H_INCLUDED
+# define JSON_VERSION_H_INCLUDED
+
+# define JSONCPP_VERSION_STRING "1.6.5"
+# define JSONCPP_VERSION_MAJOR 1
+# define JSONCPP_VERSION_MINOR 6
+# define JSONCPP_VERSION_PATCH 5
+# define JSONCPP_VERSION_QUALIFIER
+# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
+
+#endif // JSON_VERSION_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+#define JSON_CONFIG_H_INCLUDED
+
+/// If defined, indicates that json library is embedded in CppTL library.
+//# define JSON_IN_CPPTL 1
+
+/// If defined, indicates that json may leverage CppTL library
+//# define JSON_USE_CPPTL 1
+/// If defined, indicates that cpptl vector based map should be used instead of
+/// std::map
+/// as Value container.
+//# define JSON_USE_CPPTL_SMALLMAP 1
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgated header.
+// #define JSON_IS_AMALGAMATION
+
+#ifdef JSON_IN_CPPTL
+#include <cpptl/config.h>
+#ifndef JSON_USE_CPPTL
+#define JSON_USE_CPPTL 1
+#endif
+#endif
+
+#ifdef JSON_IN_CPPTL
+#define JSON_API CPPTL_API
+#elif defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_IN_CPPTL
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+#if defined(_MSC_VER) // MSVC
+# if _MSC_VER <= 1200 // MSVC 6
+ // Microsoft Visual Studio 6 only support conversion from __int64 to double
+ // (no conversion from unsigned __int64).
+# define JSON_USE_INT64_DOUBLE_CONVERSION 1
+ // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
+ // characters in the debug information)
+ // All projects I've ever seen with VS6 were using this globally (not bothering
+ // with pragma push/pop).
+# pragma warning(disable : 4786)
+# endif // MSVC 6
+
+# if _MSC_VER >= 1500 // MSVC 2008
+ /// Indicates that the following function is deprecated.
+# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+# endif
+
+#endif // defined(_MSC_VER)
+
+
+#ifndef JSON_HAS_RVALUE_REFERENCES
+
+#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // MSVC >= 2010
+
+#ifdef __clang__
+#if __has_feature(cxx_rvalue_references)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // has_feature
+
+#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // GXX_EXPERIMENTAL
+
+#endif // __clang__ || __GNUC__
+
+#endif // not defined JSON_HAS_RVALUE_REFERENCES
+
+#ifndef JSON_HAS_RVALUE_REFERENCES
+#define JSON_HAS_RVALUE_REFERENCES 0
+#endif
+
+#ifdef __clang__
+#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
+# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
+# endif // GNUC version
+#endif // __clang__ || __GNUC__
+
+#if !defined(JSONCPP_DEPRECATED)
+#define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+namespace Json {
+typedef int Int;
+typedef unsigned int UInt;
+#if defined(JSON_NO_INT64)
+typedef int LargestInt;
+typedef unsigned int LargestUInt;
+#undef JSON_HAS_INT64
+#else // if defined(JSON_NO_INT64)
+// For Microsoft Visual use specific types as long long is not supported
+#if defined(_MSC_VER) // Microsoft Visual Studio
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#else // if defined(_MSC_VER) // Other platforms, use long long
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#endif // if defined(_MSC_VER)
+typedef Int64 LargestInt;
+typedef UInt64 LargestUInt;
+#define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+} // end namespace Json
+
+#endif // JSON_CONFIG_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// writer.h
+class FastWriter;
+class StyledWriter;
+
+// reader.h
+class Reader;
+
+// features.h
+class Features;
+
+// value.h
+typedef unsigned int ArrayIndex;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
+
+} // namespace Json
+
+#endif // JSON_FORWARDS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
+#define CPPTL_JSON_FEATURES_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+/** \brief Configuration passed to reader and writer.
+ * This configuration object can be used to force the Reader or Writer
+ * to behave in a standard conforming way.
+ */
+class JSON_API Features {
+public:
+ /** \brief A configuration that allows all features and assumes all strings
+ * are UTF-8.
+ * - C & C++ comments are allowed
+ * - Root object can be any JSON value
+ * - Assumes Value strings are encoded in UTF-8
+ */
+ static Features all();
+
+ /** \brief A configuration that is strictly compatible with the JSON
+ * specification.
+ * - Comments are forbidden.
+ * - Root object must be either an array or an object value.
+ * - Assumes Value strings are encoded in UTF-8
+ */
+ static Features strictMode();
+
+ /** \brief Initialize the configuration like JsonConfig::allFeatures;
+ */
+ Features();
+
+ /// \c true if comments are allowed. Default: \c true.
+ bool allowComments_;
+
+ /// \c true if root must be either an array or an object value. Default: \c
+ /// false.
+ bool strictRoot_;
+
+ /// \c true if dropped null placeholders are allowed. Default: \c false.
+ bool allowDroppedNullPlaceholders_;
+
+ /// \c true if numeric object key are allowed. Default: \c false.
+ bool allowNumericKeys_;
+};
+
+} // namespace Json
+
+#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_H_INCLUDED
+#define CPPTL_JSON_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <string>
+#include <vector>
+#include <exception>
+
+#ifndef JSON_USE_CPPTL_SMALLMAP
+#include <map>
+#else
+#include <cpptl/smallmap.h>
+#endif
+#ifdef JSON_USE_CPPTL
+#include <cpptl/forwards.h>
+#endif
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+/** \brief JSON (JavaScript Object Notation).
+ */
+namespace Json {
+
+/** Base class for all exceptions we throw.
+ *
+ * We use nothing but these internally. Of course, STL can throw others.
+ */
+class JSON_API Exception : public std::exception {
+public:
+ Exception(std::string const& msg);
+ ~Exception() throw() override;
+ char const* what() const throw() override;
+protected:
+ std::string msg_;
+};
+
+/** Exceptions which the user cannot easily avoid.
+ *
+ * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API RuntimeError : public Exception {
+public:
+ RuntimeError(std::string const& msg);
+};
+
+/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
+ *
+ * These are precondition-violations (user bugs) and internal errors (our bugs).
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API LogicError : public Exception {
+public:
+ LogicError(std::string const& msg);
+};
+
+/// used internally
+void throwRuntimeError(std::string const& msg);
+/// used internally
+void throwLogicError(std::string const& msg);
+
+/** \brief Type of the value held by a Value object.
+ */
+enum ValueType {
+ nullValue = 0, ///< 'null' value
+ intValue, ///< signed integer value
+ uintValue, ///< unsigned integer value
+ realValue, ///< double value
+ stringValue, ///< UTF-8 string value
+ booleanValue, ///< bool value
+ arrayValue, ///< array value (ordered list)
+ objectValue ///< object value (collection of name/value pairs).
+};
+
+enum CommentPlacement {
+ commentBefore = 0, ///< a comment placed on the line before a value
+ commentAfterOnSameLine, ///< a comment just after a value on the same line
+ commentAfter, ///< a comment on the line after a value (only make sense for
+ /// root value)
+ numberOfCommentPlacement
+};
+
+//# ifdef JSON_USE_CPPTL
+// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
+// typedef CppTL::AnyEnumerator<const Value &> EnumValues;
+//# endif
+
+/** \brief Lightweight wrapper to tag static string.
+ *
+ * Value constructor and objectValue member assignement takes advantage of the
+ * StaticString and avoid the cost of string duplication when storing the
+ * string or the member name.
+ *
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+class JSON_API StaticString {
+public:
+ explicit StaticString(const char* czstring) : c_str_(czstring) {}
+
+ operator const char*() const { return c_str_; }
+
+ const char* c_str() const { return c_str_; }
+
+private:
+ const char* c_str_;
+};
+
+/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
+ *
+ * This class is a discriminated union wrapper that can represents a:
+ * - signed integer [range: Value::minInt - Value::maxInt]
+ * - unsigned integer (range: 0 - Value::maxUInt)
+ * - double
+ * - UTF-8 string
+ * - boolean
+ * - 'null'
+ * - an ordered list of Value
+ * - collection of name/value pairs (javascript object)
+ *
+ * The type of the held value is represented by a #ValueType and
+ * can be obtained using type().
+ *
+ * Values of an #objectValue or #arrayValue can be accessed using operator[]()
+ * methods.
+ * Non-const methods will automatically create the a #nullValue element
+ * if it does not exist.
+ * The sequence of an #arrayValue will be automatically resized and initialized
+ * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
+ *
+ * The get() methods can be used to obtain default value in the case the
+ * required element does not exist.
+ *
+ * It is possible to iterate over the list of a #objectValue values using
+ * the getMemberNames() method.
+ *
+ * \note #Value string-length fit in size_t, but keys must be < 2^30.
+ * (The reason is an implementation detail.) A #CharReader will raise an
+ * exception if a bound is exceeded to avoid security holes in your app,
+ * but the Value API does *not* check bounds. That is the responsibility
+ * of the caller.
+ */
+class JSON_API Value {
+ friend class ValueIteratorBase;
+public:
+ typedef std::vector<std::string> Members;
+ typedef ValueIterator iterator;
+ typedef ValueConstIterator const_iterator;
+ typedef Json::UInt UInt;
+ typedef Json::Int Int;
+#if defined(JSON_HAS_INT64)
+ typedef Json::UInt64 UInt64;
+ typedef Json::Int64 Int64;
+#endif // defined(JSON_HAS_INT64)
+ typedef Json::LargestInt LargestInt;
+ typedef Json::LargestUInt LargestUInt;
+ typedef Json::ArrayIndex ArrayIndex;
+
+ static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value().
+ static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null
+ /// Minimum signed integer value that can be stored in a Json::Value.
+ static const LargestInt minLargestInt;
+ /// Maximum signed integer value that can be stored in a Json::Value.
+ static const LargestInt maxLargestInt;
+ /// Maximum unsigned integer value that can be stored in a Json::Value.
+ static const LargestUInt maxLargestUInt;
+
+ /// Minimum signed int value that can be stored in a Json::Value.
+ static const Int minInt;
+ /// Maximum signed int value that can be stored in a Json::Value.
+ static const Int maxInt;
+ /// Maximum unsigned int value that can be stored in a Json::Value.
+ static const UInt maxUInt;
+
+#if defined(JSON_HAS_INT64)
+ /// Minimum signed 64 bits int value that can be stored in a Json::Value.
+ static const Int64 minInt64;
+ /// Maximum signed 64 bits int value that can be stored in a Json::Value.
+ static const Int64 maxInt64;
+ /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
+ static const UInt64 maxUInt64;
+#endif // defined(JSON_HAS_INT64)
+
+private:
+#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+ class CZString {
+ public:
+ enum DuplicationPolicy {
+ noDuplication = 0,
+ duplicate,
+ duplicateOnCopy
+ };
+ CZString(ArrayIndex index);
+ CZString(char const* str, unsigned length, DuplicationPolicy allocate);
+ CZString(CZString const& other);
+#if JSON_HAS_RVALUE_REFERENCES
+ CZString(CZString&& other);
+#endif
+ ~CZString();
+ CZString& operator=(CZString other);
+ bool operator<(CZString const& other) const;
+ bool operator==(CZString const& other) const;
+ ArrayIndex index() const;
+ //const char* c_str() const; ///< \deprecated
+ char const* data() const;
+ unsigned length() const;
+ bool isStaticString() const;
+
+ private:
+ void swap(CZString& other);
+
+ struct StringStorage {
+ unsigned policy_: 2;
+ unsigned length_: 30; // 1GB max
+ };
+
+ char const* cstr_; // actually, a prefixed string, unless policy is noDup
+ union {
+ ArrayIndex index_;
+ StringStorage storage_;
+ };
+ };
+
+public:
+#ifndef JSON_USE_CPPTL_SMALLMAP
+ typedef std::map<CZString, Value> ObjectValues;
+#else
+ typedef CppTL::SmallMap<CZString, Value> ObjectValues;
+#endif // ifndef JSON_USE_CPPTL_SMALLMAP
+#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+public:
+ /** \brief Create a default Value of the given type.
+
+ This is a very useful constructor.
+ To create an empty array, pass arrayValue.
+ To create an empty object, pass objectValue.
+ Another Value can then be set to this one by assignment.
+This is useful since clear() and resize() will not alter types.
+
+ Examples:
+\code
+Json::Value null_value; // null
+Json::Value arr_value(Json::arrayValue); // []
+Json::Value obj_value(Json::objectValue); // {}
+\endcode
+ */
+ Value(ValueType type = nullValue);
+ Value(Int value);
+ Value(UInt value);
+#if defined(JSON_HAS_INT64)
+ Value(Int64 value);
+ Value(UInt64 value);
+#endif // if defined(JSON_HAS_INT64)
+ Value(double value);
+ Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
+ Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
+ /** \brief Constructs a value from a static string.
+
+ * Like other value string constructor but do not duplicate the string for
+ * internal storage. The given string must remain alive after the call to this
+ * constructor.
+ * \note This works only for null-terminated strings. (We cannot change the
+ * size of this class, so we have nowhere to store the length,
+ * which might be computed later for various operations.)
+ *
+ * Example of usage:
+ * \code
+ * static StaticString foo("some text");
+ * Json::Value aValue(foo);
+ * \endcode
+ */
+ Value(const StaticString& value);
+ Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too.
+#ifdef JSON_USE_CPPTL
+ Value(const CppTL::ConstString& value);
+#endif
+ Value(bool value);
+ /// Deep copy.
+ Value(const Value& other);
+#if JSON_HAS_RVALUE_REFERENCES
+ /// Move constructor
+ Value(Value&& other);
+#endif
+ ~Value();
+
+ /// Deep copy, then swap(other).
+ /// \note Over-write existing comments. To preserve comments, use #swapPayload().
+ Value& operator=(Value other);
+ /// Swap everything.
+ void swap(Value& other);
+ /// Swap values but leave comments and source offsets in place.
+ void swapPayload(Value& other);
+
+ ValueType type() const;
+
+ /// Compare payload only, not comments etc.
+ bool operator<(const Value& other) const;
+ bool operator<=(const Value& other) const;
+ bool operator>=(const Value& other) const;
+ bool operator>(const Value& other) const;
+ bool operator==(const Value& other) const;
+ bool operator!=(const Value& other) const;
+ int compare(const Value& other) const;
+
+ const char* asCString() const; ///< Embedded zeroes could cause you trouble!
+ std::string asString() const; ///< Embedded zeroes are possible.
+ /** Get raw char* of string-value.
+ * \return false if !string. (Seg-fault if str or end are NULL.)
+ */
+ bool getString(
+ char const** begin, char const** end) const;
+#ifdef JSON_USE_CPPTL
+ CppTL::ConstString asConstString() const;
+#endif
+ Int asInt() const;
+ UInt asUInt() const;
+#if defined(JSON_HAS_INT64)
+ Int64 asInt64() const;
+ UInt64 asUInt64() const;
+#endif // if defined(JSON_HAS_INT64)
+ LargestInt asLargestInt() const;
+ LargestUInt asLargestUInt() const;
+ float asFloat() const;
+ double asDouble() const;
+ bool asBool() const;
+
+ bool isNull() const;
+ bool isBool() const;
+ bool isInt() const;
+ bool isInt64() const;
+ bool isUInt() const;
+ bool isUInt64() const;
+ bool isIntegral() const;
+ bool isDouble() const;
+ bool isNumeric() const;
+ bool isString() const;
+ bool isArray() const;
+ bool isObject() const;
+
+ bool isConvertibleTo(ValueType other) const;
+
+ /// Number of values in array or object
+ ArrayIndex size() const;
+
+ /// \brief Return true if empty array, empty object, or null;
+ /// otherwise, false.
+ bool empty() const;
+
+ /// Return isNull()
+ bool operator!() const;
+
+ /// Remove all object members and array elements.
+ /// \pre type() is arrayValue, objectValue, or nullValue
+ /// \post type() is unchanged
+ void clear();
+
+ /// Resize the array to size elements.
+ /// New elements are initialized to null.
+ /// May only be called on nullValue or arrayValue.
+ /// \pre type() is arrayValue or nullValue
+ /// \post type() is arrayValue
+ void resize(ArrayIndex size);
+
+ /// Access an array element (zero based index ).
+ /// If the array contains less than index element, then null value are
+ /// inserted
+ /// in the array so that its size is index+1.
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ Value& operator[](ArrayIndex index);
+
+ /// Access an array element (zero based index ).
+ /// If the array contains less than index element, then null value are
+ /// inserted
+ /// in the array so that its size is index+1.
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ Value& operator[](int index);
+
+ /// Access an array element (zero based index )
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ const Value& operator[](ArrayIndex index) const;
+
+ /// Access an array element (zero based index )
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ const Value& operator[](int index) const;
+
+ /// If the array contains at least index+1 elements, returns the element
+ /// value,
+ /// otherwise returns defaultValue.
+ Value get(ArrayIndex index, const Value& defaultValue) const;
+ /// Return true if index < size().
+ bool isValidIndex(ArrayIndex index) const;
+ /// \brief Append value to array at the end.
+ ///
+ /// Equivalent to jsonvalue[jsonvalue.size()] = value;
+ Value& append(const Value& value);
+
+ /// Access an object value by name, create a null member if it does not exist.
+ /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
+ /// Exceeding that will cause an exception.
+ Value& operator[](const char* key);
+ /// Access an object value by name, returns null if there is no member with
+ /// that name.
+ const Value& operator[](const char* key) const;
+ /// Access an object value by name, create a null member if it does not exist.
+ /// \param key may contain embedded nulls.
+ Value& operator[](const std::string& key);
+ /// Access an object value by name, returns null if there is no member with
+ /// that name.
+ /// \param key may contain embedded nulls.
+ const Value& operator[](const std::string& key) const;
+ /** \brief Access an object value by name, create a null member if it does not
+ exist.
+
+ * If the object has no entry for that name, then the member name used to store
+ * the new entry is not duplicated.
+ * Example of use:
+ * \code
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+ Value& operator[](const StaticString& key);
+#ifdef JSON_USE_CPPTL
+ /// Access an object value by name, create a null member if it does not exist.
+ Value& operator[](const CppTL::ConstString& key);
+ /// Access an object value by name, returns null if there is no member with
+ /// that name.
+ const Value& operator[](const CppTL::ConstString& key) const;
+#endif
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ Value get(const char* key, const Value& defaultValue) const;
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ /// \note key may contain embedded nulls.
+ Value get(const char* begin, const char* end, const Value& defaultValue) const;
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ /// \param key may contain embedded nulls.
+ Value get(const std::string& key, const Value& defaultValue) const;
+#ifdef JSON_USE_CPPTL
+ /// Return the member named key if it exist, defaultValue otherwise.
+ /// \note deep copy
+ Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
+#endif
+ /// Most general and efficient version of isMember()const, get()const,
+ /// and operator[]const
+ /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+ Value const* find(char const* begin, char const* end) const;
+ /// Most general and efficient version of object-mutators.
+ /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+ /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
+ Value const* demand(char const* begin, char const* end);
+ /// \brief Remove and return the named member.
+ ///
+ /// Do nothing if it did not exist.
+ /// \return the removed Value, or null.
+ /// \pre type() is objectValue or nullValue
+ /// \post type() is unchanged
+ /// \deprecated
+ Value removeMember(const char* key);
+ /// Same as removeMember(const char*)
+ /// \param key may contain embedded nulls.
+ /// \deprecated
+ Value removeMember(const std::string& key);
+ /// Same as removeMember(const char* begin, const char* end, Value* removed),
+ /// but 'key' is null-terminated.
+ bool removeMember(const char* key, Value* removed);
+ /** \brief Remove the named map member.
+
+ Update 'removed' iff removed.
+ \param key may contain embedded nulls.
+ \return true iff removed (no exceptions)
+ */
+ bool removeMember(std::string const& key, Value* removed);
+ /// Same as removeMember(std::string const& key, Value* removed)
+ bool removeMember(const char* begin, const char* end, Value* removed);
+ /** \brief Remove the indexed array element.
+
+ O(n) expensive operations.
+ Update 'removed' iff removed.
+ \return true iff removed (no exceptions)
+ */
+ bool removeIndex(ArrayIndex i, Value* removed);
+
+ /// Return true if the object has a member named key.
+ /// \note 'key' must be null-terminated.
+ bool isMember(const char* key) const;
+ /// Return true if the object has a member named key.
+ /// \param key may contain embedded nulls.
+ bool isMember(const std::string& key) const;
+ /// Same as isMember(std::string const& key)const
+ bool isMember(const char* begin, const char* end) const;
+#ifdef JSON_USE_CPPTL
+ /// Return true if the object has a member named key.
+ bool isMember(const CppTL::ConstString& key) const;
+#endif
+
+ /// \brief Return a list of the member names.
+ ///
+ /// If null, return an empty list.
+ /// \pre type() is objectValue or nullValue
+ /// \post if type() was nullValue, it remains nullValue
+ Members getMemberNames() const;
+
+ //# ifdef JSON_USE_CPPTL
+ // EnumMemberNames enumMemberNames() const;
+ // EnumValues enumValues() const;
+ //# endif
+
+ /// \deprecated Always pass len.
+ JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.")
+ void setComment(const char* comment, CommentPlacement placement);
+ /// Comments must be //... or /* ... */
+ void setComment(const char* comment, size_t len, CommentPlacement placement);
+ /// Comments must be //... or /* ... */
+ void setComment(const std::string& comment, CommentPlacement placement);
+ bool hasComment(CommentPlacement placement) const;
+ /// Include delimiters and embedded newlines.
+ std::string getComment(CommentPlacement placement) const;
+
+ std::string toStyledString() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ iterator begin();
+ iterator end();
+
+ // Accessors for the [start, limit) range of bytes within the JSON text from
+ // which this value was parsed, if any.
+ void setOffsetStart(size_t start);
+ void setOffsetLimit(size_t limit);
+ size_t getOffsetStart() const;
+ size_t getOffsetLimit() const;
+
+private:
+ void initBasic(ValueType type, bool allocated = false);
+
+ Value& resolveReference(const char* key);
+ Value& resolveReference(const char* key, const char* end);
+
+ struct CommentInfo {
+ CommentInfo();
+ ~CommentInfo();
+
+ void setComment(const char* text, size_t len);
+
+ char* comment_;
+ };
+
+ // struct MemberNamesTransform
+ //{
+ // typedef const char *result_type;
+ // const char *operator()( const CZString &name ) const
+ // {
+ // return name.c_str();
+ // }
+ //};
+
+ union ValueHolder {
+ LargestInt int_;
+ LargestUInt uint_;
+ double real_;
+ bool bool_;
+ char* string_; // actually ptr to unsigned, followed by str, unless !allocated_
+ ObjectValues* map_;
+ } value_;
+ ValueType type_ : 8;
+ unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
+ // If not allocated_, string_ must be null-terminated.
+ CommentInfo* comments_;
+
+ // [start, limit) byte offsets in the source JSON text from which this Value
+ // was extracted.
+ size_t start_;
+ size_t limit_;
+};
+
+/** \brief Experimental and untested: represents an element of the "path" to
+ * access a node.
+ */
+class JSON_API PathArgument {
+public:
+ friend class Path;
+
+ PathArgument();
+ PathArgument(ArrayIndex index);
+ PathArgument(const char* key);
+ PathArgument(const std::string& key);
+
+private:
+ enum Kind {
+ kindNone = 0,
+ kindIndex,
+ kindKey
+ };
+ std::string key_;
+ ArrayIndex index_;
+ Kind kind_;
+};
+
+/** \brief Experimental and untested: represents a "path" to access a node.
+ *
+ * Syntax:
+ * - "." => root node
+ * - ".[n]" => elements at index 'n' of root node (an array value)
+ * - ".name" => member named 'name' of root node (an object value)
+ * - ".name1.name2.name3"
+ * - ".[0][1][2].name1[3]"
+ * - ".%" => member name is provided as parameter
+ * - ".[%]" => index is provied as parameter
+ */
+class JSON_API Path {
+public:
+ Path(const std::string& path,
+ const PathArgument& a1 = PathArgument(),
+ const PathArgument& a2 = PathArgument(),
+ const PathArgument& a3 = PathArgument(),
+ const PathArgument& a4 = PathArgument(),
+ const PathArgument& a5 = PathArgument());
+
+ const Value& resolve(const Value& root) const;
+ Value resolve(const Value& root, const Value& defaultValue) const;
+ /// Creates the "path" to access the specified node and returns a reference on
+ /// the node.
+ Value& make(Value& root) const;
+
+private:
+ typedef std::vector<const PathArgument*> InArgs;
+ typedef std::vector<PathArgument> Args;
+
+ void makePath(const std::string& path, const InArgs& in);
+ void addPathInArg(const std::string& path,
+ const InArgs& in,
+ InArgs::const_iterator& itInArg,
+ PathArgument::Kind kind);
+ void invalidPath(const std::string& path, int location);
+
+ Args args_;
+};
+
+/** \brief base class for Value iterators.
+ *
+ */
+class JSON_API ValueIteratorBase {
+public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef unsigned int size_t;
+ typedef int difference_type;
+ typedef ValueIteratorBase SelfType;
+
+ bool operator==(const SelfType& other) const { return isEqual(other); }
+
+ bool operator!=(const SelfType& other) const { return !isEqual(other); }
+
+ difference_type operator-(const SelfType& other) const {
+ return other.computeDistance(*this);
+ }
+
+ /// Return either the index or the member name of the referenced value as a
+ /// Value.
+ Value key() const;
+
+ /// Return the index of the referenced Value, or -1 if it is not an arrayValue.
+ UInt index() const;
+
+ /// Return the member name of the referenced Value, or "" if it is not an
+ /// objectValue.
+ /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
+ std::string name() const;
+
+ /// Return the member name of the referenced Value. "" if it is not an
+ /// objectValue.
+ /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.
+ JSONCPP_DEPRECATED("Use `key = name();` instead.")
+ char const* memberName() const;
+ /// Return the member name of the referenced Value, or NULL if it is not an
+ /// objectValue.
+ /// \note Better version than memberName(). Allows embedded nulls.
+ char const* memberName(char const** end) const;
+
+protected:
+ Value& deref() const;
+
+ void increment();
+
+ void decrement();
+
+ difference_type computeDistance(const SelfType& other) const;
+
+ bool isEqual(const SelfType& other) const;
+
+ void copy(const SelfType& other);
+
+private:
+ Value::ObjectValues::iterator current_;
+ // Indicates that iterator is for a null value.
+ bool isNull_;
+
+public:
+ // For some reason, BORLAND needs these at the end, rather
+ // than earlier. No idea why.
+ ValueIteratorBase();
+ explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
+};
+
+/** \brief const iterator for object and array value.
+ *
+ */
+class JSON_API ValueConstIterator : public ValueIteratorBase {
+ friend class Value;
+
+public:
+ typedef const Value value_type;
+ //typedef unsigned int size_t;
+ //typedef int difference_type;
+ typedef const Value& reference;
+ typedef const Value* pointer;
+ typedef ValueConstIterator SelfType;
+
+ ValueConstIterator();
+ ValueConstIterator(ValueIterator const& other);
+
+private:
+/*! \internal Use by Value to create an iterator.
+ */
+ explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
+public:
+ SelfType& operator=(const ValueIteratorBase& other);
+
+ SelfType operator++(int) {
+ SelfType temp(*this);
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator--(int) {
+ SelfType temp(*this);
+ --*this;
+ return temp;
+ }
+
+ SelfType& operator--() {
+ decrement();
+ return *this;
+ }
+
+ SelfType& operator++() {
+ increment();
+ return *this;
+ }
+
+ reference operator*() const { return deref(); }
+
+ pointer operator->() const { return &deref(); }
+};
+
+/** \brief Iterator for object and array value.
+ */
+class JSON_API ValueIterator : public ValueIteratorBase {
+ friend class Value;
+
+public:
+ typedef Value value_type;
+ typedef unsigned int size_t;
+ typedef int difference_type;
+ typedef Value& reference;
+ typedef Value* pointer;
+ typedef ValueIterator SelfType;
+
+ ValueIterator();
+ explicit ValueIterator(const ValueConstIterator& other);
+ ValueIterator(const ValueIterator& other);
+
+private:
+/*! \internal Use by Value to create an iterator.
+ */
+ explicit ValueIterator(const Value::ObjectValues::iterator& current);
+public:
+ SelfType& operator=(const SelfType& other);
+
+ SelfType operator++(int) {
+ SelfType temp(*this);
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator--(int) {
+ SelfType temp(*this);
+ --*this;
+ return temp;
+ }
+
+ SelfType& operator--() {
+ decrement();
+ return *this;
+ }
+
+ SelfType& operator++() {
+ increment();
+ return *this;
+ }
+
+ reference operator*() const { return deref(); }
+
+ pointer operator->() const { return &deref(); }
+};
+
+} // namespace Json
+
+
+namespace std {
+/// Specialize std::swap() for Json::Value.
+template<>
+inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }
+}
+
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // CPPTL_JSON_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_READER_H_INCLUDED
+#define CPPTL_JSON_READER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "features.h"
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <deque>
+#include <iosfwd>
+#include <stack>
+#include <string>
+#include <istream>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+namespace Json {
+
+/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
+ *Value.
+ *
+ * \deprecated Use CharReader and CharReaderBuilder.
+ */
+class JSON_API Reader {
+public:
+ typedef char Char;
+ typedef const Char* Location;
+
+ /** \brief An error tagged with where in the JSON text it was encountered.
+ *
+ * The offsets give the [start, limit) range of bytes within the text. Note
+ * that this is bytes, not codepoints.
+ *
+ */
+ struct StructuredError {
+ size_t offset_start;
+ size_t offset_limit;
+ std::string message;
+ };
+
+ /** \brief Constructs a Reader allowing all features
+ * for parsing.
+ */
+ Reader();
+
+ /** \brief Constructs a Reader allowing the specified feature set
+ * for parsing.
+ */
+ Reader(const Features& features);
+
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+ * document.
+ * \param document UTF-8 encoded string containing the document to read.
+ * \param root [out] Contains the root value of the document if it was
+ * successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing them
+ * back during
+ * serialization, \c false to discard comments.
+ * This parameter is ignored if
+ * Features::allowComments_
+ * is \c false.
+ * \return \c true if the document was successfully parsed, \c false if an
+ * error occurred.
+ */
+ bool
+ parse(const std::string& document, Value& root, bool collectComments = true);
+
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+ document.
+ * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
+ document to read.
+ * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+ document to read.
+ * Must be >= beginDoc.
+ * \param root [out] Contains the root value of the document if it was
+ * successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing them
+ back during
+ * serialization, \c false to discard comments.
+ * This parameter is ignored if
+ Features::allowComments_
+ * is \c false.
+ * \return \c true if the document was successfully parsed, \c false if an
+ error occurred.
+ */
+ bool parse(const char* beginDoc,
+ const char* endDoc,
+ Value& root,
+ bool collectComments = true);
+
+ /// \brief Parse from input stream.
+ /// \see Json::operator>>(std::istream&, Json::Value&).
+ bool parse(std::istream& is, Value& root, bool collectComments = true);
+
+ /** \brief Returns a user friendly string that list errors in the parsed
+ * document.
+ * \return Formatted error message with the list of errors with their location
+ * in
+ * the parsed document. An empty string is returned if no error
+ * occurred
+ * during parsing.
+ * \deprecated Use getFormattedErrorMessages() instead (typo fix).
+ */
+ JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
+ std::string getFormatedErrorMessages() const;
+
+ /** \brief Returns a user friendly string that list errors in the parsed
+ * document.
+ * \return Formatted error message with the list of errors with their location
+ * in
+ * the parsed document. An empty string is returned if no error
+ * occurred
+ * during parsing.
+ */
+ std::string getFormattedErrorMessages() const;
+
+ /** \brief Returns a vector of structured erros encounted while parsing.
+ * \return A (possibly empty) vector of StructuredError objects. Currently
+ * only one error can be returned, but the caller should tolerate
+ * multiple
+ * errors. This can occur if the parser recovers from a non-fatal
+ * parse error and then encounters additional errors.
+ */
+ std::vector<StructuredError> getStructuredErrors() const;
+
+ /** \brief Add a semantic error message.
+ * \param value JSON Value location associated with the error
+ * \param message The error message.
+ * \return \c true if the error was successfully added, \c false if the
+ * Value offset exceeds the document size.
+ */
+ bool pushError(const Value& value, const std::string& message);
+
+ /** \brief Add a semantic error message with extra context.
+ * \param value JSON Value location associated with the error
+ * \param message The error message.
+ * \param extra Additional JSON Value location to contextualize the error
+ * \return \c true if the error was successfully added, \c false if either
+ * Value offset exceeds the document size.
+ */
+ bool pushError(const Value& value, const std::string& message, const Value& extra);
+
+ /** \brief Return whether there are any errors.
+ * \return \c true if there are no errors to report \c false if
+ * errors have occurred.
+ */
+ bool good() const;
+
+private:
+ enum TokenType {
+ tokenEndOfStream = 0,
+ tokenObjectBegin,
+ tokenObjectEnd,
+ tokenArrayBegin,
+ tokenArrayEnd,
+ tokenString,
+ tokenNumber,
+ tokenTrue,
+ tokenFalse,
+ tokenNull,
+ tokenArraySeparator,
+ tokenMemberSeparator,
+ tokenComment,
+ tokenError
+ };
+
+ class Token {
+ public:
+ TokenType type_;
+ Location start_;
+ Location end_;
+ };
+
+ class ErrorInfo {
+ public:
+ Token token_;
+ std::string message_;
+ Location extra_;
+ };
+
+ typedef std::deque<ErrorInfo> Errors;
+
+ bool readToken(Token& token);
+ void skipSpaces();
+ bool match(Location pattern, int patternLength);
+ bool readComment();
+ bool readCStyleComment();
+ bool readCppStyleComment();
+ bool readString();
+ void readNumber();
+ bool readValue();
+ bool readObject(Token& token);
+ bool readArray(Token& token);
+ bool decodeNumber(Token& token);
+ bool decodeNumber(Token& token, Value& decoded);
+ bool decodeString(Token& token);
+ bool decodeString(Token& token, std::string& decoded);
+ bool decodeDouble(Token& token);
+ bool decodeDouble(Token& token, Value& decoded);
+ bool decodeUnicodeCodePoint(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode);
+ bool decodeUnicodeEscapeSequence(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode);
+ bool addError(const std::string& message, Token& token, Location extra = 0);
+ bool recoverFromError(TokenType skipUntilToken);
+ bool addErrorAndRecover(const std::string& message,
+ Token& token,
+ TokenType skipUntilToken);
+ void skipUntilSpace();
+ Value& currentValue();
+ Char getNextChar();
+ void
+ getLocationLineAndColumn(Location location, int& line, int& column) const;
+ std::string getLocationLineAndColumn(Location location) const;
+ void addComment(Location begin, Location end, CommentPlacement placement);
+ void skipCommentTokens(Token& token);
+
+ typedef std::stack<Value*> Nodes;
+ Nodes nodes_;
+ Errors errors_;
+ std::string document_;
+ Location begin_;
+ Location end_;
+ Location current_;
+ Location lastValueEnd_;
+ Value* lastValue_;
+ std::string commentsBefore_;
+ Features features_;
+ bool collectComments_;
+}; // Reader
+
+/** Interface for reading JSON from a char array.
+ */
+class JSON_API CharReader {
+public:
+ virtual ~CharReader() {}
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+ document.
+ * The document must be a UTF-8 encoded string containing the document to read.
+ *
+ * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
+ document to read.
+ * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+ document to read.
+ * Must be >= beginDoc.
+ * \param root [out] Contains the root value of the document if it was
+ * successfully parsed.
+ * \param errs [out] Formatted error messages (if not NULL)
+ * a user friendly string that lists errors in the parsed
+ * document.
+ * \return \c true if the document was successfully parsed, \c false if an
+ error occurred.
+ */
+ virtual bool parse(
+ char const* beginDoc, char const* endDoc,
+ Value* root, std::string* errs) = 0;
+
+ class JSON_API Factory {
+ public:
+ virtual ~Factory() {}
+ /** \brief Allocate a CharReader via operator new().
+ * \throw std::exception if something goes wrong (e.g. invalid settings)
+ */
+ virtual CharReader* newCharReader() const = 0;
+ }; // Factory
+}; // CharReader
+
+/** \brief Build a CharReader implementation.
+
+Usage:
+\code
+ using namespace Json;
+ CharReaderBuilder builder;
+ builder["collectComments"] = false;
+ Value value;
+ std::string errs;
+ bool ok = parseFromStream(builder, std::cin, &value, &errs);
+\endcode
+*/
+class JSON_API CharReaderBuilder : public CharReader::Factory {
+public:
+ // Note: We use a Json::Value so that we can add data-members to this class
+ // without a major version bump.
+ /** Configuration of this builder.
+ These are case-sensitive.
+ Available settings (case-sensitive):
+ - `"collectComments": false or true`
+ - true to collect comment and allow writing them
+ back during serialization, false to discard comments.
+ This parameter is ignored if allowComments is false.
+ - `"allowComments": false or true`
+ - true if comments are allowed.
+ - `"strictRoot": false or true`
+ - true if root must be either an array or an object value
+ - `"allowDroppedNullPlaceholders": false or true`
+ - true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
+ - `"allowNumericKeys": false or true`
+ - true if numeric object keys are allowed.
+ - `"allowSingleQuotes": false or true`
+ - true if '' are allowed for strings (both keys and values)
+ - `"stackLimit": integer`
+ - Exceeding stackLimit (recursive depth of `readValue()`) will
+ cause an exception.
+ - This is a security issue (seg-faults caused by deeply nested JSON),
+ so the default is low.
+ - `"failIfExtra": false or true`
+ - If true, `parse()` returns false when extra non-whitespace trails
+ the JSON value in the input string.
+ - `"rejectDupKeys": false or true`
+ - If true, `parse()` returns false when a key is duplicated within an object.
+ - `"allowSpecialFloats": false or true`
+ - If true, special float values (NaNs and infinities) are allowed
+ and their values are lossfree restorable.
+
+ You can examine 'settings_` yourself
+ to see the defaults. You can also write and read them just like any
+ JSON Value.
+ \sa setDefaults()
+ */
+ Json::Value settings_;
+
+ CharReaderBuilder();
+ ~CharReaderBuilder() override;
+
+ CharReader* newCharReader() const override;
+
+ /** \return true if 'settings' are legal and consistent;
+ * otherwise, indicate bad settings via 'invalid'.
+ */
+ bool validate(Json::Value* invalid) const;
+
+ /** A simple way to update a specific setting.
+ */
+ Value& operator[](std::string key);
+
+ /** Called by ctor, but you can use this to reset settings_.
+ * \pre 'settings' != NULL (but Json::null is fine)
+ * \remark Defaults:
+ * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
+ */
+ static void setDefaults(Json::Value* settings);
+ /** Same as old Features::strictMode().
+ * \pre 'settings' != NULL (but Json::null is fine)
+ * \remark Defaults:
+ * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
+ */
+ static void strictMode(Json::Value* settings);
+};
+
+/** Consume entire stream and use its begin/end.
+ * Someday we might have a real StreamReader, but for now this
+ * is convenient.
+ */
+bool JSON_API parseFromStream(
+ CharReader::Factory const&,
+ std::istream&,
+ Value* root, std::string* errs);
+
+/** \brief Read from 'sin' into 'root'.
+
+ Always keep comments from the input JSON.
+
+ This can be used to read a file into a particular sub-object.
+ For example:
+ \code
+ Json::Value root;
+ cin >> root["dir"]["file"];
+ cout << root;
+ \endcode
+ Result:
+ \verbatim
+ {
+ "dir": {
+ "file": {
+ // The input stream JSON would be nested here.
+ }
+ }
+ }
+ \endverbatim
+ \throw std::exception on parse error.
+ \see Json::operator<<()
+*/
+JSON_API std::istream& operator>>(std::istream&, Value&);
+
+} // namespace Json
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // CPPTL_JSON_READER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_WRITER_H_INCLUDED
+#define JSON_WRITER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <vector>
+#include <string>
+#include <ostream>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+namespace Json {
+
+class Value;
+
+/**
+
+Usage:
+\code
+ using namespace Json;
+ void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
+ std::unique_ptr<StreamWriter> const writer(
+ factory.newStreamWriter());
+ writer->write(value, &std::cout);
+ std::cout << std::endl; // add lf and flush
+ }
+\endcode
+*/
+class JSON_API StreamWriter {
+protected:
+ std::ostream* sout_; // not owned; will not delete
+public:
+ StreamWriter();
+ virtual ~StreamWriter();
+ /** Write Value into document as configured in sub-class.
+ Do not take ownership of sout, but maintain a reference during function.
+ \pre sout != NULL
+ \return zero on success (For now, we always return zero, so check the stream instead.)
+ \throw std::exception possibly, depending on configuration
+ */
+ virtual int write(Value const& root, std::ostream* sout) = 0;
+
+ /** \brief A simple abstract factory.
+ */
+ class JSON_API Factory {
+ public:
+ virtual ~Factory();
+ /** \brief Allocate a CharReader via operator new().
+ * \throw std::exception if something goes wrong (e.g. invalid settings)
+ */
+ virtual StreamWriter* newStreamWriter() const = 0;
+ }; // Factory
+}; // StreamWriter
+
+/** \brief Write into stringstream, then return string, for convenience.
+ * A StreamWriter will be created from the factory, used, and then deleted.
+ */
+std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);
+
+
+/** \brief Build a StreamWriter implementation.
+
+Usage:
+\code
+ using namespace Json;
+ Value value = ...;
+ StreamWriterBuilder builder;
+ builder["commentStyle"] = "None";
+ builder["indentation"] = " "; // or whatever you like
+ std::unique_ptr<Json::StreamWriter> writer(
+ builder.newStreamWriter());
+ writer->write(value, &std::cout);
+ std::cout << std::endl; // add lf and flush
+\endcode
+*/
+class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
+public:
+ // Note: We use a Json::Value so that we can add data-members to this class
+ // without a major version bump.
+ /** Configuration of this builder.
+ Available settings (case-sensitive):
+ - "commentStyle": "None" or "All"
+ - "indentation": "<anything>"
+ - "enableYAMLCompatibility": false or true
+ - slightly change the whitespace around colons
+ - "dropNullPlaceholders": false or true
+ - Drop the "null" string from the writer's output for nullValues.
+ Strictly speaking, this is not valid JSON. But when the output is being
+ fed to a browser's Javascript, it makes for smaller output and the
+ browser can handle the output just fine.
+ - "useSpecialFloats": false or true
+ - If true, outputs non-finite floating point values in the following way:
+ NaN values as "NaN", positive infinity as "Infinity", and negative infinity
+ as "-Infinity".
+
+ You can examine 'settings_` yourself
+ to see the defaults. You can also write and read them just like any
+ JSON Value.
+ \sa setDefaults()
+ */
+ Json::Value settings_;
+
+ StreamWriterBuilder();
+ ~StreamWriterBuilder() override;
+
+ /**
+ * \throw std::exception if something goes wrong (e.g. invalid settings)
+ */
+ StreamWriter* newStreamWriter() const override;
+
+ /** \return true if 'settings' are legal and consistent;
+ * otherwise, indicate bad settings via 'invalid'.
+ */
+ bool validate(Json::Value* invalid) const;
+ /** A simple way to update a specific setting.
+ */
+ Value& operator[](std::string key);
+
+ /** Called by ctor, but you can use this to reset settings_.
+ * \pre 'settings' != NULL (but Json::null is fine)
+ * \remark Defaults:
+ * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
+ */
+ static void setDefaults(Json::Value* settings);
+};
+
+/** \brief Abstract class for writers.
+ * \deprecated Use StreamWriter. (And really, this is an implementation detail.)
+ */
+class JSON_API Writer {
+public:
+ virtual ~Writer();
+
+ virtual std::string write(const Value& root) = 0;
+};
+
+/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
+ *without formatting (not human friendly).
+ *
+ * The JSON document is written in a single line. It is not intended for 'human'
+ *consumption,
+ * but may be usefull to support feature such as RPC where bandwith is limited.
+ * \sa Reader, Value
+ * \deprecated Use StreamWriterBuilder.
+ */
+class JSON_API FastWriter : public Writer {
+
+public:
+ FastWriter();
+ ~FastWriter() override {}
+
+ void enableYAMLCompatibility();
+
+ /** \brief Drop the "null" string from the writer's output for nullValues.
+ * Strictly speaking, this is not valid JSON. But when the output is being
+ * fed to a browser's Javascript, it makes for smaller output and the
+ * browser can handle the output just fine.
+ */
+ void dropNullPlaceholders();
+
+ void omitEndingLineFeed();
+
+public: // overridden from Writer
+ std::string write(const Value& root) override;
+
+private:
+ void writeValue(const Value& value);
+
+ std::string document_;
+ bool yamlCompatiblityEnabled_;
+ bool dropNullPlaceholders_;
+ bool omitEndingLineFeed_;
+};
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ *human friendly way.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ * - if empty then print {} without indent and line break
+ * - if not empty the print '{', line break & indent, print one value per
+ *line
+ * and then unindent and line break and print '}'.
+ * - Array value:
+ * - if empty then print [] without indent and line break
+ * - if the array contains no object value, empty array or some other value
+ *types,
+ * and all the values fit on one lines, then print the array on a single
+ *line.
+ * - otherwise, it the values do not fit on one line, or the array contains
+ * object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ *#CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+class JSON_API StyledWriter : public Writer {
+public:
+ StyledWriter();
+ ~StyledWriter() override {}
+
+public: // overridden from Writer
+ /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+ * \param root Value to serialize.
+ * \return String containing the JSON document that represents the root value.
+ */
+ std::string write(const Value& root) override;
+
+private:
+ void writeValue(const Value& value);
+ void writeArrayValue(const Value& value);
+ bool isMultineArray(const Value& value);
+ void pushValue(const std::string& value);
+ void writeIndent();
+ void writeWithIndent(const std::string& value);
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue(const Value& root);
+ void writeCommentAfterValueOnSameLine(const Value& root);
+ bool hasCommentForValue(const Value& value);
+ static std::string normalizeEOL(const std::string& text);
+
+ typedef std::vector<std::string> ChildValues;
+
+ ChildValues childValues_;
+ std::string document_;
+ std::string indentString_;
+ int rightMargin_;
+ int indentSize_;
+ bool addChildValues_;
+};
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ human friendly way,
+ to a stream rather than to a string.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ * - if empty then print {} without indent and line break
+ * - if not empty the print '{', line break & indent, print one value per
+ line
+ * and then unindent and line break and print '}'.
+ * - Array value:
+ * - if empty then print [] without indent and line break
+ * - if the array contains no object value, empty array or some other value
+ types,
+ * and all the values fit on one lines, then print the array on a single
+ line.
+ * - otherwise, it the values do not fit on one line, or the array contains
+ * object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ #CommentPlacement.
+ *
+ * \param indentation Each level will be indented by this amount extra.
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+class JSON_API StyledStreamWriter {
+public:
+ StyledStreamWriter(std::string indentation = "\t");
+ ~StyledStreamWriter() {}
+
+public:
+ /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+ * \param out Stream to write to. (Can be ostringstream, e.g.)
+ * \param root Value to serialize.
+ * \note There is no point in deriving from Writer, since write() should not
+ * return a value.
+ */
+ void write(std::ostream& out, const Value& root);
+
+private:
+ void writeValue(const Value& value);
+ void writeArrayValue(const Value& value);
+ bool isMultineArray(const Value& value);
+ void pushValue(const std::string& value);
+ void writeIndent();
+ void writeWithIndent(const std::string& value);
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue(const Value& root);
+ void writeCommentAfterValueOnSameLine(const Value& root);
+ bool hasCommentForValue(const Value& value);
+ static std::string normalizeEOL(const std::string& text);
+
+ typedef std::vector<std::string> ChildValues;
+
+ ChildValues childValues_;
+ std::ostream* document_;
+ std::string indentString_;
+ int rightMargin_;
+ std::string indentation_;
+ bool addChildValues_ : 1;
+ bool indented_ : 1;
+};
+
+#if defined(JSON_HAS_INT64)
+std::string JSON_API valueToString(Int value);
+std::string JSON_API valueToString(UInt value);
+#endif // if defined(JSON_HAS_INT64)
+std::string JSON_API valueToString(LargestInt value);
+std::string JSON_API valueToString(LargestUInt value);
+std::string JSON_API valueToString(double value);
+std::string JSON_API valueToString(bool value);
+std::string JSON_API valueToQuotedString(const char* value);
+
+/// \brief Output using the StyledStreamWriter.
+/// \see Json::operator>>()
+JSON_API std::ostream& operator<<(std::ostream&, const Value& root);
+
+} // namespace Json
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_WRITER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+#include <stdlib.h>
+#include <sstream>
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+/** It should not be possible for a maliciously designed file to
+ * cause an abort() or seg-fault, so these macros are used only
+ * for pre-condition violations and internal logic errors.
+ */
+#if JSON_USE_EXCEPTION
+
+// @todo <= add detail about condition in exception
+# define JSON_ASSERT(condition) \
+ {if (!(condition)) {Json::throwLogicError( "assert json failed" );}}
+
+# define JSON_FAIL_MESSAGE(message) \
+ { \
+ std::ostringstream oss; oss << message; \
+ Json::throwLogicError(oss.str()); \
+ abort(); \
+ }
+
+#else // JSON_USE_EXCEPTION
+
+# define JSON_ASSERT(condition) assert(condition)
+
+// The call to assert() will show the failure message in debug builds. In
+// release builds we abort, for a core-dump or debugger.
+# define JSON_FAIL_MESSAGE(message) \
+ { \
+ std::ostringstream oss; oss << message; \
+ assert(false && oss.str().c_str()); \
+ abort(); \
+ }
+
+
+#endif
+
+#define JSON_ASSERT_MESSAGE(condition, message) \
+ if (!(condition)) { \
+ JSON_FAIL_MESSAGE(message); \
+ }
+
+#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_AMALGATED_H_INCLUDED
diff --git a/conformance/third_party/jsoncpp/jsoncpp.cpp b/conformance/third_party/jsoncpp/jsoncpp.cpp
new file mode 100644
index 00000000..f803962a
--- /dev/null
+++ b/conformance/third_party/jsoncpp/jsoncpp.cpp
@@ -0,0 +1,5192 @@
+/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation,
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all
+jurisdictions which recognize such a disclaimer. In such jurisdictions,
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this
+software may choose to accept it either as 1) Public Domain, 2) under the
+conditions of the MIT License (see below), or 3) under the terms of dual
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+ http://en.wikipedia.org/wiki/MIT_License
+
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+#include "third_party/jsoncpp/json.h"
+
+#ifndef JSON_IS_AMALGAMATION
+#error "Compile with -I PATH_TO_JSON_DIRECTORY"
+#endif
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+/* This header provides common string manipulation support, such as UTF-8,
+ * portable conversion from/to string...
+ *
+ * It is an internal header that must not be exposed.
+ */
+
+namespace Json {
+
+/// Converts a unicode code-point to UTF-8.
+static inline std::string codePointToUTF8(unsigned int cp) {
+ std::string result;
+
+ // based on description from http://en.wikipedia.org/wiki/UTF-8
+
+ if (cp <= 0x7f) {
+ result.resize(1);
+ result[0] = static_cast<char>(cp);
+ } else if (cp <= 0x7FF) {
+ result.resize(2);
+ result[1] = static_cast<char>(0x80 | (0x3f & cp));
+ result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
+ } else if (cp <= 0xFFFF) {
+ result.resize(3);
+ result[2] = static_cast<char>(0x80 | (0x3f & cp));
+ result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+ result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
+ } else if (cp <= 0x10FFFF) {
+ result.resize(4);
+ result[3] = static_cast<char>(0x80 | (0x3f & cp));
+ result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+ result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
+ result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
+ }
+
+ return result;
+}
+
+/// Returns true if ch is a control character (in range [1,31]).
+static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
+
+enum {
+ /// Constant that specify the size of the buffer that must be passed to
+ /// uintToString.
+ uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
+};
+
+// Defines a char buffer for use with uintToString().
+typedef char UIntToStringBuffer[uintToStringBufferSize];
+
+/** Converts an unsigned integer to string.
+ * @param value Unsigned interger to convert to string
+ * @param current Input/Output string buffer.
+ * Must have at least uintToStringBufferSize chars free.
+ */
+static inline void uintToString(LargestUInt value, char*& current) {
+ *--current = 0;
+ do {
+ *--current = static_cast<signed char>(value % 10U + static_cast<unsigned>('0'));
+ value /= 10;
+ } while (value != 0);
+}
+
+/** Change ',' to '.' everywhere in buffer.
+ *
+ * We had a sophisticated way, but it did not work in WinCE.
+ * @see https://github.com/open-source-parsers/jsoncpp/pull/9
+ */
+static inline void fixNumericLocale(char* begin, char* end) {
+ while (begin < end) {
+ if (*begin == ',') {
+ *begin = '.';
+ }
+ ++begin;
+ }
+}
+
+} // namespace Json {
+
+#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/assertions.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include "json_tool.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <utility>
+#include <cstdio>
+#include <cassert>
+#include <cstring>
+#include <istream>
+#include <sstream>
+#include <memory>
+#include <set>
+#include <limits>
+
+#if defined(_MSC_VER)
+#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
+#define snprintf sprintf_s
+#elif _MSC_VER >= 1900 // VC++ 14.0 and above
+#define snprintf std::snprintf
+#else
+#define snprintf _snprintf
+#endif
+#elif defined(__ANDROID__) || defined(__QNXNTO__)
+#define snprintf snprintf
+#elif __cplusplus >= 201103L
+#define snprintf std::snprintf
+#endif
+
+#if defined(__QNXNTO__)
+#define sscanf std::sscanf
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+
+static int const stackLimit_g = 1000;
+static int stackDepth_g = 0; // see readValue()
+
+namespace Json {
+
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+typedef std::unique_ptr<CharReader> CharReaderPtr;
+#else
+typedef std::auto_ptr<CharReader> CharReaderPtr;
+#endif
+
+// Implementation of class Features
+// ////////////////////////////////
+
+Features::Features()
+ : allowComments_(true), strictRoot_(false),
+ allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
+
+Features Features::all() { return Features(); }
+
+Features Features::strictMode() {
+ Features features;
+ features.allowComments_ = false;
+ features.strictRoot_ = true;
+ features.allowDroppedNullPlaceholders_ = false;
+ features.allowNumericKeys_ = false;
+ return features;
+}
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+static bool containsNewLine(Reader::Location begin, Reader::Location end) {
+ for (; begin < end; ++begin)
+ if (*begin == '\n' || *begin == '\r')
+ return true;
+ return false;
+}
+
+// Class Reader
+// //////////////////////////////////////////////////////////////////
+
+Reader::Reader()
+ : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+ lastValue_(), commentsBefore_(), features_(Features::all()),
+ collectComments_() {}
+
+Reader::Reader(const Features& features)
+ : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+ lastValue_(), commentsBefore_(), features_(features), collectComments_() {
+}
+
+bool
+Reader::parse(const std::string& document, Value& root, bool collectComments) {
+ document_ = document;
+ const char* begin = document_.c_str();
+ const char* end = begin + document_.length();
+ return parse(begin, end, root, collectComments);
+}
+
+bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
+ // std::istream_iterator<char> begin(sin);
+ // std::istream_iterator<char> end;
+ // Those would allow streamed input from a file, if parse() were a
+ // template function.
+
+ // Since std::string is reference-counted, this at least does not
+ // create an extra copy.
+ std::string doc;
+ std::getline(sin, doc, (char)EOF);
+ return parse(doc, root, collectComments);
+}
+
+bool Reader::parse(const char* beginDoc,
+ const char* endDoc,
+ Value& root,
+ bool collectComments) {
+ if (!features_.allowComments_) {
+ collectComments = false;
+ }
+
+ begin_ = beginDoc;
+ end_ = endDoc;
+ collectComments_ = collectComments;
+ current_ = begin_;
+ lastValueEnd_ = 0;
+ lastValue_ = 0;
+ commentsBefore_ = "";
+ errors_.clear();
+ while (!nodes_.empty())
+ nodes_.pop();
+ nodes_.push(&root);
+
+ stackDepth_g = 0; // Yes, this is bad coding, but options are limited.
+ bool successful = readValue();
+ Token token;
+ skipCommentTokens(token);
+ if (collectComments_ && !commentsBefore_.empty())
+ root.setComment(commentsBefore_, commentAfter);
+ if (features_.strictRoot_) {
+ if (!root.isArray() && !root.isObject()) {
+ // Set error location to start of doc, ideally should be first token found
+ // in doc
+ token.type_ = tokenError;
+ token.start_ = beginDoc;
+ token.end_ = endDoc;
+ addError(
+ "A valid JSON document must be either an array or an object value.",
+ token);
+ return false;
+ }
+ }
+ return successful;
+}
+
+bool Reader::readValue() {
+ // This is a non-reentrant way to support a stackLimit. Terrible!
+ // But this deprecated class has a security problem: Bad input can
+ // cause a seg-fault. This seems like a fair, binary-compatible way
+ // to prevent the problem.
+ if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
+ ++stackDepth_g;
+
+ Token token;
+ skipCommentTokens(token);
+ bool successful = true;
+
+ if (collectComments_ && !commentsBefore_.empty()) {
+ currentValue().setComment(commentsBefore_, commentBefore);
+ commentsBefore_ = "";
+ }
+
+ switch (token.type_) {
+ case tokenObjectBegin:
+ successful = readObject(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenArrayBegin:
+ successful = readArray(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenNumber:
+ successful = decodeNumber(token);
+ break;
+ case tokenString:
+ successful = decodeString(token);
+ break;
+ case tokenTrue:
+ {
+ Value v(true);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenFalse:
+ {
+ Value v(false);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenNull:
+ {
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenArraySeparator:
+ case tokenObjectEnd:
+ case tokenArrayEnd:
+ if (features_.allowDroppedNullPlaceholders_) {
+ // "Un-read" the current token and mark the current value as a null
+ // token.
+ current_--;
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(current_ - begin_ - 1);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ } // Else, fall through...
+ default:
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return addError("Syntax error: value, object or array expected.", token);
+ }
+
+ if (collectComments_) {
+ lastValueEnd_ = current_;
+ lastValue_ = &currentValue();
+ }
+
+ --stackDepth_g;
+ return successful;
+}
+
+void Reader::skipCommentTokens(Token& token) {
+ if (features_.allowComments_) {
+ do {
+ readToken(token);
+ } while (token.type_ == tokenComment);
+ } else {
+ readToken(token);
+ }
+}
+
+bool Reader::readToken(Token& token) {
+ skipSpaces();
+ token.start_ = current_;
+ Char c = getNextChar();
+ bool ok = true;
+ switch (c) {
+ case '{':
+ token.type_ = tokenObjectBegin;
+ break;
+ case '}':
+ token.type_ = tokenObjectEnd;
+ break;
+ case '[':
+ token.type_ = tokenArrayBegin;
+ break;
+ case ']':
+ token.type_ = tokenArrayEnd;
+ break;
+ case '"':
+ token.type_ = tokenString;
+ ok = readString();
+ break;
+ case '/':
+ token.type_ = tokenComment;
+ ok = readComment();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ token.type_ = tokenNumber;
+ readNumber();
+ break;
+ case 't':
+ token.type_ = tokenTrue;
+ ok = match("rue", 3);
+ break;
+ case 'f':
+ token.type_ = tokenFalse;
+ ok = match("alse", 4);
+ break;
+ case 'n':
+ token.type_ = tokenNull;
+ ok = match("ull", 3);
+ break;
+ case ',':
+ token.type_ = tokenArraySeparator;
+ break;
+ case ':':
+ token.type_ = tokenMemberSeparator;
+ break;
+ case 0:
+ token.type_ = tokenEndOfStream;
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (!ok)
+ token.type_ = tokenError;
+ token.end_ = current_;
+ return true;
+}
+
+void Reader::skipSpaces() {
+ while (current_ != end_) {
+ Char c = *current_;
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+ ++current_;
+ else
+ break;
+ }
+}
+
+bool Reader::match(Location pattern, int patternLength) {
+ if (end_ - current_ < patternLength)
+ return false;
+ int index = patternLength;
+ while (index--)
+ if (current_[index] != pattern[index])
+ return false;
+ current_ += patternLength;
+ return true;
+}
+
+bool Reader::readComment() {
+ Location commentBegin = current_ - 1;
+ Char c = getNextChar();
+ bool successful = false;
+ if (c == '*')
+ successful = readCStyleComment();
+ else if (c == '/')
+ successful = readCppStyleComment();
+ if (!successful)
+ return false;
+
+ if (collectComments_) {
+ CommentPlacement placement = commentBefore;
+ if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+ if (c != '*' || !containsNewLine(commentBegin, current_))
+ placement = commentAfterOnSameLine;
+ }
+
+ addComment(commentBegin, current_, placement);
+ }
+ return true;
+}
+
+static std::string normalizeEOL(Reader::Location begin, Reader::Location end) {
+ std::string normalized;
+ normalized.reserve(end - begin);
+ Reader::Location current = begin;
+ while (current != end) {
+ char c = *current++;
+ if (c == '\r') {
+ if (current != end && *current == '\n')
+ // convert dos EOL
+ ++current;
+ // convert Mac EOL
+ normalized += '\n';
+ } else {
+ normalized += c;
+ }
+ }
+ return normalized;
+}
+
+void
+Reader::addComment(Location begin, Location end, CommentPlacement placement) {
+ assert(collectComments_);
+ const std::string& normalized = normalizeEOL(begin, end);
+ if (placement == commentAfterOnSameLine) {
+ assert(lastValue_ != 0);
+ lastValue_->setComment(normalized, placement);
+ } else {
+ commentsBefore_ += normalized;
+ }
+}
+
+bool Reader::readCStyleComment() {
+ while (current_ != end_) {
+ Char c = getNextChar();
+ if (c == '*' && *current_ == '/')
+ break;
+ }
+ return getNextChar() == '/';
+}
+
+bool Reader::readCppStyleComment() {
+ while (current_ != end_) {
+ Char c = getNextChar();
+ if (c == '\n')
+ break;
+ if (c == '\r') {
+ // Consume DOS EOL. It will be normalized in addComment.
+ if (current_ != end_ && *current_ == '\n')
+ getNextChar();
+ // Break on Moc OS 9 EOL.
+ break;
+ }
+ }
+ return true;
+}
+
+void Reader::readNumber() {
+ const char *p = current_;
+ char c = '0'; // stopgap for already consumed character
+ // integral part
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ // fractional part
+ if (c == '.') {
+ c = (current_ = p) < end_ ? *p++ : 0;
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ }
+ // exponential part
+ if (c == 'e' || c == 'E') {
+ c = (current_ = p) < end_ ? *p++ : 0;
+ if (c == '+' || c == '-')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ }
+}
+
+bool Reader::readString() {
+ Char c = 0;
+ while (current_ != end_) {
+ c = getNextChar();
+ if (c == '\\')
+ getNextChar();
+ else if (c == '"')
+ break;
+ }
+ return c == '"';
+}
+
+bool Reader::readObject(Token& tokenStart) {
+ Token tokenName;
+ std::string name;
+ Value init(objectValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(tokenStart.start_ - begin_);
+ while (readToken(tokenName)) {
+ bool initialTokenOk = true;
+ while (tokenName.type_ == tokenComment && initialTokenOk)
+ initialTokenOk = readToken(tokenName);
+ if (!initialTokenOk)
+ break;
+ if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+ return true;
+ name = "";
+ if (tokenName.type_ == tokenString) {
+ if (!decodeString(tokenName, name))
+ return recoverFromError(tokenObjectEnd);
+ } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+ Value numberName;
+ if (!decodeNumber(tokenName, numberName))
+ return recoverFromError(tokenObjectEnd);
+ name = numberName.asString();
+ } else {
+ break;
+ }
+
+ Token colon;
+ if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+ return addErrorAndRecover(
+ "Missing ':' after object member name", colon, tokenObjectEnd);
+ }
+ Value& value = currentValue()[name];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenObjectEnd);
+
+ Token comma;
+ if (!readToken(comma) ||
+ (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+ comma.type_ != tokenComment)) {
+ return addErrorAndRecover(
+ "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+ }
+ bool finalizeTokenOk = true;
+ while (comma.type_ == tokenComment && finalizeTokenOk)
+ finalizeTokenOk = readToken(comma);
+ if (comma.type_ == tokenObjectEnd)
+ return true;
+ }
+ return addErrorAndRecover(
+ "Missing '}' or object member name", tokenName, tokenObjectEnd);
+}
+
+bool Reader::readArray(Token& tokenStart) {
+ Value init(arrayValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(tokenStart.start_ - begin_);
+ skipSpaces();
+ if (*current_ == ']') // empty array
+ {
+ Token endArray;
+ readToken(endArray);
+ return true;
+ }
+ int index = 0;
+ for (;;) {
+ Value& value = currentValue()[index++];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenArrayEnd);
+
+ Token token;
+ // Accept Comment after last item in the array.
+ ok = readToken(token);
+ while (token.type_ == tokenComment && ok) {
+ ok = readToken(token);
+ }
+ bool badTokenType =
+ (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+ if (!ok || badTokenType) {
+ return addErrorAndRecover(
+ "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+ }
+ if (token.type_ == tokenArrayEnd)
+ break;
+ }
+ return true;
+}
+
+bool Reader::decodeNumber(Token& token) {
+ Value decoded;
+ if (!decodeNumber(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool Reader::decodeNumber(Token& token, Value& decoded) {
+ // Attempts to parse the number as an integer. If the number is
+ // larger than the maximum supported value of an integer then
+ // we decode the number as a double.
+ Location current = token.start_;
+ bool isNegative = *current == '-';
+ if (isNegative)
+ ++current;
+ // TODO: Help the compiler do the div and mod at compile time or get rid of them.
+ Value::LargestUInt maxIntegerValue =
+ isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
+ : Value::maxLargestUInt;
+ Value::LargestUInt threshold = maxIntegerValue / 10;
+ Value::LargestUInt value = 0;
+ while (current < token.end_) {
+ Char c = *current++;
+ if (c < '0' || c > '9')
+ return decodeDouble(token, decoded);
+ Value::UInt digit(c - '0');
+ if (value >= threshold) {
+ // We've hit or exceeded the max value divided by 10 (rounded down). If
+ // a) we've only just touched the limit, b) this is the last digit, and
+ // c) it's small enough to fit in that rounding delta, we're okay.
+ // Otherwise treat this number as a double to avoid overflow.
+ if (value > threshold || current != token.end_ ||
+ digit > maxIntegerValue % 10) {
+ return decodeDouble(token, decoded);
+ }
+ }
+ value = value * 10 + digit;
+ }
+ if (isNegative && value == maxIntegerValue)
+ decoded = Value::minLargestInt;
+ else if (isNegative)
+ decoded = -Value::LargestInt(value);
+ else if (value <= Value::LargestUInt(Value::maxInt))
+ decoded = Value::LargestInt(value);
+ else
+ decoded = value;
+ return true;
+}
+
+bool Reader::decodeDouble(Token& token) {
+ Value decoded;
+ if (!decodeDouble(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool Reader::decodeDouble(Token& token, Value& decoded) {
+ double value = 0;
+ std::string buffer(token.start_, token.end_);
+ std::istringstream is(buffer);
+ if (!(is >> value))
+ return addError("'" + std::string(token.start_, token.end_) +
+ "' is not a number.",
+ token);
+ decoded = value;
+ return true;
+}
+
+bool Reader::decodeString(Token& token) {
+ std::string decoded_string;
+ if (!decodeString(token, decoded_string))
+ return false;
+ Value decoded(decoded_string);
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool Reader::decodeString(Token& token, std::string& decoded) {
+ decoded.reserve(token.end_ - token.start_ - 2);
+ Location current = token.start_ + 1; // skip '"'
+ Location end = token.end_ - 1; // do not include '"'
+ while (current != end) {
+ Char c = *current++;
+ if (c == '"')
+ break;
+ else if (c == '\\') {
+ if (current == end)
+ return addError("Empty escape sequence in string", token, current);
+ Char escape = *current++;
+ switch (escape) {
+ case '"':
+ decoded += '"';
+ break;
+ case '/':
+ decoded += '/';
+ break;
+ case '\\':
+ decoded += '\\';
+ break;
+ case 'b':
+ decoded += '\b';
+ break;
+ case 'f':
+ decoded += '\f';
+ break;
+ case 'n':
+ decoded += '\n';
+ break;
+ case 'r':
+ decoded += '\r';
+ break;
+ case 't':
+ decoded += '\t';
+ break;
+ case 'u': {
+ unsigned int unicode;
+ if (!decodeUnicodeCodePoint(token, current, end, unicode))
+ return false;
+ decoded += codePointToUTF8(unicode);
+ } break;
+ default:
+ return addError("Bad escape sequence in string", token, current);
+ }
+ } else {
+ decoded += c;
+ }
+ }
+ return true;
+}
+
+bool Reader::decodeUnicodeCodePoint(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode) {
+
+ if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+ return false;
+ if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+ // surrogate pairs
+ if (end - current < 6)
+ return addError(
+ "additional six characters expected to parse unicode surrogate pair.",
+ token,
+ current);
+ unsigned int surrogatePair;
+ if (*(current++) == '\\' && *(current++) == 'u') {
+ if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+ unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+ } else
+ return false;
+ } else
+ return addError("expecting another \\u token to begin the second half of "
+ "a unicode surrogate pair",
+ token,
+ current);
+ }
+ return true;
+}
+
+bool Reader::decodeUnicodeEscapeSequence(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode) {
+ if (end - current < 4)
+ return addError(
+ "Bad unicode escape sequence in string: four digits expected.",
+ token,
+ current);
+ unicode = 0;
+ for (int index = 0; index < 4; ++index) {
+ Char c = *current++;
+ unicode *= 16;
+ if (c >= '0' && c <= '9')
+ unicode += c - '0';
+ else if (c >= 'a' && c <= 'f')
+ unicode += c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ unicode += c - 'A' + 10;
+ else
+ return addError(
+ "Bad unicode escape sequence in string: hexadecimal digit expected.",
+ token,
+ current);
+ }
+ return true;
+}
+
+bool
+Reader::addError(const std::string& message, Token& token, Location extra) {
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = extra;
+ errors_.push_back(info);
+ return false;
+}
+
+bool Reader::recoverFromError(TokenType skipUntilToken) {
+ int errorCount = int(errors_.size());
+ Token skip;
+ for (;;) {
+ if (!readToken(skip))
+ errors_.resize(errorCount); // discard errors caused by recovery
+ if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+ break;
+ }
+ errors_.resize(errorCount);
+ return false;
+}
+
+bool Reader::addErrorAndRecover(const std::string& message,
+ Token& token,
+ TokenType skipUntilToken) {
+ addError(message, token);
+ return recoverFromError(skipUntilToken);
+}
+
+Value& Reader::currentValue() { return *(nodes_.top()); }
+
+Reader::Char Reader::getNextChar() {
+ if (current_ == end_)
+ return 0;
+ return *current_++;
+}
+
+void Reader::getLocationLineAndColumn(Location location,
+ int& line,
+ int& column) const {
+ Location current = begin_;
+ Location lastLineStart = current;
+ line = 0;
+ while (current < location && current != end_) {
+ Char c = *current++;
+ if (c == '\r') {
+ if (*current == '\n')
+ ++current;
+ lastLineStart = current;
+ ++line;
+ } else if (c == '\n') {
+ lastLineStart = current;
+ ++line;
+ }
+ }
+ // column & line start at 1
+ column = int(location - lastLineStart) + 1;
+ ++line;
+}
+
+std::string Reader::getLocationLineAndColumn(Location location) const {
+ int line, column;
+ getLocationLineAndColumn(location, line, column);
+ char buffer[18 + 16 + 16 + 1];
+ snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+ return buffer;
+}
+
+// Deprecated. Preserved for backward compatibility
+std::string Reader::getFormatedErrorMessages() const {
+ return getFormattedErrorMessages();
+}
+
+std::string Reader::getFormattedErrorMessages() const {
+ std::string formattedMessage;
+ for (Errors::const_iterator itError = errors_.begin();
+ itError != errors_.end();
+ ++itError) {
+ const ErrorInfo& error = *itError;
+ formattedMessage +=
+ "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+ formattedMessage += " " + error.message_ + "\n";
+ if (error.extra_)
+ formattedMessage +=
+ "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+ }
+ return formattedMessage;
+}
+
+std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
+ std::vector<Reader::StructuredError> allErrors;
+ for (Errors::const_iterator itError = errors_.begin();
+ itError != errors_.end();
+ ++itError) {
+ const ErrorInfo& error = *itError;
+ Reader::StructuredError structured;
+ structured.offset_start = error.token_.start_ - begin_;
+ structured.offset_limit = error.token_.end_ - begin_;
+ structured.message = error.message_;
+ allErrors.push_back(structured);
+ }
+ return allErrors;
+}
+
+bool Reader::pushError(const Value& value, const std::string& message) {
+ size_t length = end_ - begin_;
+ if(value.getOffsetStart() > length
+ || value.getOffsetLimit() > length)
+ return false;
+ Token token;
+ token.type_ = tokenError;
+ token.start_ = begin_ + value.getOffsetStart();
+ token.end_ = end_ + value.getOffsetLimit();
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = 0;
+ errors_.push_back(info);
+ return true;
+}
+
+bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) {
+ size_t length = end_ - begin_;
+ if(value.getOffsetStart() > length
+ || value.getOffsetLimit() > length
+ || extra.getOffsetLimit() > length)
+ return false;
+ Token token;
+ token.type_ = tokenError;
+ token.start_ = begin_ + value.getOffsetStart();
+ token.end_ = begin_ + value.getOffsetLimit();
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = begin_ + extra.getOffsetStart();
+ errors_.push_back(info);
+ return true;
+}
+
+bool Reader::good() const {
+ return !errors_.size();
+}
+
+// exact copy of Features
+class OurFeatures {
+public:
+ static OurFeatures all();
+ bool allowComments_;
+ bool strictRoot_;
+ bool allowDroppedNullPlaceholders_;
+ bool allowNumericKeys_;
+ bool allowSingleQuotes_;
+ bool failIfExtra_;
+ bool rejectDupKeys_;
+ bool allowSpecialFloats_;
+ int stackLimit_;
+}; // OurFeatures
+
+// exact copy of Implementation of class Features
+// ////////////////////////////////
+
+OurFeatures OurFeatures::all() { return OurFeatures(); }
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+// exact copy of Reader, renamed to OurReader
+class OurReader {
+public:
+ typedef char Char;
+ typedef const Char* Location;
+ struct StructuredError {
+ size_t offset_start;
+ size_t offset_limit;
+ std::string message;
+ };
+
+ OurReader(OurFeatures const& features);
+ bool parse(const char* beginDoc,
+ const char* endDoc,
+ Value& root,
+ bool collectComments = true);
+ std::string getFormattedErrorMessages() const;
+ std::vector<StructuredError> getStructuredErrors() const;
+ bool pushError(const Value& value, const std::string& message);
+ bool pushError(const Value& value, const std::string& message, const Value& extra);
+ bool good() const;
+
+private:
+ OurReader(OurReader const&); // no impl
+ void operator=(OurReader const&); // no impl
+
+ enum TokenType {
+ tokenEndOfStream = 0,
+ tokenObjectBegin,
+ tokenObjectEnd,
+ tokenArrayBegin,
+ tokenArrayEnd,
+ tokenString,
+ tokenNumber,
+ tokenTrue,
+ tokenFalse,
+ tokenNull,
+ tokenNaN,
+ tokenPosInf,
+ tokenNegInf,
+ tokenArraySeparator,
+ tokenMemberSeparator,
+ tokenComment,
+ tokenError
+ };
+
+ class Token {
+ public:
+ TokenType type_;
+ Location start_;
+ Location end_;
+ };
+
+ class ErrorInfo {
+ public:
+ Token token_;
+ std::string message_;
+ Location extra_;
+ };
+
+ typedef std::deque<ErrorInfo> Errors;
+
+ bool readToken(Token& token);
+ void skipSpaces();
+ bool match(Location pattern, int patternLength);
+ bool readComment();
+ bool readCStyleComment();
+ bool readCppStyleComment();
+ bool readString();
+ bool readStringSingleQuote();
+ bool readNumber(bool checkInf);
+ bool readValue();
+ bool readObject(Token& token);
+ bool readArray(Token& token);
+ bool decodeNumber(Token& token);
+ bool decodeNumber(Token& token, Value& decoded);
+ bool decodeString(Token& token);
+ bool decodeString(Token& token, std::string& decoded);
+ bool decodeDouble(Token& token);
+ bool decodeDouble(Token& token, Value& decoded);
+ bool decodeUnicodeCodePoint(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode);
+ bool decodeUnicodeEscapeSequence(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode);
+ bool addError(const std::string& message, Token& token, Location extra = 0);
+ bool recoverFromError(TokenType skipUntilToken);
+ bool addErrorAndRecover(const std::string& message,
+ Token& token,
+ TokenType skipUntilToken);
+ void skipUntilSpace();
+ Value& currentValue();
+ Char getNextChar();
+ void
+ getLocationLineAndColumn(Location location, int& line, int& column) const;
+ std::string getLocationLineAndColumn(Location location) const;
+ void addComment(Location begin, Location end, CommentPlacement placement);
+ void skipCommentTokens(Token& token);
+
+ typedef std::stack<Value*> Nodes;
+ Nodes nodes_;
+ Errors errors_;
+ std::string document_;
+ Location begin_;
+ Location end_;
+ Location current_;
+ Location lastValueEnd_;
+ Value* lastValue_;
+ std::string commentsBefore_;
+ int stackDepth_;
+
+ OurFeatures const features_;
+ bool collectComments_;
+}; // OurReader
+
+// complete copy of Read impl, for OurReader
+
+OurReader::OurReader(OurFeatures const& features)
+ : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+ lastValue_(), commentsBefore_(),
+ stackDepth_(0),
+ features_(features), collectComments_() {
+}
+
+bool OurReader::parse(const char* beginDoc,
+ const char* endDoc,
+ Value& root,
+ bool collectComments) {
+ if (!features_.allowComments_) {
+ collectComments = false;
+ }
+
+ begin_ = beginDoc;
+ end_ = endDoc;
+ collectComments_ = collectComments;
+ current_ = begin_;
+ lastValueEnd_ = 0;
+ lastValue_ = 0;
+ commentsBefore_ = "";
+ errors_.clear();
+ while (!nodes_.empty())
+ nodes_.pop();
+ nodes_.push(&root);
+
+ stackDepth_ = 0;
+ bool successful = readValue();
+ Token token;
+ skipCommentTokens(token);
+ if (features_.failIfExtra_) {
+ if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {
+ addError("Extra non-whitespace after JSON value.", token);
+ return false;
+ }
+ }
+ if (collectComments_ && !commentsBefore_.empty())
+ root.setComment(commentsBefore_, commentAfter);
+ if (features_.strictRoot_) {
+ if (!root.isArray() && !root.isObject()) {
+ // Set error location to start of doc, ideally should be first token found
+ // in doc
+ token.type_ = tokenError;
+ token.start_ = beginDoc;
+ token.end_ = endDoc;
+ addError(
+ "A valid JSON document must be either an array or an object value.",
+ token);
+ return false;
+ }
+ }
+ return successful;
+}
+
+bool OurReader::readValue() {
+ if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
+ ++stackDepth_;
+ Token token;
+ skipCommentTokens(token);
+ bool successful = true;
+
+ if (collectComments_ && !commentsBefore_.empty()) {
+ currentValue().setComment(commentsBefore_, commentBefore);
+ commentsBefore_ = "";
+ }
+
+ switch (token.type_) {
+ case tokenObjectBegin:
+ successful = readObject(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenArrayBegin:
+ successful = readArray(token);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ case tokenNumber:
+ successful = decodeNumber(token);
+ break;
+ case tokenString:
+ successful = decodeString(token);
+ break;
+ case tokenTrue:
+ {
+ Value v(true);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenFalse:
+ {
+ Value v(false);
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenNull:
+ {
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenNaN:
+ {
+ Value v(std::numeric_limits<double>::quiet_NaN());
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenPosInf:
+ {
+ Value v(std::numeric_limits<double>::infinity());
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenNegInf:
+ {
+ Value v(-std::numeric_limits<double>::infinity());
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ }
+ break;
+ case tokenArraySeparator:
+ case tokenObjectEnd:
+ case tokenArrayEnd:
+ if (features_.allowDroppedNullPlaceholders_) {
+ // "Un-read" the current token and mark the current value as a null
+ // token.
+ current_--;
+ Value v;
+ currentValue().swapPayload(v);
+ currentValue().setOffsetStart(current_ - begin_ - 1);
+ currentValue().setOffsetLimit(current_ - begin_);
+ break;
+ } // else, fall through ...
+ default:
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return addError("Syntax error: value, object or array expected.", token);
+ }
+
+ if (collectComments_) {
+ lastValueEnd_ = current_;
+ lastValue_ = &currentValue();
+ }
+
+ --stackDepth_;
+ return successful;
+}
+
+void OurReader::skipCommentTokens(Token& token) {
+ if (features_.allowComments_) {
+ do {
+ readToken(token);
+ } while (token.type_ == tokenComment);
+ } else {
+ readToken(token);
+ }
+}
+
+bool OurReader::readToken(Token& token) {
+ skipSpaces();
+ token.start_ = current_;
+ Char c = getNextChar();
+ bool ok = true;
+ switch (c) {
+ case '{':
+ token.type_ = tokenObjectBegin;
+ break;
+ case '}':
+ token.type_ = tokenObjectEnd;
+ break;
+ case '[':
+ token.type_ = tokenArrayBegin;
+ break;
+ case ']':
+ token.type_ = tokenArrayEnd;
+ break;
+ case '"':
+ token.type_ = tokenString;
+ ok = readString();
+ break;
+ case '\'':
+ if (features_.allowSingleQuotes_) {
+ token.type_ = tokenString;
+ ok = readStringSingleQuote();
+ break;
+ } // else continue
+ case '/':
+ token.type_ = tokenComment;
+ ok = readComment();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ token.type_ = tokenNumber;
+ readNumber(false);
+ break;
+ case '-':
+ if (readNumber(true)) {
+ token.type_ = tokenNumber;
+ } else {
+ token.type_ = tokenNegInf;
+ ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+ }
+ break;
+ case 't':
+ token.type_ = tokenTrue;
+ ok = match("rue", 3);
+ break;
+ case 'f':
+ token.type_ = tokenFalse;
+ ok = match("alse", 4);
+ break;
+ case 'n':
+ token.type_ = tokenNull;
+ ok = match("ull", 3);
+ break;
+ case 'N':
+ if (features_.allowSpecialFloats_) {
+ token.type_ = tokenNaN;
+ ok = match("aN", 2);
+ } else {
+ ok = false;
+ }
+ break;
+ case 'I':
+ if (features_.allowSpecialFloats_) {
+ token.type_ = tokenPosInf;
+ ok = match("nfinity", 7);
+ } else {
+ ok = false;
+ }
+ break;
+ case ',':
+ token.type_ = tokenArraySeparator;
+ break;
+ case ':':
+ token.type_ = tokenMemberSeparator;
+ break;
+ case 0:
+ token.type_ = tokenEndOfStream;
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (!ok)
+ token.type_ = tokenError;
+ token.end_ = current_;
+ return true;
+}
+
+void OurReader::skipSpaces() {
+ while (current_ != end_) {
+ Char c = *current_;
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+ ++current_;
+ else
+ break;
+ }
+}
+
+bool OurReader::match(Location pattern, int patternLength) {
+ if (end_ - current_ < patternLength)
+ return false;
+ int index = patternLength;
+ while (index--)
+ if (current_[index] != pattern[index])
+ return false;
+ current_ += patternLength;
+ return true;
+}
+
+bool OurReader::readComment() {
+ Location commentBegin = current_ - 1;
+ Char c = getNextChar();
+ bool successful = false;
+ if (c == '*')
+ successful = readCStyleComment();
+ else if (c == '/')
+ successful = readCppStyleComment();
+ if (!successful)
+ return false;
+
+ if (collectComments_) {
+ CommentPlacement placement = commentBefore;
+ if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+ if (c != '*' || !containsNewLine(commentBegin, current_))
+ placement = commentAfterOnSameLine;
+ }
+
+ addComment(commentBegin, current_, placement);
+ }
+ return true;
+}
+
+void
+OurReader::addComment(Location begin, Location end, CommentPlacement placement) {
+ assert(collectComments_);
+ const std::string& normalized = normalizeEOL(begin, end);
+ if (placement == commentAfterOnSameLine) {
+ assert(lastValue_ != 0);
+ lastValue_->setComment(normalized, placement);
+ } else {
+ commentsBefore_ += normalized;
+ }
+}
+
+bool OurReader::readCStyleComment() {
+ while (current_ != end_) {
+ Char c = getNextChar();
+ if (c == '*' && *current_ == '/')
+ break;
+ }
+ return getNextChar() == '/';
+}
+
+bool OurReader::readCppStyleComment() {
+ while (current_ != end_) {
+ Char c = getNextChar();
+ if (c == '\n')
+ break;
+ if (c == '\r') {
+ // Consume DOS EOL. It will be normalized in addComment.
+ if (current_ != end_ && *current_ == '\n')
+ getNextChar();
+ // Break on Moc OS 9 EOL.
+ break;
+ }
+ }
+ return true;
+}
+
+bool OurReader::readNumber(bool checkInf) {
+ const char *p = current_;
+ if (checkInf && p != end_ && *p == 'I') {
+ current_ = ++p;
+ return false;
+ }
+ char c = '0'; // stopgap for already consumed character
+ // integral part
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ // fractional part
+ if (c == '.') {
+ c = (current_ = p) < end_ ? *p++ : 0;
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ }
+ // exponential part
+ if (c == 'e' || c == 'E') {
+ c = (current_ = p) < end_ ? *p++ : 0;
+ if (c == '+' || c == '-')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ while (c >= '0' && c <= '9')
+ c = (current_ = p) < end_ ? *p++ : 0;
+ }
+ return true;
+}
+bool OurReader::readString() {
+ Char c = 0;
+ while (current_ != end_) {
+ c = getNextChar();
+ if (c == '\\')
+ getNextChar();
+ else if (c == '"')
+ break;
+ }
+ return c == '"';
+}
+
+
+bool OurReader::readStringSingleQuote() {
+ Char c = 0;
+ while (current_ != end_) {
+ c = getNextChar();
+ if (c == '\\')
+ getNextChar();
+ else if (c == '\'')
+ break;
+ }
+ return c == '\'';
+}
+
+bool OurReader::readObject(Token& tokenStart) {
+ Token tokenName;
+ std::string name;
+ Value init(objectValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(tokenStart.start_ - begin_);
+ while (readToken(tokenName)) {
+ bool initialTokenOk = true;
+ while (tokenName.type_ == tokenComment && initialTokenOk)
+ initialTokenOk = readToken(tokenName);
+ if (!initialTokenOk)
+ break;
+ if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+ return true;
+ name = "";
+ if (tokenName.type_ == tokenString) {
+ if (!decodeString(tokenName, name))
+ return recoverFromError(tokenObjectEnd);
+ } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+ Value numberName;
+ if (!decodeNumber(tokenName, numberName))
+ return recoverFromError(tokenObjectEnd);
+ name = numberName.asString();
+ } else {
+ break;
+ }
+
+ Token colon;
+ if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+ return addErrorAndRecover(
+ "Missing ':' after object member name", colon, tokenObjectEnd);
+ }
+ if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
+ if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
+ std::string msg = "Duplicate key: '" + name + "'";
+ return addErrorAndRecover(
+ msg, tokenName, tokenObjectEnd);
+ }
+ Value& value = currentValue()[name];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenObjectEnd);
+
+ Token comma;
+ if (!readToken(comma) ||
+ (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+ comma.type_ != tokenComment)) {
+ return addErrorAndRecover(
+ "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+ }
+ bool finalizeTokenOk = true;
+ while (comma.type_ == tokenComment && finalizeTokenOk)
+ finalizeTokenOk = readToken(comma);
+ if (comma.type_ == tokenObjectEnd)
+ return true;
+ }
+ return addErrorAndRecover(
+ "Missing '}' or object member name", tokenName, tokenObjectEnd);
+}
+
+bool OurReader::readArray(Token& tokenStart) {
+ Value init(arrayValue);
+ currentValue().swapPayload(init);
+ currentValue().setOffsetStart(tokenStart.start_ - begin_);
+ skipSpaces();
+ if (*current_ == ']') // empty array
+ {
+ Token endArray;
+ readToken(endArray);
+ return true;
+ }
+ int index = 0;
+ for (;;) {
+ Value& value = currentValue()[index++];
+ nodes_.push(&value);
+ bool ok = readValue();
+ nodes_.pop();
+ if (!ok) // error already set
+ return recoverFromError(tokenArrayEnd);
+
+ Token token;
+ // Accept Comment after last item in the array.
+ ok = readToken(token);
+ while (token.type_ == tokenComment && ok) {
+ ok = readToken(token);
+ }
+ bool badTokenType =
+ (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+ if (!ok || badTokenType) {
+ return addErrorAndRecover(
+ "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+ }
+ if (token.type_ == tokenArrayEnd)
+ break;
+ }
+ return true;
+}
+
+bool OurReader::decodeNumber(Token& token) {
+ Value decoded;
+ if (!decodeNumber(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool OurReader::decodeNumber(Token& token, Value& decoded) {
+ // Attempts to parse the number as an integer. If the number is
+ // larger than the maximum supported value of an integer then
+ // we decode the number as a double.
+ Location current = token.start_;
+ bool isNegative = *current == '-';
+ if (isNegative)
+ ++current;
+ // TODO: Help the compiler do the div and mod at compile time or get rid of them.
+ Value::LargestUInt maxIntegerValue =
+ isNegative ? Value::LargestUInt(-Value::minLargestInt)
+ : Value::maxLargestUInt;
+ Value::LargestUInt threshold = maxIntegerValue / 10;
+ Value::LargestUInt value = 0;
+ while (current < token.end_) {
+ Char c = *current++;
+ if (c < '0' || c > '9')
+ return decodeDouble(token, decoded);
+ Value::UInt digit(c - '0');
+ if (value >= threshold) {
+ // We've hit or exceeded the max value divided by 10 (rounded down). If
+ // a) we've only just touched the limit, b) this is the last digit, and
+ // c) it's small enough to fit in that rounding delta, we're okay.
+ // Otherwise treat this number as a double to avoid overflow.
+ if (value > threshold || current != token.end_ ||
+ digit > maxIntegerValue % 10) {
+ return decodeDouble(token, decoded);
+ }
+ }
+ value = value * 10 + digit;
+ }
+ if (isNegative)
+ decoded = -Value::LargestInt(value);
+ else if (value <= Value::LargestUInt(Value::maxInt))
+ decoded = Value::LargestInt(value);
+ else
+ decoded = value;
+ return true;
+}
+
+bool OurReader::decodeDouble(Token& token) {
+ Value decoded;
+ if (!decodeDouble(token, decoded))
+ return false;
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool OurReader::decodeDouble(Token& token, Value& decoded) {
+ double value = 0;
+ const int bufferSize = 32;
+ int count;
+ int length = int(token.end_ - token.start_);
+
+ // Sanity check to avoid buffer overflow exploits.
+ if (length < 0) {
+ return addError("Unable to parse token length", token);
+ }
+
+ // Avoid using a string constant for the format control string given to
+ // sscanf, as this can cause hard to debug crashes on OS X. See here for more
+ // info:
+ //
+ // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
+ char format[] = "%lf";
+
+ if (length <= bufferSize) {
+ Char buffer[bufferSize + 1];
+ memcpy(buffer, token.start_, length);
+ buffer[length] = 0;
+ count = sscanf(buffer, format, &value);
+ } else {
+ std::string buffer(token.start_, token.end_);
+ count = sscanf(buffer.c_str(), format, &value);
+ }
+
+ if (count != 1)
+ return addError("'" + std::string(token.start_, token.end_) +
+ "' is not a number.",
+ token);
+ decoded = value;
+ return true;
+}
+
+bool OurReader::decodeString(Token& token) {
+ std::string decoded_string;
+ if (!decodeString(token, decoded_string))
+ return false;
+ Value decoded(decoded_string);
+ currentValue().swapPayload(decoded);
+ currentValue().setOffsetStart(token.start_ - begin_);
+ currentValue().setOffsetLimit(token.end_ - begin_);
+ return true;
+}
+
+bool OurReader::decodeString(Token& token, std::string& decoded) {
+ decoded.reserve(token.end_ - token.start_ - 2);
+ Location current = token.start_ + 1; // skip '"'
+ Location end = token.end_ - 1; // do not include '"'
+ while (current != end) {
+ Char c = *current++;
+ if (c == '"')
+ break;
+ else if (c == '\\') {
+ if (current == end)
+ return addError("Empty escape sequence in string", token, current);
+ Char escape = *current++;
+ switch (escape) {
+ case '"':
+ decoded += '"';
+ break;
+ case '/':
+ decoded += '/';
+ break;
+ case '\\':
+ decoded += '\\';
+ break;
+ case 'b':
+ decoded += '\b';
+ break;
+ case 'f':
+ decoded += '\f';
+ break;
+ case 'n':
+ decoded += '\n';
+ break;
+ case 'r':
+ decoded += '\r';
+ break;
+ case 't':
+ decoded += '\t';
+ break;
+ case 'u': {
+ unsigned int unicode;
+ if (!decodeUnicodeCodePoint(token, current, end, unicode))
+ return false;
+ decoded += codePointToUTF8(unicode);
+ } break;
+ default:
+ return addError("Bad escape sequence in string", token, current);
+ }
+ } else {
+ decoded += c;
+ }
+ }
+ return true;
+}
+
+bool OurReader::decodeUnicodeCodePoint(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode) {
+
+ if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+ return false;
+ if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+ // surrogate pairs
+ if (end - current < 6)
+ return addError(
+ "additional six characters expected to parse unicode surrogate pair.",
+ token,
+ current);
+ unsigned int surrogatePair;
+ if (*(current++) == '\\' && *(current++) == 'u') {
+ if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+ unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+ } else
+ return false;
+ } else
+ return addError("expecting another \\u token to begin the second half of "
+ "a unicode surrogate pair",
+ token,
+ current);
+ }
+ return true;
+}
+
+bool OurReader::decodeUnicodeEscapeSequence(Token& token,
+ Location& current,
+ Location end,
+ unsigned int& unicode) {
+ if (end - current < 4)
+ return addError(
+ "Bad unicode escape sequence in string: four digits expected.",
+ token,
+ current);
+ unicode = 0;
+ for (int index = 0; index < 4; ++index) {
+ Char c = *current++;
+ unicode *= 16;
+ if (c >= '0' && c <= '9')
+ unicode += c - '0';
+ else if (c >= 'a' && c <= 'f')
+ unicode += c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ unicode += c - 'A' + 10;
+ else
+ return addError(
+ "Bad unicode escape sequence in string: hexadecimal digit expected.",
+ token,
+ current);
+ }
+ return true;
+}
+
+bool
+OurReader::addError(const std::string& message, Token& token, Location extra) {
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = extra;
+ errors_.push_back(info);
+ return false;
+}
+
+bool OurReader::recoverFromError(TokenType skipUntilToken) {
+ int errorCount = int(errors_.size());
+ Token skip;
+ for (;;) {
+ if (!readToken(skip))
+ errors_.resize(errorCount); // discard errors caused by recovery
+ if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+ break;
+ }
+ errors_.resize(errorCount);
+ return false;
+}
+
+bool OurReader::addErrorAndRecover(const std::string& message,
+ Token& token,
+ TokenType skipUntilToken) {
+ addError(message, token);
+ return recoverFromError(skipUntilToken);
+}
+
+Value& OurReader::currentValue() { return *(nodes_.top()); }
+
+OurReader::Char OurReader::getNextChar() {
+ if (current_ == end_)
+ return 0;
+ return *current_++;
+}
+
+void OurReader::getLocationLineAndColumn(Location location,
+ int& line,
+ int& column) const {
+ Location current = begin_;
+ Location lastLineStart = current;
+ line = 0;
+ while (current < location && current != end_) {
+ Char c = *current++;
+ if (c == '\r') {
+ if (*current == '\n')
+ ++current;
+ lastLineStart = current;
+ ++line;
+ } else if (c == '\n') {
+ lastLineStart = current;
+ ++line;
+ }
+ }
+ // column & line start at 1
+ column = int(location - lastLineStart) + 1;
+ ++line;
+}
+
+std::string OurReader::getLocationLineAndColumn(Location location) const {
+ int line, column;
+ getLocationLineAndColumn(location, line, column);
+ char buffer[18 + 16 + 16 + 1];
+ snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+ return buffer;
+}
+
+std::string OurReader::getFormattedErrorMessages() const {
+ std::string formattedMessage;
+ for (Errors::const_iterator itError = errors_.begin();
+ itError != errors_.end();
+ ++itError) {
+ const ErrorInfo& error = *itError;
+ formattedMessage +=
+ "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+ formattedMessage += " " + error.message_ + "\n";
+ if (error.extra_)
+ formattedMessage +=
+ "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+ }
+ return formattedMessage;
+}
+
+std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
+ std::vector<OurReader::StructuredError> allErrors;
+ for (Errors::const_iterator itError = errors_.begin();
+ itError != errors_.end();
+ ++itError) {
+ const ErrorInfo& error = *itError;
+ OurReader::StructuredError structured;
+ structured.offset_start = error.token_.start_ - begin_;
+ structured.offset_limit = error.token_.end_ - begin_;
+ structured.message = error.message_;
+ allErrors.push_back(structured);
+ }
+ return allErrors;
+}
+
+bool OurReader::pushError(const Value& value, const std::string& message) {
+ size_t length = end_ - begin_;
+ if(value.getOffsetStart() > length
+ || value.getOffsetLimit() > length)
+ return false;
+ Token token;
+ token.type_ = tokenError;
+ token.start_ = begin_ + value.getOffsetStart();
+ token.end_ = end_ + value.getOffsetLimit();
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = 0;
+ errors_.push_back(info);
+ return true;
+}
+
+bool OurReader::pushError(const Value& value, const std::string& message, const Value& extra) {
+ size_t length = end_ - begin_;
+ if(value.getOffsetStart() > length
+ || value.getOffsetLimit() > length
+ || extra.getOffsetLimit() > length)
+ return false;
+ Token token;
+ token.type_ = tokenError;
+ token.start_ = begin_ + value.getOffsetStart();
+ token.end_ = begin_ + value.getOffsetLimit();
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = begin_ + extra.getOffsetStart();
+ errors_.push_back(info);
+ return true;
+}
+
+bool OurReader::good() const {
+ return !errors_.size();
+}
+
+
+class OurCharReader : public CharReader {
+ bool const collectComments_;
+ OurReader reader_;
+public:
+ OurCharReader(
+ bool collectComments,
+ OurFeatures const& features)
+ : collectComments_(collectComments)
+ , reader_(features)
+ {}
+ bool parse(
+ char const* beginDoc, char const* endDoc,
+ Value* root, std::string* errs) override {
+ bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
+ if (errs) {
+ *errs = reader_.getFormattedErrorMessages();
+ }
+ return ok;
+ }
+};
+
+CharReaderBuilder::CharReaderBuilder()
+{
+ setDefaults(&settings_);
+}
+CharReaderBuilder::~CharReaderBuilder()
+{}
+CharReader* CharReaderBuilder::newCharReader() const
+{
+ bool collectComments = settings_["collectComments"].asBool();
+ OurFeatures features = OurFeatures::all();
+ features.allowComments_ = settings_["allowComments"].asBool();
+ features.strictRoot_ = settings_["strictRoot"].asBool();
+ features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
+ features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
+ features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
+ features.stackLimit_ = settings_["stackLimit"].asInt();
+ features.failIfExtra_ = settings_["failIfExtra"].asBool();
+ features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
+ features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
+ return new OurCharReader(collectComments, features);
+}
+static void getValidReaderKeys(std::set<std::string>* valid_keys)
+{
+ valid_keys->clear();
+ valid_keys->insert("collectComments");
+ valid_keys->insert("allowComments");
+ valid_keys->insert("strictRoot");
+ valid_keys->insert("allowDroppedNullPlaceholders");
+ valid_keys->insert("allowNumericKeys");
+ valid_keys->insert("allowSingleQuotes");
+ valid_keys->insert("stackLimit");
+ valid_keys->insert("failIfExtra");
+ valid_keys->insert("rejectDupKeys");
+ valid_keys->insert("allowSpecialFloats");
+}
+bool CharReaderBuilder::validate(Json::Value* invalid) const
+{
+ Json::Value my_invalid;
+ if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
+ Json::Value& inv = *invalid;
+ std::set<std::string> valid_keys;
+ getValidReaderKeys(&valid_keys);
+ Value::Members keys = settings_.getMemberNames();
+ size_t n = keys.size();
+ for (size_t i = 0; i < n; ++i) {
+ std::string const& key = keys[i];
+ if (valid_keys.find(key) == valid_keys.end()) {
+ inv[key] = settings_[key];
+ }
+ }
+ return 0u == inv.size();
+}
+Value& CharReaderBuilder::operator[](std::string key)
+{
+ return settings_[key];
+}
+// static
+void CharReaderBuilder::strictMode(Json::Value* settings)
+{
+//! [CharReaderBuilderStrictMode]
+ (*settings)["allowComments"] = false;
+ (*settings)["strictRoot"] = true;
+ (*settings)["allowDroppedNullPlaceholders"] = false;
+ (*settings)["allowNumericKeys"] = false;
+ (*settings)["allowSingleQuotes"] = false;
+ (*settings)["stackLimit"] = 1000;
+ (*settings)["failIfExtra"] = true;
+ (*settings)["rejectDupKeys"] = true;
+ (*settings)["allowSpecialFloats"] = false;
+//! [CharReaderBuilderStrictMode]
+}
+// static
+void CharReaderBuilder::setDefaults(Json::Value* settings)
+{
+//! [CharReaderBuilderDefaults]
+ (*settings)["collectComments"] = true;
+ (*settings)["allowComments"] = true;
+ (*settings)["strictRoot"] = false;
+ (*settings)["allowDroppedNullPlaceholders"] = false;
+ (*settings)["allowNumericKeys"] = false;
+ (*settings)["allowSingleQuotes"] = false;
+ (*settings)["stackLimit"] = 1000;
+ (*settings)["failIfExtra"] = false;
+ (*settings)["rejectDupKeys"] = false;
+ (*settings)["allowSpecialFloats"] = false;
+//! [CharReaderBuilderDefaults]
+}
+
+//////////////////////////////////
+// global functions
+
+bool parseFromStream(
+ CharReader::Factory const& fact, std::istream& sin,
+ Value* root, std::string* errs)
+{
+ std::ostringstream ssin;
+ ssin << sin.rdbuf();
+ std::string doc = ssin.str();
+ char const* begin = doc.data();
+ char const* end = begin + doc.size();
+ // Note that we do not actually need a null-terminator.
+ CharReaderPtr const reader(fact.newCharReader());
+ return reader->parse(begin, end, root, errs);
+}
+
+std::istream& operator>>(std::istream& sin, Value& root) {
+ CharReaderBuilder b;
+ std::string errs;
+ bool ok = parseFromStream(b, sin, &root, &errs);
+ if (!ok) {
+ fprintf(stderr,
+ "Error from reader: %s",
+ errs.c_str());
+
+ throwRuntimeError(errs);
+ }
+ return sin;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+// included by json_value.cpp
+
+namespace Json {
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIteratorBase
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIteratorBase::ValueIteratorBase()
+ : current_(), isNull_(true) {
+}
+
+ValueIteratorBase::ValueIteratorBase(
+ const Value::ObjectValues::iterator& current)
+ : current_(current), isNull_(false) {}
+
+Value& ValueIteratorBase::deref() const {
+ return current_->second;
+}
+
+void ValueIteratorBase::increment() {
+ ++current_;
+}
+
+void ValueIteratorBase::decrement() {
+ --current_;
+}
+
+ValueIteratorBase::difference_type
+ValueIteratorBase::computeDistance(const SelfType& other) const {
+#ifdef JSON_USE_CPPTL_SMALLMAP
+ return other.current_ - current_;
+#else
+ // Iterator for null value are initialized using the default
+ // constructor, which initialize current_ to the default
+ // std::map::iterator. As begin() and end() are two instance
+ // of the default std::map::iterator, they can not be compared.
+ // To allow this, we handle this comparison specifically.
+ if (isNull_ && other.isNull_) {
+ return 0;
+ }
+
+ // Usage of std::distance is not portable (does not compile with Sun Studio 12
+ // RogueWave STL,
+ // which is the one used by default).
+ // Using a portable hand-made version for non random iterator instead:
+ // return difference_type( std::distance( current_, other.current_ ) );
+ difference_type myDistance = 0;
+ for (Value::ObjectValues::iterator it = current_; it != other.current_;
+ ++it) {
+ ++myDistance;
+ }
+ return myDistance;
+#endif
+}
+
+bool ValueIteratorBase::isEqual(const SelfType& other) const {
+ if (isNull_) {
+ return other.isNull_;
+ }
+ return current_ == other.current_;
+}
+
+void ValueIteratorBase::copy(const SelfType& other) {
+ current_ = other.current_;
+ isNull_ = other.isNull_;
+}
+
+Value ValueIteratorBase::key() const {
+ const Value::CZString czstring = (*current_).first;
+ if (czstring.data()) {
+ if (czstring.isStaticString())
+ return Value(StaticString(czstring.data()));
+ return Value(czstring.data(), czstring.data() + czstring.length());
+ }
+ return Value(czstring.index());
+}
+
+UInt ValueIteratorBase::index() const {
+ const Value::CZString czstring = (*current_).first;
+ if (!czstring.data())
+ return czstring.index();
+ return Value::UInt(-1);
+}
+
+std::string ValueIteratorBase::name() const {
+ char const* keey;
+ char const* end;
+ keey = memberName(&end);
+ if (!keey) return std::string();
+ return std::string(keey, end);
+}
+
+char const* ValueIteratorBase::memberName() const {
+ const char* cname = (*current_).first.data();
+ return cname ? cname : "";
+}
+
+char const* ValueIteratorBase::memberName(char const** end) const {
+ const char* cname = (*current_).first.data();
+ if (!cname) {
+ *end = NULL;
+ return NULL;
+ }
+ *end = cname + (*current_).first.length();
+ return cname;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueConstIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueConstIterator::ValueConstIterator() {}
+
+ValueConstIterator::ValueConstIterator(
+ const Value::ObjectValues::iterator& current)
+ : ValueIteratorBase(current) {}
+
+ValueConstIterator::ValueConstIterator(ValueIterator const& other)
+ : ValueIteratorBase(other) {}
+
+ValueConstIterator& ValueConstIterator::
+operator=(const ValueIteratorBase& other) {
+ copy(other);
+ return *this;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIterator::ValueIterator() {}
+
+ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
+ : ValueIteratorBase(current) {}
+
+ValueIterator::ValueIterator(const ValueConstIterator& other)
+ : ValueIteratorBase(other) {
+ throwRuntimeError("ConstIterator to Iterator should never be allowed.");
+}
+
+ValueIterator::ValueIterator(const ValueIterator& other)
+ : ValueIteratorBase(other) {}
+
+ValueIterator& ValueIterator::operator=(const SelfType& other) {
+ copy(other);
+ return *this;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/assertions.h>
+#include <json/value.h>
+#include <json/writer.h>
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <math.h>
+#include <sstream>
+#include <utility>
+#include <cstring>
+#include <cassert>
+#ifdef JSON_USE_CPPTL
+#include <cpptl/conststring.h>
+#endif
+#include <cstddef> // size_t
+#include <algorithm> // min()
+
+#define JSON_ASSERT_UNREACHABLE assert(false)
+
+namespace Json {
+
+// This is a walkaround to avoid the static initialization of Value::null.
+// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of
+// 8 (instead of 4) as a bit of future-proofing.
+#if defined(__ARMEL__)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#else
+#define ALIGNAS(byte_alignment)
+#endif
+static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
+const unsigned char& kNullRef = kNull[0];
+const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
+const Value& Value::nullRef = null;
+
+const Int Value::minInt = Int(~(UInt(-1) / 2));
+const Int Value::maxInt = Int(UInt(-1) / 2);
+const UInt Value::maxUInt = UInt(-1);
+#if defined(JSON_HAS_INT64)
+const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
+const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
+const UInt64 Value::maxUInt64 = UInt64(-1);
+// The constant is hard-coded because some compiler have trouble
+// converting Value::maxUInt64 to a double correctly (AIX/xlC).
+// Assumes that UInt64 is a 64 bits integer.
+static const double maxUInt64AsDouble = 18446744073709551615.0;
+#endif // defined(JSON_HAS_INT64)
+const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
+const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
+const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+ return d >= min && d <= max;
+}
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+static inline double integerToDouble(Json::UInt64 value) {
+ return static_cast<double>(Int64(value / 2)) * 2.0 + Int64(value & 1);
+}
+
+template <typename T> static inline double integerToDouble(T value) {
+ return static_cast<double>(value);
+}
+
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+ return d >= integerToDouble(min) && d <= integerToDouble(max);
+}
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+
+/** Duplicates the specified string value.
+ * @param value Pointer to the string to duplicate. Must be zero-terminated if
+ * length is "unknown".
+ * @param length Length of the value. if equals to unknown, then it will be
+ * computed using strlen(value).
+ * @return Pointer on the duplicate instance of string.
+ */
+static inline char* duplicateStringValue(const char* value,
+ size_t length) {
+ // Avoid an integer overflow in the call to malloc below by limiting length
+ // to a sane value.
+ if (length >= (size_t)Value::maxInt)
+ length = Value::maxInt - 1;
+
+ char* newString = static_cast<char*>(malloc(length + 1));
+ if (newString == NULL) {
+ throwRuntimeError(
+ "in Json::Value::duplicateStringValue(): "
+ "Failed to allocate string value buffer");
+ }
+ memcpy(newString, value, length);
+ newString[length] = 0;
+ return newString;
+}
+
+/* Record the length as a prefix.
+ */
+static inline char* duplicateAndPrefixStringValue(
+ const char* value,
+ unsigned int length)
+{
+ // Avoid an integer overflow in the call to malloc below by limiting length
+ // to a sane value.
+ JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U,
+ "in Json::Value::duplicateAndPrefixStringValue(): "
+ "length too big for prefixing");
+ unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
+ char* newString = static_cast<char*>(malloc(actualLength));
+ if (newString == 0) {
+ throwRuntimeError(
+ "in Json::Value::duplicateAndPrefixStringValue(): "
+ "Failed to allocate string value buffer");
+ }
+ *reinterpret_cast<unsigned*>(newString) = length;
+ memcpy(newString + sizeof(unsigned), value, length);
+ newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later
+ return newString;
+}
+inline static void decodePrefixedString(
+ bool isPrefixed, char const* prefixed,
+ unsigned* length, char const** value)
+{
+ if (!isPrefixed) {
+ *length = static_cast<unsigned>(strlen(prefixed));
+ *value = prefixed;
+ } else {
+ *length = *reinterpret_cast<unsigned const*>(prefixed);
+ *value = prefixed + sizeof(unsigned);
+ }
+}
+/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().
+ */
+static inline void releaseStringValue(char* value) { free(value); }
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// ValueInternals...
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "json_valueiterator.inl"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+Exception::Exception(std::string const& msg)
+ : msg_(msg)
+{}
+Exception::~Exception() throw()
+{}
+char const* Exception::what() const throw()
+{
+ return msg_.c_str();
+}
+RuntimeError::RuntimeError(std::string const& msg)
+ : Exception(msg)
+{}
+LogicError::LogicError(std::string const& msg)
+ : Exception(msg)
+{}
+void throwRuntimeError(std::string const& msg)
+{
+ throw RuntimeError(msg);
+}
+void throwLogicError(std::string const& msg)
+{
+ throw LogicError(msg);
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CommentInfo
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+Value::CommentInfo::CommentInfo() : comment_(0) {}
+
+Value::CommentInfo::~CommentInfo() {
+ if (comment_)
+ releaseStringValue(comment_);
+}
+
+void Value::CommentInfo::setComment(const char* text, size_t len) {
+ if (comment_) {
+ releaseStringValue(comment_);
+ comment_ = 0;
+ }
+ JSON_ASSERT(text != 0);
+ JSON_ASSERT_MESSAGE(
+ text[0] == '\0' || text[0] == '/',
+ "in Json::Value::setComment(): Comments must start with /");
+ // It seems that /**/ style comments are acceptable as well.
+ comment_ = duplicateStringValue(text, len);
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CZString
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+// Notes: policy_ indicates if the string was allocated when
+// a string is stored.
+
+Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {}
+
+Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate)
+ : cstr_(str) {
+ // allocate != duplicate
+ storage_.policy_ = allocate & 0x3;
+ storage_.length_ = ulength & 0x3FFFFFFF;
+}
+
+Value::CZString::CZString(const CZString& other)
+ : cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0
+ ? duplicateStringValue(other.cstr_, other.storage_.length_)
+ : other.cstr_) {
+ storage_.policy_ = (other.cstr_
+ ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication
+ ? noDuplication : duplicate)
+ : static_cast<DuplicationPolicy>(other.storage_.policy_));
+ storage_.length_ = other.storage_.length_;
+}
+
+#if JSON_HAS_RVALUE_REFERENCES
+Value::CZString::CZString(CZString&& other)
+ : cstr_(other.cstr_), index_(other.index_) {
+ other.cstr_ = nullptr;
+}
+#endif
+
+Value::CZString::~CZString() {
+ if (cstr_ && storage_.policy_ == duplicate)
+ releaseStringValue(const_cast<char*>(cstr_));
+}
+
+void Value::CZString::swap(CZString& other) {
+ std::swap(cstr_, other.cstr_);
+ std::swap(index_, other.index_);
+}
+
+Value::CZString& Value::CZString::operator=(CZString other) {
+ swap(other);
+ return *this;
+}
+
+bool Value::CZString::operator<(const CZString& other) const {
+ if (!cstr_) return index_ < other.index_;
+ //return strcmp(cstr_, other.cstr_) < 0;
+ // Assume both are strings.
+ unsigned this_len = this->storage_.length_;
+ unsigned other_len = other.storage_.length_;
+ unsigned min_len = std::min(this_len, other_len);
+ int comp = memcmp(this->cstr_, other.cstr_, min_len);
+ if (comp < 0) return true;
+ if (comp > 0) return false;
+ return (this_len < other_len);
+}
+
+bool Value::CZString::operator==(const CZString& other) const {
+ if (!cstr_) return index_ == other.index_;
+ //return strcmp(cstr_, other.cstr_) == 0;
+ // Assume both are strings.
+ unsigned this_len = this->storage_.length_;
+ unsigned other_len = other.storage_.length_;
+ if (this_len != other_len) return false;
+ int comp = memcmp(this->cstr_, other.cstr_, this_len);
+ return comp == 0;
+}
+
+ArrayIndex Value::CZString::index() const { return index_; }
+
+//const char* Value::CZString::c_str() const { return cstr_; }
+const char* Value::CZString::data() const { return cstr_; }
+unsigned Value::CZString::length() const { return storage_.length_; }
+bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::Value
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+/*! \internal Default constructor initialization must be equivalent to:
+ * memset( this, 0, sizeof(Value) )
+ * This optimization is used in ValueInternalMap fast allocator.
+ */
+Value::Value(ValueType vtype) {
+ initBasic(vtype);
+ switch (vtype) {
+ case nullValue:
+ break;
+ case intValue:
+ case uintValue:
+ value_.int_ = 0;
+ break;
+ case realValue:
+ value_.real_ = 0.0;
+ break;
+ case stringValue:
+ value_.string_ = 0;
+ break;
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues();
+ break;
+ case booleanValue:
+ value_.bool_ = false;
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+}
+
+Value::Value(Int value) {
+ initBasic(intValue);
+ value_.int_ = value;
+}
+
+Value::Value(UInt value) {
+ initBasic(uintValue);
+ value_.uint_ = value;
+}
+#if defined(JSON_HAS_INT64)
+Value::Value(Int64 value) {
+ initBasic(intValue);
+ value_.int_ = value;
+}
+Value::Value(UInt64 value) {
+ initBasic(uintValue);
+ value_.uint_ = value;
+}
+#endif // defined(JSON_HAS_INT64)
+
+Value::Value(double value) {
+ initBasic(realValue);
+ value_.real_ = value;
+}
+
+Value::Value(const char* value) {
+ initBasic(stringValue, true);
+ value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
+}
+
+Value::Value(const char* beginValue, const char* endValue) {
+ initBasic(stringValue, true);
+ value_.string_ =
+ duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));
+}
+
+Value::Value(const std::string& value) {
+ initBasic(stringValue, true);
+ value_.string_ =
+ duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));
+}
+
+Value::Value(const StaticString& value) {
+ initBasic(stringValue);
+ value_.string_ = const_cast<char*>(value.c_str());
+}
+
+#ifdef JSON_USE_CPPTL
+Value::Value(const CppTL::ConstString& value) {
+ initBasic(stringValue, true);
+ value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));
+}
+#endif
+
+Value::Value(bool value) {
+ initBasic(booleanValue);
+ value_.bool_ = value;
+}
+
+Value::Value(Value const& other)
+ : type_(other.type_), allocated_(false)
+ ,
+ comments_(0), start_(other.start_), limit_(other.limit_)
+{
+ switch (type_) {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ value_ = other.value_;
+ break;
+ case stringValue:
+ if (other.value_.string_ && other.allocated_) {
+ unsigned len;
+ char const* str;
+ decodePrefixedString(other.allocated_, other.value_.string_,
+ &len, &str);
+ value_.string_ = duplicateAndPrefixStringValue(str, len);
+ allocated_ = true;
+ } else {
+ value_.string_ = other.value_.string_;
+ allocated_ = false;
+ }
+ break;
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues(*other.value_.map_);
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ if (other.comments_) {
+ comments_ = new CommentInfo[numberOfCommentPlacement];
+ for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
+ const CommentInfo& otherComment = other.comments_[comment];
+ if (otherComment.comment_)
+ comments_[comment].setComment(
+ otherComment.comment_, strlen(otherComment.comment_));
+ }
+ }
+}
+
+#if JSON_HAS_RVALUE_REFERENCES
+// Move constructor
+Value::Value(Value&& other) {
+ initBasic(nullValue);
+ swap(other);
+}
+#endif
+
+Value::~Value() {
+ switch (type_) {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ break;
+ case stringValue:
+ if (allocated_)
+ releaseStringValue(value_.string_);
+ break;
+ case arrayValue:
+ case objectValue:
+ delete value_.map_;
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+
+ if (comments_)
+ delete[] comments_;
+}
+
+Value& Value::operator=(Value other) {
+ swap(other);
+ return *this;
+}
+
+void Value::swapPayload(Value& other) {
+ ValueType temp = type_;
+ type_ = other.type_;
+ other.type_ = temp;
+ std::swap(value_, other.value_);
+ int temp2 = allocated_;
+ allocated_ = other.allocated_;
+ other.allocated_ = temp2 & 0x1;
+}
+
+void Value::swap(Value& other) {
+ swapPayload(other);
+ std::swap(comments_, other.comments_);
+ std::swap(start_, other.start_);
+ std::swap(limit_, other.limit_);
+}
+
+ValueType Value::type() const { return type_; }
+
+int Value::compare(const Value& other) const {
+ if (*this < other)
+ return -1;
+ if (*this > other)
+ return 1;
+ return 0;
+}
+
+bool Value::operator<(const Value& other) const {
+ int typeDelta = type_ - other.type_;
+ if (typeDelta)
+ return typeDelta < 0 ? true : false;
+ switch (type_) {
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ < other.value_.int_;
+ case uintValue:
+ return value_.uint_ < other.value_.uint_;
+ case realValue:
+ return value_.real_ < other.value_.real_;
+ case booleanValue:
+ return value_.bool_ < other.value_.bool_;
+ case stringValue:
+ {
+ if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
+ if (other.value_.string_) return true;
+ else return false;
+ }
+ unsigned this_len;
+ unsigned other_len;
+ char const* this_str;
+ char const* other_str;
+ decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+ decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
+ unsigned min_len = std::min(this_len, other_len);
+ int comp = memcmp(this_str, other_str, min_len);
+ if (comp < 0) return true;
+ if (comp > 0) return false;
+ return (this_len < other_len);
+ }
+ case arrayValue:
+ case objectValue: {
+ int delta = int(value_.map_->size() - other.value_.map_->size());
+ if (delta)
+ return delta < 0;
+ return (*value_.map_) < (*other.value_.map_);
+ }
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ return false; // unreachable
+}
+
+bool Value::operator<=(const Value& other) const { return !(other < *this); }
+
+bool Value::operator>=(const Value& other) const { return !(*this < other); }
+
+bool Value::operator>(const Value& other) const { return other < *this; }
+
+bool Value::operator==(const Value& other) const {
+ // if ( type_ != other.type_ )
+ // GCC 2.95.3 says:
+ // attempt to take address of bit-field structure member `Json::Value::type_'
+ // Beats me, but a temp solves the problem.
+ int temp = other.type_;
+ if (type_ != temp)
+ return false;
+ switch (type_) {
+ case nullValue:
+ return true;
+ case intValue:
+ return value_.int_ == other.value_.int_;
+ case uintValue:
+ return value_.uint_ == other.value_.uint_;
+ case realValue:
+ return value_.real_ == other.value_.real_;
+ case booleanValue:
+ return value_.bool_ == other.value_.bool_;
+ case stringValue:
+ {
+ if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
+ return (value_.string_ == other.value_.string_);
+ }
+ unsigned this_len;
+ unsigned other_len;
+ char const* this_str;
+ char const* other_str;
+ decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+ decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
+ if (this_len != other_len) return false;
+ int comp = memcmp(this_str, other_str, this_len);
+ return comp == 0;
+ }
+ case arrayValue:
+ case objectValue:
+ return value_.map_->size() == other.value_.map_->size() &&
+ (*value_.map_) == (*other.value_.map_);
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ return false; // unreachable
+}
+
+bool Value::operator!=(const Value& other) const { return !(*this == other); }
+
+const char* Value::asCString() const {
+ JSON_ASSERT_MESSAGE(type_ == stringValue,
+ "in Json::Value::asCString(): requires stringValue");
+ if (value_.string_ == 0) return 0;
+ unsigned this_len;
+ char const* this_str;
+ decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+ return this_str;
+}
+
+bool Value::getString(char const** str, char const** cend) const {
+ if (type_ != stringValue) return false;
+ if (value_.string_ == 0) return false;
+ unsigned length;
+ decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
+ *cend = *str + length;
+ return true;
+}
+
+std::string Value::asString() const {
+ switch (type_) {
+ case nullValue:
+ return "";
+ case stringValue:
+ {
+ if (value_.string_ == 0) return "";
+ unsigned this_len;
+ char const* this_str;
+ decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+ return std::string(this_str, this_len);
+ }
+ case booleanValue:
+ return value_.bool_ ? "true" : "false";
+ case intValue:
+ return valueToString(value_.int_);
+ case uintValue:
+ return valueToString(value_.uint_);
+ case realValue:
+ return valueToString(value_.real_);
+ default:
+ JSON_FAIL_MESSAGE("Type is not convertible to string");
+ }
+}
+
+#ifdef JSON_USE_CPPTL
+CppTL::ConstString Value::asConstString() const {
+ unsigned len;
+ char const* str;
+ decodePrefixedString(allocated_, value_.string_,
+ &len, &str);
+ return CppTL::ConstString(str, len);
+}
+#endif
+
+Value::Int Value::asInt() const {
+ switch (type_) {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
+ return Int(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
+ return Int(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
+ "double out of Int range");
+ return Int(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to Int.");
+}
+
+Value::UInt Value::asUInt() const {
+ switch (type_) {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
+ return UInt(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
+ return UInt(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
+ "double out of UInt range");
+ return UInt(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
+}
+
+#if defined(JSON_HAS_INT64)
+
+Value::Int64 Value::asInt64() const {
+ switch (type_) {
+ case intValue:
+ return Int64(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
+ return Int64(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
+ "double out of Int64 range");
+ return Int64(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
+}
+
+Value::UInt64 Value::asUInt64() const {
+ switch (type_) {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
+ return UInt64(value_.int_);
+ case uintValue:
+ return UInt64(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
+ "double out of UInt64 range");
+ return UInt64(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
+}
+#endif // if defined(JSON_HAS_INT64)
+
+LargestInt Value::asLargestInt() const {
+#if defined(JSON_NO_INT64)
+ return asInt();
+#else
+ return asInt64();
+#endif
+}
+
+LargestUInt Value::asLargestUInt() const {
+#if defined(JSON_NO_INT64)
+ return asUInt();
+#else
+ return asUInt64();
+#endif
+}
+
+double Value::asDouble() const {
+ switch (type_) {
+ case intValue:
+ return static_cast<double>(value_.int_);
+ case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<double>(value_.uint_);
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return integerToDouble(value_.uint_);
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ case realValue:
+ return value_.real_;
+ case nullValue:
+ return 0.0;
+ case booleanValue:
+ return value_.bool_ ? 1.0 : 0.0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to double.");
+}
+
+float Value::asFloat() const {
+ switch (type_) {
+ case intValue:
+ return static_cast<float>(value_.int_);
+ case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<float>(value_.uint_);
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return integerToDouble(value_.uint_);
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ case realValue:
+ return static_cast<float>(value_.real_);
+ case nullValue:
+ return 0.0;
+ case booleanValue:
+ return value_.bool_ ? 1.0f : 0.0f;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to float.");
+}
+
+bool Value::asBool() const {
+ switch (type_) {
+ case booleanValue:
+ return value_.bool_;
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ ? true : false;
+ case uintValue:
+ return value_.uint_ ? true : false;
+ case realValue:
+ // This is kind of strange. Not recommended.
+ return (value_.real_ != 0.0) ? true : false;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to bool.");
+}
+
+bool Value::isConvertibleTo(ValueType other) const {
+ switch (other) {
+ case nullValue:
+ return (isNumeric() && asDouble() == 0.0) ||
+ (type_ == booleanValue && value_.bool_ == false) ||
+ (type_ == stringValue && asString() == "") ||
+ (type_ == arrayValue && value_.map_->size() == 0) ||
+ (type_ == objectValue && value_.map_->size() == 0) ||
+ type_ == nullValue;
+ case intValue:
+ return isInt() ||
+ (type_ == realValue && InRange(value_.real_, minInt, maxInt)) ||
+ type_ == booleanValue || type_ == nullValue;
+ case uintValue:
+ return isUInt() ||
+ (type_ == realValue && InRange(value_.real_, 0, maxUInt)) ||
+ type_ == booleanValue || type_ == nullValue;
+ case realValue:
+ return isNumeric() || type_ == booleanValue || type_ == nullValue;
+ case booleanValue:
+ return isNumeric() || type_ == booleanValue || type_ == nullValue;
+ case stringValue:
+ return isNumeric() || type_ == booleanValue || type_ == stringValue ||
+ type_ == nullValue;
+ case arrayValue:
+ return type_ == arrayValue || type_ == nullValue;
+ case objectValue:
+ return type_ == objectValue || type_ == nullValue;
+ }
+ JSON_ASSERT_UNREACHABLE;
+ return false;
+}
+
+/// Number of values in array or object
+ArrayIndex Value::size() const {
+ switch (type_) {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ case stringValue:
+ return 0;
+ case arrayValue: // size of the array is highest index + 1
+ if (!value_.map_->empty()) {
+ ObjectValues::const_iterator itLast = value_.map_->end();
+ --itLast;
+ return (*itLast).first.index() + 1;
+ }
+ return 0;
+ case objectValue:
+ return ArrayIndex(value_.map_->size());
+ }
+ JSON_ASSERT_UNREACHABLE;
+ return 0; // unreachable;
+}
+
+bool Value::empty() const {
+ if (isNull() || isArray() || isObject())
+ return size() == 0u;
+ else
+ return false;
+}
+
+bool Value::operator!() const { return isNull(); }
+
+void Value::clear() {
+ JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||
+ type_ == objectValue,
+ "in Json::Value::clear(): requires complex value");
+ start_ = 0;
+ limit_ = 0;
+ switch (type_) {
+ case arrayValue:
+ case objectValue:
+ value_.map_->clear();
+ break;
+ default:
+ break;
+ }
+}
+
+void Value::resize(ArrayIndex newSize) {
+ JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue,
+ "in Json::Value::resize(): requires arrayValue");
+ if (type_ == nullValue)
+ *this = Value(arrayValue);
+ ArrayIndex oldSize = size();
+ if (newSize == 0)
+ clear();
+ else if (newSize > oldSize)
+ (*this)[newSize - 1];
+ else {
+ for (ArrayIndex index = newSize; index < oldSize; ++index) {
+ value_.map_->erase(index);
+ }
+ assert(size() == newSize);
+ }
+}
+
+Value& Value::operator[](ArrayIndex index) {
+ JSON_ASSERT_MESSAGE(
+ type_ == nullValue || type_ == arrayValue,
+ "in Json::Value::operator[](ArrayIndex): requires arrayValue");
+ if (type_ == nullValue)
+ *this = Value(arrayValue);
+ CZString key(index);
+ ObjectValues::iterator it = value_.map_->lower_bound(key);
+ if (it != value_.map_->end() && (*it).first == key)
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue(key, nullRef);
+ it = value_.map_->insert(it, defaultValue);
+ return (*it).second;
+}
+
+Value& Value::operator[](int index) {
+ JSON_ASSERT_MESSAGE(
+ index >= 0,
+ "in Json::Value::operator[](int index): index cannot be negative");
+ return (*this)[ArrayIndex(index)];
+}
+
+const Value& Value::operator[](ArrayIndex index) const {
+ JSON_ASSERT_MESSAGE(
+ type_ == nullValue || type_ == arrayValue,
+ "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
+ if (type_ == nullValue)
+ return nullRef;
+ CZString key(index);
+ ObjectValues::const_iterator it = value_.map_->find(key);
+ if (it == value_.map_->end())
+ return nullRef;
+ return (*it).second;
+}
+
+const Value& Value::operator[](int index) const {
+ JSON_ASSERT_MESSAGE(
+ index >= 0,
+ "in Json::Value::operator[](int index) const: index cannot be negative");
+ return (*this)[ArrayIndex(index)];
+}
+
+void Value::initBasic(ValueType vtype, bool allocated) {
+ type_ = vtype;
+ allocated_ = allocated;
+ comments_ = 0;
+ start_ = 0;
+ limit_ = 0;
+}
+
+// Access an object value by name, create a null member if it does not exist.
+// @pre Type of '*this' is object or null.
+// @param key is null-terminated.
+Value& Value::resolveReference(const char* key) {
+ JSON_ASSERT_MESSAGE(
+ type_ == nullValue || type_ == objectValue,
+ "in Json::Value::resolveReference(): requires objectValue");
+ if (type_ == nullValue)
+ *this = Value(objectValue);
+ CZString actualKey(
+ key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE!
+ ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+ if (it != value_.map_->end() && (*it).first == actualKey)
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue(actualKey, nullRef);
+ it = value_.map_->insert(it, defaultValue);
+ Value& value = (*it).second;
+ return value;
+}
+
+// @param key is not null-terminated.
+Value& Value::resolveReference(char const* key, char const* cend)
+{
+ JSON_ASSERT_MESSAGE(
+ type_ == nullValue || type_ == objectValue,
+ "in Json::Value::resolveReference(key, end): requires objectValue");
+ if (type_ == nullValue)
+ *this = Value(objectValue);
+ CZString actualKey(
+ key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy);
+ ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+ if (it != value_.map_->end() && (*it).first == actualKey)
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue(actualKey, nullRef);
+ it = value_.map_->insert(it, defaultValue);
+ Value& value = (*it).second;
+ return value;
+}
+
+Value Value::get(ArrayIndex index, const Value& defaultValue) const {
+ const Value* value = &((*this)[index]);
+ return value == &nullRef ? defaultValue : *value;
+}
+
+bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
+
+Value const* Value::find(char const* key, char const* cend) const
+{
+ JSON_ASSERT_MESSAGE(
+ type_ == nullValue || type_ == objectValue,
+ "in Json::Value::find(key, end, found): requires objectValue or nullValue");
+ if (type_ == nullValue) return NULL;
+ CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
+ ObjectValues::const_iterator it = value_.map_->find(actualKey);
+ if (it == value_.map_->end()) return NULL;
+ return &(*it).second;
+}
+const Value& Value::operator[](const char* key) const
+{
+ Value const* found = find(key, key + strlen(key));
+ if (!found) return nullRef;
+ return *found;
+}
+Value const& Value::operator[](std::string const& key) const
+{
+ Value const* found = find(key.data(), key.data() + key.length());
+ if (!found) return nullRef;
+ return *found;
+}
+
+Value& Value::operator[](const char* key) {
+ return resolveReference(key, key + strlen(key));
+}
+
+Value& Value::operator[](const std::string& key) {
+ return resolveReference(key.data(), key.data() + key.length());
+}
+
+Value& Value::operator[](const StaticString& key) {
+ return resolveReference(key.c_str());
+}
+
+#ifdef JSON_USE_CPPTL
+Value& Value::operator[](const CppTL::ConstString& key) {
+ return resolveReference(key.c_str(), key.end_c_str());
+}
+Value const& Value::operator[](CppTL::ConstString const& key) const
+{
+ Value const* found = find(key.c_str(), key.end_c_str());
+ if (!found) return nullRef;
+ return *found;
+}
+#endif
+
+Value& Value::append(const Value& value) { return (*this)[size()] = value; }
+
+Value Value::get(char const* key, char const* cend, Value const& defaultValue) const
+{
+ Value const* found = find(key, cend);
+ return !found ? defaultValue : *found;
+}
+Value Value::get(char const* key, Value const& defaultValue) const
+{
+ return get(key, key + strlen(key), defaultValue);
+}
+Value Value::get(std::string const& key, Value const& defaultValue) const
+{
+ return get(key.data(), key.data() + key.length(), defaultValue);
+}
+
+
+bool Value::removeMember(const char* key, const char* cend, Value* removed)
+{
+ if (type_ != objectValue) {
+ return false;
+ }
+ CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
+ ObjectValues::iterator it = value_.map_->find(actualKey);
+ if (it == value_.map_->end())
+ return false;
+ *removed = it->second;
+ value_.map_->erase(it);
+ return true;
+}
+bool Value::removeMember(const char* key, Value* removed)
+{
+ return removeMember(key, key + strlen(key), removed);
+}
+bool Value::removeMember(std::string const& key, Value* removed)
+{
+ return removeMember(key.data(), key.data() + key.length(), removed);
+}
+Value Value::removeMember(const char* key)
+{
+ JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
+ "in Json::Value::removeMember(): requires objectValue");
+ if (type_ == nullValue)
+ return nullRef;
+
+ Value removed; // null
+ removeMember(key, key + strlen(key), &removed);
+ return removed; // still null if removeMember() did nothing
+}
+Value Value::removeMember(const std::string& key)
+{
+ return removeMember(key.c_str());
+}
+
+bool Value::removeIndex(ArrayIndex index, Value* removed) {
+ if (type_ != arrayValue) {
+ return false;
+ }
+ CZString key(index);
+ ObjectValues::iterator it = value_.map_->find(key);
+ if (it == value_.map_->end()) {
+ return false;
+ }
+ *removed = it->second;
+ ArrayIndex oldSize = size();
+ // shift left all items left, into the place of the "removed"
+ for (ArrayIndex i = index; i < (oldSize - 1); ++i){
+ CZString keey(i);
+ (*value_.map_)[keey] = (*this)[i + 1];
+ }
+ // erase the last one ("leftover")
+ CZString keyLast(oldSize - 1);
+ ObjectValues::iterator itLast = value_.map_->find(keyLast);
+ value_.map_->erase(itLast);
+ return true;
+}
+
+#ifdef JSON_USE_CPPTL
+Value Value::get(const CppTL::ConstString& key,
+ const Value& defaultValue) const {
+ return get(key.c_str(), key.end_c_str(), defaultValue);
+}
+#endif
+
+bool Value::isMember(char const* key, char const* cend) const
+{
+ Value const* value = find(key, cend);
+ return NULL != value;
+}
+bool Value::isMember(char const* key) const
+{
+ return isMember(key, key + strlen(key));
+}
+bool Value::isMember(std::string const& key) const
+{
+ return isMember(key.data(), key.data() + key.length());
+}
+
+#ifdef JSON_USE_CPPTL
+bool Value::isMember(const CppTL::ConstString& key) const {
+ return isMember(key.c_str(), key.end_c_str());
+}
+#endif
+
+Value::Members Value::getMemberNames() const {
+ JSON_ASSERT_MESSAGE(
+ type_ == nullValue || type_ == objectValue,
+ "in Json::Value::getMemberNames(), value must be objectValue");
+ if (type_ == nullValue)
+ return Value::Members();
+ Members members;
+ members.reserve(value_.map_->size());
+ ObjectValues::const_iterator it = value_.map_->begin();
+ ObjectValues::const_iterator itEnd = value_.map_->end();
+ for (; it != itEnd; ++it) {
+ members.push_back(std::string((*it).first.data(),
+ (*it).first.length()));
+ }
+ return members;
+}
+//
+//# ifdef JSON_USE_CPPTL
+// EnumMemberNames
+// Value::enumMemberNames() const
+//{
+// if ( type_ == objectValue )
+// {
+// return CppTL::Enum::any( CppTL::Enum::transform(
+// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
+// MemberNamesTransform() ) );
+// }
+// return EnumMemberNames();
+//}
+//
+//
+// EnumValues
+// Value::enumValues() const
+//{
+// if ( type_ == objectValue || type_ == arrayValue )
+// return CppTL::Enum::anyValues( *(value_.map_),
+// CppTL::Type<const Value &>() );
+// return EnumValues();
+//}
+//
+//# endif
+
+static bool IsIntegral(double d) {
+ double integral_part;
+ return modf(d, &integral_part) == 0.0;
+}
+
+bool Value::isNull() const { return type_ == nullValue; }
+
+bool Value::isBool() const { return type_ == booleanValue; }
+
+bool Value::isInt() const {
+ switch (type_) {
+ case intValue:
+ return value_.int_ >= minInt && value_.int_ <= maxInt;
+ case uintValue:
+ return value_.uint_ <= UInt(maxInt);
+ case realValue:
+ return value_.real_ >= minInt && value_.real_ <= maxInt &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+ return false;
+}
+
+bool Value::isUInt() const {
+ switch (type_) {
+ case intValue:
+ return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
+ case uintValue:
+ return value_.uint_ <= maxUInt;
+ case realValue:
+ return value_.real_ >= 0 && value_.real_ <= maxUInt &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+ return false;
+}
+
+bool Value::isInt64() const {
+#if defined(JSON_HAS_INT64)
+ switch (type_) {
+ case intValue:
+ return true;
+ case uintValue:
+ return value_.uint_ <= UInt64(maxInt64);
+ case realValue:
+ // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
+ // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= double(minInt64) &&
+ value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
+ default:
+ break;
+ }
+#endif // JSON_HAS_INT64
+ return false;
+}
+
+bool Value::isUInt64() const {
+#if defined(JSON_HAS_INT64)
+ switch (type_) {
+ case intValue:
+ return value_.int_ >= 0;
+ case uintValue:
+ return true;
+ case realValue:
+ // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+ // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+#endif // JSON_HAS_INT64
+ return false;
+}
+
+bool Value::isIntegral() const {
+#if defined(JSON_HAS_INT64)
+ return isInt64() || isUInt64();
+#else
+ return isInt() || isUInt();
+#endif
+}
+
+bool Value::isDouble() const { return type_ == realValue || isIntegral(); }
+
+bool Value::isNumeric() const { return isIntegral() || isDouble(); }
+
+bool Value::isString() const { return type_ == stringValue; }
+
+bool Value::isArray() const { return type_ == arrayValue; }
+
+bool Value::isObject() const { return type_ == objectValue; }
+
+void Value::setComment(const char* comment, size_t len, CommentPlacement placement) {
+ if (!comments_)
+ comments_ = new CommentInfo[numberOfCommentPlacement];
+ if ((len > 0) && (comment[len-1] == '\n')) {
+ // Always discard trailing newline, to aid indentation.
+ len -= 1;
+ }
+ comments_[placement].setComment(comment, len);
+}
+
+void Value::setComment(const char* comment, CommentPlacement placement) {
+ setComment(comment, strlen(comment), placement);
+}
+
+void Value::setComment(const std::string& comment, CommentPlacement placement) {
+ setComment(comment.c_str(), comment.length(), placement);
+}
+
+bool Value::hasComment(CommentPlacement placement) const {
+ return comments_ != 0 && comments_[placement].comment_ != 0;
+}
+
+std::string Value::getComment(CommentPlacement placement) const {
+ if (hasComment(placement))
+ return comments_[placement].comment_;
+ return "";
+}
+
+void Value::setOffsetStart(size_t start) { start_ = start; }
+
+void Value::setOffsetLimit(size_t limit) { limit_ = limit; }
+
+size_t Value::getOffsetStart() const { return start_; }
+
+size_t Value::getOffsetLimit() const { return limit_; }
+
+std::string Value::toStyledString() const {
+ StyledWriter writer;
+ return writer.write(*this);
+}
+
+Value::const_iterator Value::begin() const {
+ switch (type_) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return const_iterator(value_.map_->begin());
+ break;
+ default:
+ break;
+ }
+ return const_iterator();
+}
+
+Value::const_iterator Value::end() const {
+ switch (type_) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return const_iterator(value_.map_->end());
+ break;
+ default:
+ break;
+ }
+ return const_iterator();
+}
+
+Value::iterator Value::begin() {
+ switch (type_) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return iterator(value_.map_->begin());
+ break;
+ default:
+ break;
+ }
+ return iterator();
+}
+
+Value::iterator Value::end() {
+ switch (type_) {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return iterator(value_.map_->end());
+ break;
+ default:
+ break;
+ }
+ return iterator();
+}
+
+// class PathArgument
+// //////////////////////////////////////////////////////////////////
+
+PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {}
+
+PathArgument::PathArgument(ArrayIndex index)
+ : key_(), index_(index), kind_(kindIndex) {}
+
+PathArgument::PathArgument(const char* key)
+ : key_(key), index_(), kind_(kindKey) {}
+
+PathArgument::PathArgument(const std::string& key)
+ : key_(key.c_str()), index_(), kind_(kindKey) {}
+
+// class Path
+// //////////////////////////////////////////////////////////////////
+
+Path::Path(const std::string& path,
+ const PathArgument& a1,
+ const PathArgument& a2,
+ const PathArgument& a3,
+ const PathArgument& a4,
+ const PathArgument& a5) {
+ InArgs in;
+ in.push_back(&a1);
+ in.push_back(&a2);
+ in.push_back(&a3);
+ in.push_back(&a4);
+ in.push_back(&a5);
+ makePath(path, in);
+}
+
+void Path::makePath(const std::string& path, const InArgs& in) {
+ const char* current = path.c_str();
+ const char* end = current + path.length();
+ InArgs::const_iterator itInArg = in.begin();
+ while (current != end) {
+ if (*current == '[') {
+ ++current;
+ if (*current == '%')
+ addPathInArg(path, in, itInArg, PathArgument::kindIndex);
+ else {
+ ArrayIndex index = 0;
+ for (; current != end && *current >= '0' && *current <= '9'; ++current)
+ index = index * 10 + ArrayIndex(*current - '0');
+ args_.push_back(index);
+ }
+ if (current == end || *current++ != ']')
+ invalidPath(path, int(current - path.c_str()));
+ } else if (*current == '%') {
+ addPathInArg(path, in, itInArg, PathArgument::kindKey);
+ ++current;
+ } else if (*current == '.') {
+ ++current;
+ } else {
+ const char* beginName = current;
+ while (current != end && !strchr("[.", *current))
+ ++current;
+ args_.push_back(std::string(beginName, current));
+ }
+ }
+}
+
+void Path::addPathInArg(const std::string& /*path*/,
+ const InArgs& in,
+ InArgs::const_iterator& itInArg,
+ PathArgument::Kind kind) {
+ if (itInArg == in.end()) {
+ // Error: missing argument %d
+ } else if ((*itInArg)->kind_ != kind) {
+ // Error: bad argument type
+ } else {
+ args_.push_back(**itInArg);
+ }
+}
+
+void Path::invalidPath(const std::string& /*path*/, int /*location*/) {
+ // Error: invalid path.
+}
+
+const Value& Path::resolve(const Value& root) const {
+ const Value* node = &root;
+ for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
+ const PathArgument& arg = *it;
+ if (arg.kind_ == PathArgument::kindIndex) {
+ if (!node->isArray() || !node->isValidIndex(arg.index_)) {
+ // Error: unable to resolve path (array value expected at position...
+ }
+ node = &((*node)[arg.index_]);
+ } else if (arg.kind_ == PathArgument::kindKey) {
+ if (!node->isObject()) {
+ // Error: unable to resolve path (object value expected at position...)
+ }
+ node = &((*node)[arg.key_]);
+ if (node == &Value::nullRef) {
+ // Error: unable to resolve path (object has no member named '' at
+ // position...)
+ }
+ }
+ }
+ return *node;
+}
+
+Value Path::resolve(const Value& root, const Value& defaultValue) const {
+ const Value* node = &root;
+ for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
+ const PathArgument& arg = *it;
+ if (arg.kind_ == PathArgument::kindIndex) {
+ if (!node->isArray() || !node->isValidIndex(arg.index_))
+ return defaultValue;
+ node = &((*node)[arg.index_]);
+ } else if (arg.kind_ == PathArgument::kindKey) {
+ if (!node->isObject())
+ return defaultValue;
+ node = &((*node)[arg.key_]);
+ if (node == &Value::nullRef)
+ return defaultValue;
+ }
+ }
+ return *node;
+}
+
+Value& Path::make(Value& root) const {
+ Value* node = &root;
+ for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
+ const PathArgument& arg = *it;
+ if (arg.kind_ == PathArgument::kindIndex) {
+ if (!node->isArray()) {
+ // Error: node is not an array at position ...
+ }
+ node = &((*node)[arg.index_]);
+ } else if (arg.kind_ == PathArgument::kindKey) {
+ if (!node->isObject()) {
+ // Error: node is not an object at position...
+ }
+ node = &((*node)[arg.key_]);
+ }
+ }
+ return *node;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/writer.h>
+#include "json_tool.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <utility>
+#include <set>
+#include <cassert>
+#include <cstring>
+#include <cstdio>
+
+#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
+#include <float.h>
+#define isfinite _finite
+#elif defined(__sun) && defined(__SVR4) //Solaris
+#if !defined(isfinite)
+#include <ieeefp.h>
+#define isfinite finite
+#endif
+#elif defined(_AIX)
+#if !defined(isfinite)
+#include <math.h>
+#define isfinite finite
+#endif
+#elif defined(__hpux)
+#if !defined(isfinite)
+#if defined(__ia64) && !defined(finite)
+#define isfinite(x) ((sizeof(x) == sizeof(float) ? \
+ _Isfinitef(x) : _IsFinite(x)))
+#else
+#include <math.h>
+#define isfinite finite
+#endif
+#endif
+#else
+#include <cmath>
+#if !(defined(__QNXNTO__)) // QNX already defines isfinite
+#define isfinite std::isfinite
+#endif
+#endif
+
+#if defined(_MSC_VER)
+#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
+#define snprintf sprintf_s
+#elif _MSC_VER >= 1900 // VC++ 14.0 and above
+#define snprintf std::snprintf
+#else
+#define snprintf _snprintf
+#endif
+#elif defined(__ANDROID__) || defined(__QNXNTO__)
+#define snprintf snprintf
+#elif __cplusplus >= 201103L
+#define snprintf std::snprintf
+#endif
+
+#if defined(__BORLANDC__)
+#include <float.h>
+#define isfinite _finite
+#define snprintf _snprintf
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+
+namespace Json {
+
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
+#else
+typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
+#endif
+
+static bool containsControlCharacter(const char* str) {
+ while (*str) {
+ if (isControlCharacter(*(str++)))
+ return true;
+ }
+ return false;
+}
+
+static bool containsControlCharacter0(const char* str, unsigned len) {
+ char const* end = str + len;
+ while (end != str) {
+ if (isControlCharacter(*str) || 0==*str)
+ return true;
+ ++str;
+ }
+ return false;
+}
+
+std::string valueToString(LargestInt value) {
+ UIntToStringBuffer buffer;
+ char* current = buffer + sizeof(buffer);
+ if (value == Value::minLargestInt) {
+ uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
+ *--current = '-';
+ } else if (value < 0) {
+ uintToString(LargestUInt(-value), current);
+ *--current = '-';
+ } else {
+ uintToString(LargestUInt(value), current);
+ }
+ assert(current >= buffer);
+ return current;
+}
+
+std::string valueToString(LargestUInt value) {
+ UIntToStringBuffer buffer;
+ char* current = buffer + sizeof(buffer);
+ uintToString(value, current);
+ assert(current >= buffer);
+ return current;
+}
+
+#if defined(JSON_HAS_INT64)
+
+std::string valueToString(Int value) {
+ return valueToString(LargestInt(value));
+}
+
+std::string valueToString(UInt value) {
+ return valueToString(LargestUInt(value));
+}
+
+#endif // # if defined(JSON_HAS_INT64)
+
+std::string valueToString(double value, bool useSpecialFloats, unsigned int precision) {
+ // Allocate a buffer that is more than large enough to store the 16 digits of
+ // precision requested below.
+ char buffer[32];
+ int len = -1;
+
+ char formatString[6];
+ sprintf(formatString, "%%.%dg", precision);
+
+ // Print into the buffer. We need not request the alternative representation
+ // that always has a decimal point because JSON doesn't distingish the
+ // concepts of reals and integers.
+ if (isfinite(value)) {
+ len = snprintf(buffer, sizeof(buffer), formatString, value);
+ } else {
+ // IEEE standard states that NaN values will not compare to themselves
+ if (value != value) {
+ len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
+ } else if (value < 0) {
+ len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
+ } else {
+ len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
+ }
+ // For those, we do not need to call fixNumLoc, but it is fast.
+ }
+ assert(len >= 0);
+ fixNumericLocale(buffer, buffer + len);
+ return buffer;
+}
+
+std::string valueToString(double value) { return valueToString(value, false, 17); }
+
+std::string valueToString(bool value) { return value ? "true" : "false"; }
+
+std::string valueToQuotedString(const char* value) {
+ if (value == NULL)
+ return "";
+ // Not sure how to handle unicode...
+ if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
+ !containsControlCharacter(value))
+ return std::string("\"") + value + "\"";
+ // We have to walk value and escape any special characters.
+ // Appending to std::string is not efficient, but this should be rare.
+ // (Note: forward slashes are *not* rare, but I am not escaping them.)
+ std::string::size_type maxsize =
+ strlen(value) * 2 + 3; // allescaped+quotes+NULL
+ std::string result;
+ result.reserve(maxsize); // to avoid lots of mallocs
+ result += "\"";
+ for (const char* c = value; *c != 0; ++c) {
+ switch (*c) {
+ case '\"':
+ result += "\\\"";
+ break;
+ case '\\':
+ result += "\\\\";
+ break;
+ case '\b':
+ result += "\\b";
+ break;
+ case '\f':
+ result += "\\f";
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\r':
+ result += "\\r";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ // case '/':
+ // Even though \/ is considered a legal escape in JSON, a bare
+ // slash is also legal, so I see no reason to escape it.
+ // (I hope I am not misunderstanding something.
+ // blep notes: actually escaping \/ may be useful in javascript to avoid </
+ // sequence.
+ // Should add a flag to allow this compatibility mode and prevent this
+ // sequence from occurring.
+ default:
+ if (isControlCharacter(*c)) {
+ std::ostringstream oss;
+ oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
+ << std::setw(4) << static_cast<int>(*c);
+ result += oss.str();
+ } else {
+ result += *c;
+ }
+ break;
+ }
+ }
+ result += "\"";
+ return result;
+}
+
+// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
+static char const* strnpbrk(char const* s, char const* accept, size_t n) {
+ assert((s || !n) && accept);
+
+ char const* const end = s + n;
+ for (char const* cur = s; cur < end; ++cur) {
+ int const c = *cur;
+ for (char const* a = accept; *a; ++a) {
+ if (*a == c) {
+ return cur;
+ }
+ }
+ }
+ return NULL;
+}
+static std::string valueToQuotedStringN(const char* value, unsigned length) {
+ if (value == NULL)
+ return "";
+ // Not sure how to handle unicode...
+ if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
+ !containsControlCharacter0(value, length))
+ return std::string("\"") + value + "\"";
+ // We have to walk value and escape any special characters.
+ // Appending to std::string is not efficient, but this should be rare.
+ // (Note: forward slashes are *not* rare, but I am not escaping them.)
+ std::string::size_type maxsize =
+ length * 2 + 3; // allescaped+quotes+NULL
+ std::string result;
+ result.reserve(maxsize); // to avoid lots of mallocs
+ result += "\"";
+ char const* end = value + length;
+ for (const char* c = value; c != end; ++c) {
+ switch (*c) {
+ case '\"':
+ result += "\\\"";
+ break;
+ case '\\':
+ result += "\\\\";
+ break;
+ case '\b':
+ result += "\\b";
+ break;
+ case '\f':
+ result += "\\f";
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\r':
+ result += "\\r";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ // case '/':
+ // Even though \/ is considered a legal escape in JSON, a bare
+ // slash is also legal, so I see no reason to escape it.
+ // (I hope I am not misunderstanding something.)
+ // blep notes: actually escaping \/ may be useful in javascript to avoid </
+ // sequence.
+ // Should add a flag to allow this compatibility mode and prevent this
+ // sequence from occurring.
+ default:
+ if ((isControlCharacter(*c)) || (*c == 0)) {
+ std::ostringstream oss;
+ oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
+ << std::setw(4) << static_cast<int>(*c);
+ result += oss.str();
+ } else {
+ result += *c;
+ }
+ break;
+ }
+ }
+ result += "\"";
+ return result;
+}
+
+// Class Writer
+// //////////////////////////////////////////////////////////////////
+Writer::~Writer() {}
+
+// Class FastWriter
+// //////////////////////////////////////////////////////////////////
+
+FastWriter::FastWriter()
+ : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
+ omitEndingLineFeed_(false) {}
+
+void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
+
+void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
+
+void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
+
+std::string FastWriter::write(const Value& root) {
+ document_ = "";
+ writeValue(root);
+ if (!omitEndingLineFeed_)
+ document_ += "\n";
+ return document_;
+}
+
+void FastWriter::writeValue(const Value& value) {
+ switch (value.type()) {
+ case nullValue:
+ if (!dropNullPlaceholders_)
+ document_ += "null";
+ break;
+ case intValue:
+ document_ += valueToString(value.asLargestInt());
+ break;
+ case uintValue:
+ document_ += valueToString(value.asLargestUInt());
+ break;
+ case realValue:
+ document_ += valueToString(value.asDouble());
+ break;
+ case stringValue:
+ {
+ // Is NULL possible for value.string_?
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
+ break;
+ }
+ case booleanValue:
+ document_ += valueToString(value.asBool());
+ break;
+ case arrayValue: {
+ document_ += '[';
+ int size = value.size();
+ for (int index = 0; index < size; ++index) {
+ if (index > 0)
+ document_ += ',';
+ writeValue(value[index]);
+ }
+ document_ += ']';
+ } break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ document_ += '{';
+ for (Value::Members::iterator it = members.begin(); it != members.end();
+ ++it) {
+ const std::string& name = *it;
+ if (it != members.begin())
+ document_ += ',';
+ document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
+ document_ += yamlCompatiblityEnabled_ ? ": " : ":";
+ writeValue(value[name]);
+ }
+ document_ += '}';
+ } break;
+ }
+}
+
+// Class StyledWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledWriter::StyledWriter()
+ : rightMargin_(74), indentSize_(3), addChildValues_() {}
+
+std::string StyledWriter::write(const Value& root) {
+ document_ = "";
+ addChildValues_ = false;
+ indentString_ = "";
+ writeCommentBeforeValue(root);
+ writeValue(root);
+ writeCommentAfterValueOnSameLine(root);
+ document_ += "\n";
+ return document_;
+}
+
+void StyledWriter::writeValue(const Value& value) {
+ switch (value.type()) {
+ case nullValue:
+ pushValue("null");
+ break;
+ case intValue:
+ pushValue(valueToString(value.asLargestInt()));
+ break;
+ case uintValue:
+ pushValue(valueToString(value.asLargestUInt()));
+ break;
+ case realValue:
+ pushValue(valueToString(value.asDouble()));
+ break;
+ case stringValue:
+ {
+ // Is NULL possible for value.string_?
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
+ else pushValue("");
+ break;
+ }
+ case booleanValue:
+ pushValue(valueToString(value.asBool()));
+ break;
+ case arrayValue:
+ writeArrayValue(value);
+ break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ if (members.empty())
+ pushValue("{}");
+ else {
+ writeWithIndent("{");
+ indent();
+ Value::Members::iterator it = members.begin();
+ for (;;) {
+ const std::string& name = *it;
+ const Value& childValue = value[name];
+ writeCommentBeforeValue(childValue);
+ writeWithIndent(valueToQuotedString(name.c_str()));
+ document_ += " : ";
+ writeValue(childValue);
+ if (++it == members.end()) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ document_ += ',';
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("}");
+ }
+ } break;
+ }
+}
+
+void StyledWriter::writeArrayValue(const Value& value) {
+ unsigned size = value.size();
+ if (size == 0)
+ pushValue("[]");
+ else {
+ bool isArrayMultiLine = isMultineArray(value);
+ if (isArrayMultiLine) {
+ writeWithIndent("[");
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index = 0;
+ for (;;) {
+ const Value& childValue = value[index];
+ writeCommentBeforeValue(childValue);
+ if (hasChildValue)
+ writeWithIndent(childValues_[index]);
+ else {
+ writeIndent();
+ writeValue(childValue);
+ }
+ if (++index == size) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ document_ += ',';
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("]");
+ } else // output on a single line
+ {
+ assert(childValues_.size() == size);
+ document_ += "[ ";
+ for (unsigned index = 0; index < size; ++index) {
+ if (index > 0)
+ document_ += ", ";
+ document_ += childValues_[index];
+ }
+ document_ += " ]";
+ }
+ }
+}
+
+bool StyledWriter::isMultineArray(const Value& value) {
+ int size = value.size();
+ bool isMultiLine = size * 3 >= rightMargin_;
+ childValues_.clear();
+ for (int index = 0; index < size && !isMultiLine; ++index) {
+ const Value& childValue = value[index];
+ isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+ childValue.size() > 0);
+ }
+ if (!isMultiLine) // check if line length > max line length
+ {
+ childValues_.reserve(size);
+ addChildValues_ = true;
+ int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+ for (int index = 0; index < size; ++index) {
+ if (hasCommentForValue(value[index])) {
+ isMultiLine = true;
+ }
+ writeValue(value[index]);
+ lineLength += int(childValues_[index].length());
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+void StyledWriter::pushValue(const std::string& value) {
+ if (addChildValues_)
+ childValues_.push_back(value);
+ else
+ document_ += value;
+}
+
+void StyledWriter::writeIndent() {
+ if (!document_.empty()) {
+ char last = document_[document_.length() - 1];
+ if (last == ' ') // already indented
+ return;
+ if (last != '\n') // Comments may add new-line
+ document_ += '\n';
+ }
+ document_ += indentString_;
+}
+
+void StyledWriter::writeWithIndent(const std::string& value) {
+ writeIndent();
+ document_ += value;
+}
+
+void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
+
+void StyledWriter::unindent() {
+ assert(int(indentString_.size()) >= indentSize_);
+ indentString_.resize(indentString_.size() - indentSize_);
+}
+
+void StyledWriter::writeCommentBeforeValue(const Value& root) {
+ if (!root.hasComment(commentBefore))
+ return;
+
+ document_ += "\n";
+ writeIndent();
+ const std::string& comment = root.getComment(commentBefore);
+ std::string::const_iterator iter = comment.begin();
+ while (iter != comment.end()) {
+ document_ += *iter;
+ if (*iter == '\n' &&
+ (iter != comment.end() && *(iter + 1) == '/'))
+ writeIndent();
+ ++iter;
+ }
+
+ // Comments are stripped of trailing newlines, so add one here
+ document_ += "\n";
+}
+
+void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+ if (root.hasComment(commentAfterOnSameLine))
+ document_ += " " + root.getComment(commentAfterOnSameLine);
+
+ if (root.hasComment(commentAfter)) {
+ document_ += "\n";
+ document_ += root.getComment(commentAfter);
+ document_ += "\n";
+ }
+}
+
+bool StyledWriter::hasCommentForValue(const Value& value) {
+ return value.hasComment(commentBefore) ||
+ value.hasComment(commentAfterOnSameLine) ||
+ value.hasComment(commentAfter);
+}
+
+// Class StyledStreamWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledStreamWriter::StyledStreamWriter(std::string indentation)
+ : document_(NULL), rightMargin_(74), indentation_(indentation),
+ addChildValues_() {}
+
+void StyledStreamWriter::write(std::ostream& out, const Value& root) {
+ document_ = &out;
+ addChildValues_ = false;
+ indentString_ = "";
+ indented_ = true;
+ writeCommentBeforeValue(root);
+ if (!indented_) writeIndent();
+ indented_ = true;
+ writeValue(root);
+ writeCommentAfterValueOnSameLine(root);
+ *document_ << "\n";
+ document_ = NULL; // Forget the stream, for safety.
+}
+
+void StyledStreamWriter::writeValue(const Value& value) {
+ switch (value.type()) {
+ case nullValue:
+ pushValue("null");
+ break;
+ case intValue:
+ pushValue(valueToString(value.asLargestInt()));
+ break;
+ case uintValue:
+ pushValue(valueToString(value.asLargestUInt()));
+ break;
+ case realValue:
+ pushValue(valueToString(value.asDouble()));
+ break;
+ case stringValue:
+ {
+ // Is NULL possible for value.string_?
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
+ else pushValue("");
+ break;
+ }
+ case booleanValue:
+ pushValue(valueToString(value.asBool()));
+ break;
+ case arrayValue:
+ writeArrayValue(value);
+ break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ if (members.empty())
+ pushValue("{}");
+ else {
+ writeWithIndent("{");
+ indent();
+ Value::Members::iterator it = members.begin();
+ for (;;) {
+ const std::string& name = *it;
+ const Value& childValue = value[name];
+ writeCommentBeforeValue(childValue);
+ writeWithIndent(valueToQuotedString(name.c_str()));
+ *document_ << " : ";
+ writeValue(childValue);
+ if (++it == members.end()) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *document_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("}");
+ }
+ } break;
+ }
+}
+
+void StyledStreamWriter::writeArrayValue(const Value& value) {
+ unsigned size = value.size();
+ if (size == 0)
+ pushValue("[]");
+ else {
+ bool isArrayMultiLine = isMultineArray(value);
+ if (isArrayMultiLine) {
+ writeWithIndent("[");
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index = 0;
+ for (;;) {
+ const Value& childValue = value[index];
+ writeCommentBeforeValue(childValue);
+ if (hasChildValue)
+ writeWithIndent(childValues_[index]);
+ else {
+ if (!indented_) writeIndent();
+ indented_ = true;
+ writeValue(childValue);
+ indented_ = false;
+ }
+ if (++index == size) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *document_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("]");
+ } else // output on a single line
+ {
+ assert(childValues_.size() == size);
+ *document_ << "[ ";
+ for (unsigned index = 0; index < size; ++index) {
+ if (index > 0)
+ *document_ << ", ";
+ *document_ << childValues_[index];
+ }
+ *document_ << " ]";
+ }
+ }
+}
+
+bool StyledStreamWriter::isMultineArray(const Value& value) {
+ int size = value.size();
+ bool isMultiLine = size * 3 >= rightMargin_;
+ childValues_.clear();
+ for (int index = 0; index < size && !isMultiLine; ++index) {
+ const Value& childValue = value[index];
+ isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+ childValue.size() > 0);
+ }
+ if (!isMultiLine) // check if line length > max line length
+ {
+ childValues_.reserve(size);
+ addChildValues_ = true;
+ int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+ for (int index = 0; index < size; ++index) {
+ if (hasCommentForValue(value[index])) {
+ isMultiLine = true;
+ }
+ writeValue(value[index]);
+ lineLength += int(childValues_[index].length());
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+void StyledStreamWriter::pushValue(const std::string& value) {
+ if (addChildValues_)
+ childValues_.push_back(value);
+ else
+ *document_ << value;
+}
+
+void StyledStreamWriter::writeIndent() {
+ // blep intended this to look at the so-far-written string
+ // to determine whether we are already indented, but
+ // with a stream we cannot do that. So we rely on some saved state.
+ // The caller checks indented_.
+ *document_ << '\n' << indentString_;
+}
+
+void StyledStreamWriter::writeWithIndent(const std::string& value) {
+ if (!indented_) writeIndent();
+ *document_ << value;
+ indented_ = false;
+}
+
+void StyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void StyledStreamWriter::unindent() {
+ assert(indentString_.size() >= indentation_.size());
+ indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
+ if (!root.hasComment(commentBefore))
+ return;
+
+ if (!indented_) writeIndent();
+ const std::string& comment = root.getComment(commentBefore);
+ std::string::const_iterator iter = comment.begin();
+ while (iter != comment.end()) {
+ *document_ << *iter;
+ if (*iter == '\n' &&
+ (iter != comment.end() && *(iter + 1) == '/'))
+ // writeIndent(); // would include newline
+ *document_ << indentString_;
+ ++iter;
+ }
+ indented_ = false;
+}
+
+void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+ if (root.hasComment(commentAfterOnSameLine))
+ *document_ << ' ' << root.getComment(commentAfterOnSameLine);
+
+ if (root.hasComment(commentAfter)) {
+ writeIndent();
+ *document_ << root.getComment(commentAfter);
+ }
+ indented_ = false;
+}
+
+bool StyledStreamWriter::hasCommentForValue(const Value& value) {
+ return value.hasComment(commentBefore) ||
+ value.hasComment(commentAfterOnSameLine) ||
+ value.hasComment(commentAfter);
+}
+
+//////////////////////////
+// BuiltStyledStreamWriter
+
+/// Scoped enums are not available until C++11.
+struct CommentStyle {
+ /// Decide whether to write comments.
+ enum Enum {
+ None, ///< Drop all comments.
+ Most, ///< Recover odd behavior of previous versions (not implemented yet).
+ All ///< Keep all comments.
+ };
+};
+
+struct BuiltStyledStreamWriter : public StreamWriter
+{
+ BuiltStyledStreamWriter(
+ std::string const& indentation,
+ CommentStyle::Enum cs,
+ std::string const& colonSymbol,
+ std::string const& nullSymbol,
+ std::string const& endingLineFeedSymbol,
+ bool useSpecialFloats,
+ unsigned int precision);
+ int write(Value const& root, std::ostream* sout) override;
+private:
+ void writeValue(Value const& value);
+ void writeArrayValue(Value const& value);
+ bool isMultineArray(Value const& value);
+ void pushValue(std::string const& value);
+ void writeIndent();
+ void writeWithIndent(std::string const& value);
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue(Value const& root);
+ void writeCommentAfterValueOnSameLine(Value const& root);
+ static bool hasCommentForValue(const Value& value);
+
+ typedef std::vector<std::string> ChildValues;
+
+ ChildValues childValues_;
+ std::string indentString_;
+ int rightMargin_;
+ std::string indentation_;
+ CommentStyle::Enum cs_;
+ std::string colonSymbol_;
+ std::string nullSymbol_;
+ std::string endingLineFeedSymbol_;
+ bool addChildValues_ : 1;
+ bool indented_ : 1;
+ bool useSpecialFloats_ : 1;
+ unsigned int precision_;
+};
+BuiltStyledStreamWriter::BuiltStyledStreamWriter(
+ std::string const& indentation,
+ CommentStyle::Enum cs,
+ std::string const& colonSymbol,
+ std::string const& nullSymbol,
+ std::string const& endingLineFeedSymbol,
+ bool useSpecialFloats,
+ unsigned int precision)
+ : rightMargin_(74)
+ , indentation_(indentation)
+ , cs_(cs)
+ , colonSymbol_(colonSymbol)
+ , nullSymbol_(nullSymbol)
+ , endingLineFeedSymbol_(endingLineFeedSymbol)
+ , addChildValues_(false)
+ , indented_(false)
+ , useSpecialFloats_(useSpecialFloats)
+ , precision_(precision)
+{
+}
+int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
+{
+ sout_ = sout;
+ addChildValues_ = false;
+ indented_ = true;
+ indentString_ = "";
+ writeCommentBeforeValue(root);
+ if (!indented_) writeIndent();
+ indented_ = true;
+ writeValue(root);
+ writeCommentAfterValueOnSameLine(root);
+ *sout_ << endingLineFeedSymbol_;
+ sout_ = NULL;
+ return 0;
+}
+void BuiltStyledStreamWriter::writeValue(Value const& value) {
+ switch (value.type()) {
+ case nullValue:
+ pushValue(nullSymbol_);
+ break;
+ case intValue:
+ pushValue(valueToString(value.asLargestInt()));
+ break;
+ case uintValue:
+ pushValue(valueToString(value.asLargestUInt()));
+ break;
+ case realValue:
+ pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
+ break;
+ case stringValue:
+ {
+ // Is NULL is possible for value.string_?
+ char const* str;
+ char const* end;
+ bool ok = value.getString(&str, &end);
+ if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
+ else pushValue("");
+ break;
+ }
+ case booleanValue:
+ pushValue(valueToString(value.asBool()));
+ break;
+ case arrayValue:
+ writeArrayValue(value);
+ break;
+ case objectValue: {
+ Value::Members members(value.getMemberNames());
+ if (members.empty())
+ pushValue("{}");
+ else {
+ writeWithIndent("{");
+ indent();
+ Value::Members::iterator it = members.begin();
+ for (;;) {
+ std::string const& name = *it;
+ Value const& childValue = value[name];
+ writeCommentBeforeValue(childValue);
+ writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
+ *sout_ << colonSymbol_;
+ writeValue(childValue);
+ if (++it == members.end()) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *sout_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("}");
+ }
+ } break;
+ }
+}
+
+void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
+ unsigned size = value.size();
+ if (size == 0)
+ pushValue("[]");
+ else {
+ bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
+ if (isMultiLine) {
+ writeWithIndent("[");
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index = 0;
+ for (;;) {
+ Value const& childValue = value[index];
+ writeCommentBeforeValue(childValue);
+ if (hasChildValue)
+ writeWithIndent(childValues_[index]);
+ else {
+ if (!indented_) writeIndent();
+ indented_ = true;
+ writeValue(childValue);
+ indented_ = false;
+ }
+ if (++index == size) {
+ writeCommentAfterValueOnSameLine(childValue);
+ break;
+ }
+ *sout_ << ",";
+ writeCommentAfterValueOnSameLine(childValue);
+ }
+ unindent();
+ writeWithIndent("]");
+ } else // output on a single line
+ {
+ assert(childValues_.size() == size);
+ *sout_ << "[";
+ if (!indentation_.empty()) *sout_ << " ";
+ for (unsigned index = 0; index < size; ++index) {
+ if (index > 0)
+ *sout_ << ", ";
+ *sout_ << childValues_[index];
+ }
+ if (!indentation_.empty()) *sout_ << " ";
+ *sout_ << "]";
+ }
+ }
+}
+
+bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
+ int size = value.size();
+ bool isMultiLine = size * 3 >= rightMargin_;
+ childValues_.clear();
+ for (int index = 0; index < size && !isMultiLine; ++index) {
+ Value const& childValue = value[index];
+ isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+ childValue.size() > 0);
+ }
+ if (!isMultiLine) // check if line length > max line length
+ {
+ childValues_.reserve(size);
+ addChildValues_ = true;
+ int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+ for (int index = 0; index < size; ++index) {
+ if (hasCommentForValue(value[index])) {
+ isMultiLine = true;
+ }
+ writeValue(value[index]);
+ lineLength += int(childValues_[index].length());
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+void BuiltStyledStreamWriter::pushValue(std::string const& value) {
+ if (addChildValues_)
+ childValues_.push_back(value);
+ else
+ *sout_ << value;
+}
+
+void BuiltStyledStreamWriter::writeIndent() {
+ // blep intended this to look at the so-far-written string
+ // to determine whether we are already indented, but
+ // with a stream we cannot do that. So we rely on some saved state.
+ // The caller checks indented_.
+
+ if (!indentation_.empty()) {
+ // In this case, drop newlines too.
+ *sout_ << '\n' << indentString_;
+ }
+}
+
+void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {
+ if (!indented_) writeIndent();
+ *sout_ << value;
+ indented_ = false;
+}
+
+void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void BuiltStyledStreamWriter::unindent() {
+ assert(indentString_.size() >= indentation_.size());
+ indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
+ if (cs_ == CommentStyle::None) return;
+ if (!root.hasComment(commentBefore))
+ return;
+
+ if (!indented_) writeIndent();
+ const std::string& comment = root.getComment(commentBefore);
+ std::string::const_iterator iter = comment.begin();
+ while (iter != comment.end()) {
+ *sout_ << *iter;
+ if (*iter == '\n' &&
+ (iter != comment.end() && *(iter + 1) == '/'))
+ // writeIndent(); // would write extra newline
+ *sout_ << indentString_;
+ ++iter;
+ }
+ indented_ = false;
+}
+
+void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
+ if (cs_ == CommentStyle::None) return;
+ if (root.hasComment(commentAfterOnSameLine))
+ *sout_ << " " + root.getComment(commentAfterOnSameLine);
+
+ if (root.hasComment(commentAfter)) {
+ writeIndent();
+ *sout_ << root.getComment(commentAfter);
+ }
+}
+
+// static
+bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
+ return value.hasComment(commentBefore) ||
+ value.hasComment(commentAfterOnSameLine) ||
+ value.hasComment(commentAfter);
+}
+
+///////////////
+// StreamWriter
+
+StreamWriter::StreamWriter()
+ : sout_(NULL)
+{
+}
+StreamWriter::~StreamWriter()
+{
+}
+StreamWriter::Factory::~Factory()
+{}
+StreamWriterBuilder::StreamWriterBuilder()
+{
+ setDefaults(&settings_);
+}
+StreamWriterBuilder::~StreamWriterBuilder()
+{}
+StreamWriter* StreamWriterBuilder::newStreamWriter() const
+{
+ std::string indentation = settings_["indentation"].asString();
+ std::string cs_str = settings_["commentStyle"].asString();
+ bool eyc = settings_["enableYAMLCompatibility"].asBool();
+ bool dnp = settings_["dropNullPlaceholders"].asBool();
+ bool usf = settings_["useSpecialFloats"].asBool();
+ unsigned int pre = settings_["precision"].asUInt();
+ CommentStyle::Enum cs = CommentStyle::All;
+ if (cs_str == "All") {
+ cs = CommentStyle::All;
+ } else if (cs_str == "None") {
+ cs = CommentStyle::None;
+ } else {
+ throwRuntimeError("commentStyle must be 'All' or 'None'");
+ }
+ std::string colonSymbol = " : ";
+ if (eyc) {
+ colonSymbol = ": ";
+ } else if (indentation.empty()) {
+ colonSymbol = ":";
+ }
+ std::string nullSymbol = "null";
+ if (dnp) {
+ nullSymbol = "";
+ }
+ if (pre > 17) pre = 17;
+ std::string endingLineFeedSymbol = "";
+ return new BuiltStyledStreamWriter(
+ indentation, cs,
+ colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
+}
+static void getValidWriterKeys(std::set<std::string>* valid_keys)
+{
+ valid_keys->clear();
+ valid_keys->insert("indentation");
+ valid_keys->insert("commentStyle");
+ valid_keys->insert("enableYAMLCompatibility");
+ valid_keys->insert("dropNullPlaceholders");
+ valid_keys->insert("useSpecialFloats");
+ valid_keys->insert("precision");
+}
+bool StreamWriterBuilder::validate(Json::Value* invalid) const
+{
+ Json::Value my_invalid;
+ if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL
+ Json::Value& inv = *invalid;
+ std::set<std::string> valid_keys;
+ getValidWriterKeys(&valid_keys);
+ Value::Members keys = settings_.getMemberNames();
+ size_t n = keys.size();
+ for (size_t i = 0; i < n; ++i) {
+ std::string const& key = keys[i];
+ if (valid_keys.find(key) == valid_keys.end()) {
+ inv[key] = settings_[key];
+ }
+ }
+ return 0u == inv.size();
+}
+Value& StreamWriterBuilder::operator[](std::string key)
+{
+ return settings_[key];
+}
+// static
+void StreamWriterBuilder::setDefaults(Json::Value* settings)
+{
+ //! [StreamWriterBuilderDefaults]
+ (*settings)["commentStyle"] = "All";
+ (*settings)["indentation"] = "\t";
+ (*settings)["enableYAMLCompatibility"] = false;
+ (*settings)["dropNullPlaceholders"] = false;
+ (*settings)["useSpecialFloats"] = false;
+ (*settings)["precision"] = 17;
+ //! [StreamWriterBuilderDefaults]
+}
+
+std::string writeString(StreamWriter::Factory const& builder, Value const& root) {
+ std::ostringstream sout;
+ StreamWriterPtr const writer(builder.newStreamWriter());
+ writer->write(root, &sout);
+ return sout.str();
+}
+
+std::ostream& operator<<(std::ostream& sout, Value const& root) {
+ StreamWriterBuilder builder;
+ StreamWriterPtr const writer(builder.newStreamWriter());
+ writer->write(root, &sout);
+ return sout;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
diff --git a/csharp/CHANGES.txt b/csharp/CHANGES.txt
index b29debb8..a87cd4d5 100644
--- a/csharp/CHANGES.txt
+++ b/csharp/CHANGES.txt
@@ -128,7 +128,7 @@ Fixes:
- Issue 12: default value for enumerate fields must be filled out
Other:
-- Rewrite of build using MSBbuild instead of NAnt
+- Rewrite of build using MSbuild instead of NAnt
- Moved to NUnit Version 2.2.8.0
- Changed to using secure .snk for releases
diff --git a/csharp/README.md b/csharp/README.md
index 305c44b6..8c3993e0 100644
--- a/csharp/README.md
+++ b/csharp/README.md
@@ -1,18 +1,7 @@
This directory contains the C# Protocol Buffers runtime library.
-Status: Alpha - ready for early adopters
-========================================
-
-This code is still under significant churn. Unlike the original port,
-it only supports proto3 (but not *all* of proto3 yet) - there are no
-unknown fields or extensions, for example. protoc will (eventually)
-deliberately fail if it is asked to generate C# code for proto2
-messages other than descriptor.proto, which is still required for
-reflection. (It's currently exposed publicly, but won't be
-eventually.)
-
-Also unlike the original port, the new version embraces mutability -
-there are no builder types.
+Status: Beta - ready for external testing
+=========================================
Usage
=====
@@ -36,14 +25,25 @@ The runtime library is built as a portable class library, supporting:
- Windows 8
- Windows Phone Silverlight 8
- Windows Phone 8.1
-- .NET Core (dnxcore)
+- .NET Core
+
+You should be able to use Protocol Buffers in Visual Studio 2012 and
+all later versions. This includes all code generated by `protoc`,
+which only uses features from C# 3 and earlier.
Building
========
-Open the `src/Google.Protobuf.sln` solution in Visual Studio. Click "Build solution" to build the solution. You should be able to run the NUnit test from Test Explorer (you might need to install NUnit Visual Studio add-in).
+Open the `src/Google.Protobuf.sln` solution in Visual Studio 2015 or
+later. You should be able to run the NUnit test from Test Explorer
+(you might need to install NUnit Visual Studio add-in).
-Supported Visual Studio versions are VS2013 (update 4) and VS2015. On Linux, you can also use Monodevelop 5.9 (older versions might work fine).
+Although *users* of this project are only expected to have Visual
+Studio 2012 or later, *developers* of the library are required to
+have Visual Studio 2015 or later, as the library uses C# 6 features
+in its implementation. These features have no impact when using the
+compiled code - they're only relevant when building the
+`Google.Protobuf` assembly.
History of C# protobufs
=======================
@@ -52,3 +52,19 @@ This subtree was originally imported from https://github.com/jskeet/protobuf-csh
and represents the latest development version of C# protobufs, that will now be developed
and maintained by Google. All the development will be done in open, under this repository
(https://github.com/google/protobuf).
+
+The previous project differs from this project in a number of ways:
+
+- The old code only supported proto2; the new code only supports
+proto3 (so no unknown fields, no required/optional distinction, no
+extensions)
+- The old code was based on immutable message types and builders for
+them
+- The old code did not support maps or `oneof`
+- The old code had its own JSON representation, whereas the new code
+uses the standard protobuf JSON representation
+- The old code had no notion of the "well-known types" which have
+special support in the new code
+- The old project supported some older platforms (such as older
+versions of Silverlight) which are not currently supported in the
+new project
diff --git a/csharp/generate_protos.sh b/csharp/generate_protos.sh
index 3a556b0e..9899097d 100755
--- a/csharp/generate_protos.sh
+++ b/csharp/generate_protos.sh
@@ -3,16 +3,6 @@
# You first need to make sure protoc has been built (see instructions on
# building protoc in root of this repository)
-# This script performs a few fix-ups as part of generation. These are:
-# - descriptor.proto is renamed to descriptor_proto_file.proto before
-# generation, to avoid the naming collision between the class for the file
-# descriptor and its Descriptor property
-# - This change also impacts UnittestCustomOptions, which expects to
-# use a class of Descriptor when it's actually been renamed to
-# DescriptorProtoFile.
-# - Issue 307 (codegen for double-nested types) breaks Unittest.proto and
-# its lite equivalents.
-
set -ex
# cd to repository root
@@ -68,5 +58,5 @@ $PROTOC -Icsharp/protos --csharp_out=csharp/src/Google.Protobuf.Test \
$PROTOC -Iexamples --csharp_out=csharp/src/AddressBook \
examples/addressbook.proto
-$PROTOC -Iconformance --csharp_out=csharp/src/Google.Protobuf.Conformance \
+$PROTOC -Iconformance -Isrc --csharp_out=csharp/src/Google.Protobuf.Conformance \
conformance/conformance.proto
diff --git a/csharp/keys/Google.Protobuf.snk b/csharp/keys/Google.Protobuf.snk
new file mode 100644
index 00000000..7515443c
--- /dev/null
+++ b/csharp/keys/Google.Protobuf.snk
Binary files differ
diff --git a/csharp/keys/README.md b/csharp/keys/README.md
index 1a014198..ede67357 100644
--- a/csharp/keys/README.md
+++ b/csharp/keys/README.md
@@ -2,4 +2,8 @@ Contents
--------
- Google.Protobuf.public.snk:
- Public key to verify strong name of Google.Protobuf assemblies. \ No newline at end of file
+ Public key to verify strong name of Google.Protobuf assemblies.
+- Google.Protobuf.snk:
+ Signing key to provide strong name of Google.Protobuf assemblies.
+ As per [Microsoft guidance](https://msdn.microsoft.com/en-us/library/wd40t7ad(v=vs.110).aspx)
+ signing key should be checked into the repository.
diff --git a/csharp/src/AddressBook/Addressbook.cs b/csharp/src/AddressBook/Addressbook.cs
index 02dbc8e4..166dd49a 100644
--- a/csharp/src/AddressBook/Addressbook.cs
+++ b/csharp/src/AddressBook/Addressbook.cs
@@ -32,17 +32,20 @@ namespace Google.Protobuf.Examples.AddressBook {
"GAEgAygLMhAudHV0b3JpYWwuUGVyc29uQlAKFGNvbS5leGFtcGxlLnR1dG9y",
"aWFsQhFBZGRyZXNzQm9va1Byb3Rvc6oCJEdvb2dsZS5Qcm90b2J1Zi5FeGFt",
"cGxlcy5BZGRyZXNzQm9va2IGcHJvdG8z"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person), global::Google.Protobuf.Examples.AddressBook.Person.Parser, new[]{ "Name", "Id", "Email", "Phones" }, null, new[]{ typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) }, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser, new[]{ "Number", "Type" }, null, null, null)}),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), global::Google.Protobuf.Examples.AddressBook.AddressBook.Parser, new[]{ "People" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person), global::Google.Protobuf.Examples.AddressBook.Person.Parser, new[]{ "Name", "Id", "Email", "Phones" }, null, new[]{ typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser, new[]{ "Number", "Type" }, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), global::Google.Protobuf.Examples.AddressBook.AddressBook.Parser, new[]{ "People" }, null, null, null)
}));
}
#endregion
}
#region Messages
+ /// <summary>
+ /// [START messages]
+ /// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Person : pb::IMessage<Person> {
private static readonly pb::MessageParser<Person> _parser = new pb::MessageParser<Person>(() => new Person());
@@ -79,7 +82,7 @@ namespace Google.Protobuf.Examples.AddressBook {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -102,7 +105,7 @@ namespace Google.Protobuf.Examples.AddressBook {
public string Email {
get { return email_; }
set {
- email_ = pb::Preconditions.CheckNotNull(value, "value");
+ email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -143,7 +146,7 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -264,7 +267,7 @@ namespace Google.Protobuf.Examples.AddressBook {
public string Number {
get { return number_; }
set {
- number_ = pb::Preconditions.CheckNotNull(value, "value");
+ number_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -302,7 +305,7 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -426,7 +429,7 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Conformance/Conformance.cs b/csharp/src/Google.Protobuf.Conformance/Conformance.cs
index b9ab4820..7d85d28c 100644
--- a/csharp/src/Google.Protobuf.Conformance/Conformance.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Conformance.cs
@@ -23,125 +23,175 @@ namespace Conformance {
static ConformanceReflection() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
- "ChFjb25mb3JtYW5jZS5wcm90bxILY29uZm9ybWFuY2UijQEKEkNvbmZvcm1h",
- "bmNlUmVxdWVzdBIaChBwcm90b2J1Zl9wYXlsb2FkGAEgASgMSAASFgoManNv",
- "bl9wYXlsb2FkGAIgASgJSAASOAoXcmVxdWVzdGVkX291dHB1dF9mb3JtYXQY",
- "AyABKA4yFy5jb25mb3JtYW5jZS5XaXJlRm9ybWF0QgkKB3BheWxvYWQilgEK",
- "E0NvbmZvcm1hbmNlUmVzcG9uc2USFQoLcGFyc2VfZXJyb3IYASABKAlIABIX",
- "Cg1ydW50aW1lX2Vycm9yGAIgASgJSAASGgoQcHJvdG9idWZfcGF5bG9hZBgD",
- "IAEoDEgAEhYKDGpzb25fcGF5bG9hZBgEIAEoCUgAEhEKB3NraXBwZWQYBSAB",
- "KAlIAEIICgZyZXN1bHQi6yIKDFRlc3RBbGxUeXBlcxIWCg5vcHRpb25hbF9p",
- "bnQzMhgBIAEoBRIWCg5vcHRpb25hbF9pbnQ2NBgCIAEoAxIXCg9vcHRpb25h",
- "bF91aW50MzIYAyABKA0SFwoPb3B0aW9uYWxfdWludDY0GAQgASgEEhcKD29w",
- "dGlvbmFsX3NpbnQzMhgFIAEoERIXCg9vcHRpb25hbF9zaW50NjQYBiABKBIS",
- "GAoQb3B0aW9uYWxfZml4ZWQzMhgHIAEoBxIYChBvcHRpb25hbF9maXhlZDY0",
- "GAggASgGEhkKEW9wdGlvbmFsX3NmaXhlZDMyGAkgASgPEhkKEW9wdGlvbmFs",
- "X3NmaXhlZDY0GAogASgQEhYKDm9wdGlvbmFsX2Zsb2F0GAsgASgCEhcKD29w",
- "dGlvbmFsX2RvdWJsZRgMIAEoARIVCg1vcHRpb25hbF9ib29sGA0gASgIEhcK",
- "D29wdGlvbmFsX3N0cmluZxgOIAEoCRIWCg5vcHRpb25hbF9ieXRlcxgPIAEo",
- "DBJIChdvcHRpb25hbF9uZXN0ZWRfbWVzc2FnZRgSIAEoCzInLmNvbmZvcm1h",
- "bmNlLlRlc3RBbGxUeXBlcy5OZXN0ZWRNZXNzYWdlEj0KGG9wdGlvbmFsX2Zv",
- "cmVpZ25fbWVzc2FnZRgTIAEoCzIbLmNvbmZvcm1hbmNlLkZvcmVpZ25NZXNz",
- "YWdlEkIKFG9wdGlvbmFsX25lc3RlZF9lbnVtGBUgASgOMiQuY29uZm9ybWFu",
- "Y2UuVGVzdEFsbFR5cGVzLk5lc3RlZEVudW0SNwoVb3B0aW9uYWxfZm9yZWln",
- "bl9lbnVtGBYgASgOMhguY29uZm9ybWFuY2UuRm9yZWlnbkVudW0SIQoVb3B0",
- "aW9uYWxfc3RyaW5nX3BpZWNlGBggASgJQgIIAhIZCg1vcHRpb25hbF9jb3Jk",
- "GBkgASgJQgIIARI0ChFyZWN1cnNpdmVfbWVzc2FnZRgbIAEoCzIZLmNvbmZv",
- "cm1hbmNlLlRlc3RBbGxUeXBlcxIWCg5yZXBlYXRlZF9pbnQzMhgfIAMoBRIW",
- "Cg5yZXBlYXRlZF9pbnQ2NBggIAMoAxIXCg9yZXBlYXRlZF91aW50MzIYISAD",
- "KA0SFwoPcmVwZWF0ZWRfdWludDY0GCIgAygEEhcKD3JlcGVhdGVkX3NpbnQz",
- "MhgjIAMoERIXCg9yZXBlYXRlZF9zaW50NjQYJCADKBISGAoQcmVwZWF0ZWRf",
- "Zml4ZWQzMhglIAMoBxIYChByZXBlYXRlZF9maXhlZDY0GCYgAygGEhkKEXJl",
- "cGVhdGVkX3NmaXhlZDMyGCcgAygPEhkKEXJlcGVhdGVkX3NmaXhlZDY0GCgg",
- "AygQEhYKDnJlcGVhdGVkX2Zsb2F0GCkgAygCEhcKD3JlcGVhdGVkX2RvdWJs",
- "ZRgqIAMoARIVCg1yZXBlYXRlZF9ib29sGCsgAygIEhcKD3JlcGVhdGVkX3N0",
- "cmluZxgsIAMoCRIWCg5yZXBlYXRlZF9ieXRlcxgtIAMoDBJIChdyZXBlYXRl",
- "ZF9uZXN0ZWRfbWVzc2FnZRgwIAMoCzInLmNvbmZvcm1hbmNlLlRlc3RBbGxU",
- "eXBlcy5OZXN0ZWRNZXNzYWdlEj0KGHJlcGVhdGVkX2ZvcmVpZ25fbWVzc2Fn",
- "ZRgxIAMoCzIbLmNvbmZvcm1hbmNlLkZvcmVpZ25NZXNzYWdlEkIKFHJlcGVh",
- "dGVkX25lc3RlZF9lbnVtGDMgAygOMiQuY29uZm9ybWFuY2UuVGVzdEFsbFR5",
- "cGVzLk5lc3RlZEVudW0SNwoVcmVwZWF0ZWRfZm9yZWlnbl9lbnVtGDQgAygO",
- "MhguY29uZm9ybWFuY2UuRm9yZWlnbkVudW0SIQoVcmVwZWF0ZWRfc3RyaW5n",
- "X3BpZWNlGDYgAygJQgIIAhIZCg1yZXBlYXRlZF9jb3JkGDcgAygJQgIIARJF",
- "Cg9tYXBfaW50MzJfaW50MzIYOCADKAsyLC5jb25mb3JtYW5jZS5UZXN0QWxs",
- "VHlwZXMuTWFwSW50MzJJbnQzMkVudHJ5EkUKD21hcF9pbnQ2NF9pbnQ2NBg5",
- "IAMoCzIsLmNvbmZvcm1hbmNlLlRlc3RBbGxUeXBlcy5NYXBJbnQ2NEludDY0",
- "RW50cnkSSQoRbWFwX3VpbnQzMl91aW50MzIYOiADKAsyLi5jb25mb3JtYW5j",
- "ZS5UZXN0QWxsVHlwZXMuTWFwVWludDMyVWludDMyRW50cnkSSQoRbWFwX3Vp",
- "bnQ2NF91aW50NjQYOyADKAsyLi5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMu",
- "TWFwVWludDY0VWludDY0RW50cnkSSQoRbWFwX3NpbnQzMl9zaW50MzIYPCAD",
- "KAsyLi5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFwU2ludDMyU2ludDMy",
- "RW50cnkSSQoRbWFwX3NpbnQ2NF9zaW50NjQYPSADKAsyLi5jb25mb3JtYW5j",
- "ZS5UZXN0QWxsVHlwZXMuTWFwU2ludDY0U2ludDY0RW50cnkSTQoTbWFwX2Zp",
- "eGVkMzJfZml4ZWQzMhg+IAMoCzIwLmNvbmZvcm1hbmNlLlRlc3RBbGxUeXBl",
- "cy5NYXBGaXhlZDMyRml4ZWQzMkVudHJ5Ek0KE21hcF9maXhlZDY0X2ZpeGVk",
- "NjQYPyADKAsyMC5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFwRml4ZWQ2",
- "NEZpeGVkNjRFbnRyeRJRChVtYXBfc2ZpeGVkMzJfc2ZpeGVkMzIYQCADKAsy",
- "Mi5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFwU2ZpeGVkMzJTZml4ZWQz",
- "MkVudHJ5ElEKFW1hcF9zZml4ZWQ2NF9zZml4ZWQ2NBhBIAMoCzIyLmNvbmZv",
- "cm1hbmNlLlRlc3RBbGxUeXBlcy5NYXBTZml4ZWQ2NFNmaXhlZDY0RW50cnkS",
- "RQoPbWFwX2ludDMyX2Zsb2F0GEIgAygLMiwuY29uZm9ybWFuY2UuVGVzdEFs",
- "bFR5cGVzLk1hcEludDMyRmxvYXRFbnRyeRJHChBtYXBfaW50MzJfZG91Ymxl",
- "GEMgAygLMi0uY29uZm9ybWFuY2UuVGVzdEFsbFR5cGVzLk1hcEludDMyRG91",
- "YmxlRW50cnkSQQoNbWFwX2Jvb2xfYm9vbBhEIAMoCzIqLmNvbmZvcm1hbmNl",
- "LlRlc3RBbGxUeXBlcy5NYXBCb29sQm9vbEVudHJ5EkkKEW1hcF9zdHJpbmdf",
- "c3RyaW5nGEUgAygLMi4uY29uZm9ybWFuY2UuVGVzdEFsbFR5cGVzLk1hcFN0",
- "cmluZ1N0cmluZ0VudHJ5EkcKEG1hcF9zdHJpbmdfYnl0ZXMYRiADKAsyLS5j",
- "b25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFwU3RyaW5nQnl0ZXNFbnRyeRJY",
- "ChltYXBfc3RyaW5nX25lc3RlZF9tZXNzYWdlGEcgAygLMjUuY29uZm9ybWFu",
- "Y2UuVGVzdEFsbFR5cGVzLk1hcFN0cmluZ05lc3RlZE1lc3NhZ2VFbnRyeRJa",
- "ChptYXBfc3RyaW5nX2ZvcmVpZ25fbWVzc2FnZRhIIAMoCzI2LmNvbmZvcm1h",
- "bmNlLlRlc3RBbGxUeXBlcy5NYXBTdHJpbmdGb3JlaWduTWVzc2FnZUVudHJ5",
- "ElIKFm1hcF9zdHJpbmdfbmVzdGVkX2VudW0YSSADKAsyMi5jb25mb3JtYW5j",
- "ZS5UZXN0QWxsVHlwZXMuTWFwU3RyaW5nTmVzdGVkRW51bUVudHJ5ElQKF21h",
- "cF9zdHJpbmdfZm9yZWlnbl9lbnVtGEogAygLMjMuY29uZm9ybWFuY2UuVGVz",
- "dEFsbFR5cGVzLk1hcFN0cmluZ0ZvcmVpZ25FbnVtRW50cnkSFgoMb25lb2Zf",
- "dWludDMyGG8gASgNSAASRwoUb25lb2ZfbmVzdGVkX21lc3NhZ2UYcCABKAsy",
- "Jy5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTmVzdGVkTWVzc2FnZUgAEhYK",
- "DG9uZW9mX3N0cmluZxhxIAEoCUgAEhUKC29uZW9mX2J5dGVzGHIgASgMSAAa",
- "SgoNTmVzdGVkTWVzc2FnZRIJCgFhGAEgASgFEi4KC2NvcmVjdXJzaXZlGAIg",
- "ASgLMhkuY29uZm9ybWFuY2UuVGVzdEFsbFR5cGVzGjQKEk1hcEludDMySW50",
- "MzJFbnRyeRILCgNrZXkYASABKAUSDQoFdmFsdWUYAiABKAU6AjgBGjQKEk1h",
- "cEludDY0SW50NjRFbnRyeRILCgNrZXkYASABKAMSDQoFdmFsdWUYAiABKAM6",
- "AjgBGjYKFE1hcFVpbnQzMlVpbnQzMkVudHJ5EgsKA2tleRgBIAEoDRINCgV2",
- "YWx1ZRgCIAEoDToCOAEaNgoUTWFwVWludDY0VWludDY0RW50cnkSCwoDa2V5",
- "GAEgASgEEg0KBXZhbHVlGAIgASgEOgI4ARo2ChRNYXBTaW50MzJTaW50MzJF",
- "bnRyeRILCgNrZXkYASABKBESDQoFdmFsdWUYAiABKBE6AjgBGjYKFE1hcFNp",
- "bnQ2NFNpbnQ2NEVudHJ5EgsKA2tleRgBIAEoEhINCgV2YWx1ZRgCIAEoEjoC",
- "OAEaOAoWTWFwRml4ZWQzMkZpeGVkMzJFbnRyeRILCgNrZXkYASABKAcSDQoF",
- "dmFsdWUYAiABKAc6AjgBGjgKFk1hcEZpeGVkNjRGaXhlZDY0RW50cnkSCwoD",
- "a2V5GAEgASgGEg0KBXZhbHVlGAIgASgGOgI4ARo6ChhNYXBTZml4ZWQzMlNm",
- "aXhlZDMyRW50cnkSCwoDa2V5GAEgASgPEg0KBXZhbHVlGAIgASgPOgI4ARo6",
- "ChhNYXBTZml4ZWQ2NFNmaXhlZDY0RW50cnkSCwoDa2V5GAEgASgQEg0KBXZh",
- "bHVlGAIgASgQOgI4ARo0ChJNYXBJbnQzMkZsb2F0RW50cnkSCwoDa2V5GAEg",
- "ASgFEg0KBXZhbHVlGAIgASgCOgI4ARo1ChNNYXBJbnQzMkRvdWJsZUVudHJ5",
- "EgsKA2tleRgBIAEoBRINCgV2YWx1ZRgCIAEoAToCOAEaMgoQTWFwQm9vbEJv",
- "b2xFbnRyeRILCgNrZXkYASABKAgSDQoFdmFsdWUYAiABKAg6AjgBGjYKFE1h",
- "cFN0cmluZ1N0cmluZ0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEo",
- "CToCOAEaNQoTTWFwU3RyaW5nQnl0ZXNFbnRyeRILCgNrZXkYASABKAkSDQoF",
- "dmFsdWUYAiABKAw6AjgBGmYKG01hcFN0cmluZ05lc3RlZE1lc3NhZ2VFbnRy",
- "eRILCgNrZXkYASABKAkSNgoFdmFsdWUYAiABKAsyJy5jb25mb3JtYW5jZS5U",
- "ZXN0QWxsVHlwZXMuTmVzdGVkTWVzc2FnZToCOAEaWwocTWFwU3RyaW5nRm9y",
- "ZWlnbk1lc3NhZ2VFbnRyeRILCgNrZXkYASABKAkSKgoFdmFsdWUYAiABKAsy",
- "Gy5jb25mb3JtYW5jZS5Gb3JlaWduTWVzc2FnZToCOAEaYAoYTWFwU3RyaW5n",
- "TmVzdGVkRW51bUVudHJ5EgsKA2tleRgBIAEoCRIzCgV2YWx1ZRgCIAEoDjIk",
- "LmNvbmZvcm1hbmNlLlRlc3RBbGxUeXBlcy5OZXN0ZWRFbnVtOgI4ARpVChlN",
- "YXBTdHJpbmdGb3JlaWduRW51bUVudHJ5EgsKA2tleRgBIAEoCRInCgV2YWx1",
- "ZRgCIAEoDjIYLmNvbmZvcm1hbmNlLkZvcmVpZ25FbnVtOgI4ASI5CgpOZXN0",
- "ZWRFbnVtEgcKA0ZPTxAAEgcKA0JBUhABEgcKA0JBWhACEhAKA05FRxD/////",
- "//////8BQg0KC29uZW9mX2ZpZWxkIhsKDkZvcmVpZ25NZXNzYWdlEgkKAWMY",
- "ASABKAUqNQoKV2lyZUZvcm1hdBIPCgtVTlNQRUNJRklFRBAAEgwKCFBST1RP",
- "QlVGEAESCAoESlNPThACKkAKC0ZvcmVpZ25FbnVtEg8KC0ZPUkVJR05fRk9P",
- "EAASDwoLRk9SRUlHTl9CQVIQARIPCgtGT1JFSUdOX0JBWhACQiEKH2NvbS5n",
- "b29nbGUucHJvdG9idWYuY29uZm9ybWFuY2ViBnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
- new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.ForeignEnum), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "RequestedOutputFormat" }, new[]{ "Payload" }, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped" }, new[]{ "Result" }, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Conformance.TestAllTypes), global::Conformance.TestAllTypes.Parser, new[]{ "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalNestedMessage", "OptionalForeignMessage", "OptionalNestedEnum", "OptionalForeignEnum", "OptionalStringPiece", "OptionalCord", "RecursiveMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedStringPiece", "RepeatedCord", "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapStringBytes", "MapStringNestedMessage", "MapStringForeignMessage", "MapStringNestedEnum", "MapStringForeignEnum", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes" }, new[]{ "OneofField" }, new[]{ typeof(global::Conformance.TestAllTypes.Types.NestedEnum) }, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Conformance.TestAllTypes.Types.NestedMessage), global::Conformance.TestAllTypes.Types.NestedMessage.Parser, new[]{ "A", "Corecursive" }, null, null, null),
+ "ChFjb25mb3JtYW5jZS5wcm90bxILY29uZm9ybWFuY2UaGWdvb2dsZS9wcm90",
+ "b2J1Zi9hbnkucHJvdG8aHmdvb2dsZS9wcm90b2J1Zi9kdXJhdGlvbi5wcm90",
+ "bxogZ29vZ2xlL3Byb3RvYnVmL2ZpZWxkX21hc2sucHJvdG8aHGdvb2dsZS9w",
+ "cm90b2J1Zi9zdHJ1Y3QucHJvdG8aH2dvb2dsZS9wcm90b2J1Zi90aW1lc3Rh",
+ "bXAucHJvdG8aHmdvb2dsZS9wcm90b2J1Zi93cmFwcGVycy5wcm90byKNAQoS",
+ "Q29uZm9ybWFuY2VSZXF1ZXN0EhoKEHByb3RvYnVmX3BheWxvYWQYASABKAxI",
+ "ABIWCgxqc29uX3BheWxvYWQYAiABKAlIABI4ChdyZXF1ZXN0ZWRfb3V0cHV0",
+ "X2Zvcm1hdBgDIAEoDjIXLmNvbmZvcm1hbmNlLldpcmVGb3JtYXRCCQoHcGF5",
+ "bG9hZCKxAQoTQ29uZm9ybWFuY2VSZXNwb25zZRIVCgtwYXJzZV9lcnJvchgB",
+ "IAEoCUgAEhkKD3NlcmlhbGl6ZV9lcnJvchgGIAEoCUgAEhcKDXJ1bnRpbWVf",
+ "ZXJyb3IYAiABKAlIABIaChBwcm90b2J1Zl9wYXlsb2FkGAMgASgMSAASFgoM",
+ "anNvbl9wYXlsb2FkGAQgASgJSAASEQoHc2tpcHBlZBgFIAEoCUgAQggKBnJl",
+ "c3VsdCLVMgoMVGVzdEFsbFR5cGVzEhYKDm9wdGlvbmFsX2ludDMyGAEgASgF",
+ "EhYKDm9wdGlvbmFsX2ludDY0GAIgASgDEhcKD29wdGlvbmFsX3VpbnQzMhgD",
+ "IAEoDRIXCg9vcHRpb25hbF91aW50NjQYBCABKAQSFwoPb3B0aW9uYWxfc2lu",
+ "dDMyGAUgASgREhcKD29wdGlvbmFsX3NpbnQ2NBgGIAEoEhIYChBvcHRpb25h",
+ "bF9maXhlZDMyGAcgASgHEhgKEG9wdGlvbmFsX2ZpeGVkNjQYCCABKAYSGQoR",
+ "b3B0aW9uYWxfc2ZpeGVkMzIYCSABKA8SGQoRb3B0aW9uYWxfc2ZpeGVkNjQY",
+ "CiABKBASFgoOb3B0aW9uYWxfZmxvYXQYCyABKAISFwoPb3B0aW9uYWxfZG91",
+ "YmxlGAwgASgBEhUKDW9wdGlvbmFsX2Jvb2wYDSABKAgSFwoPb3B0aW9uYWxf",
+ "c3RyaW5nGA4gASgJEhYKDm9wdGlvbmFsX2J5dGVzGA8gASgMEkgKF29wdGlv",
+ "bmFsX25lc3RlZF9tZXNzYWdlGBIgASgLMicuY29uZm9ybWFuY2UuVGVzdEFs",
+ "bFR5cGVzLk5lc3RlZE1lc3NhZ2USPQoYb3B0aW9uYWxfZm9yZWlnbl9tZXNz",
+ "YWdlGBMgASgLMhsuY29uZm9ybWFuY2UuRm9yZWlnbk1lc3NhZ2USQgoUb3B0",
+ "aW9uYWxfbmVzdGVkX2VudW0YFSABKA4yJC5jb25mb3JtYW5jZS5UZXN0QWxs",
+ "VHlwZXMuTmVzdGVkRW51bRI3ChVvcHRpb25hbF9mb3JlaWduX2VudW0YFiAB",
+ "KA4yGC5jb25mb3JtYW5jZS5Gb3JlaWduRW51bRIhChVvcHRpb25hbF9zdHJp",
+ "bmdfcGllY2UYGCABKAlCAggCEhkKDW9wdGlvbmFsX2NvcmQYGSABKAlCAggB",
+ "EjQKEXJlY3Vyc2l2ZV9tZXNzYWdlGBsgASgLMhkuY29uZm9ybWFuY2UuVGVz",
+ "dEFsbFR5cGVzEhYKDnJlcGVhdGVkX2ludDMyGB8gAygFEhYKDnJlcGVhdGVk",
+ "X2ludDY0GCAgAygDEhcKD3JlcGVhdGVkX3VpbnQzMhghIAMoDRIXCg9yZXBl",
+ "YXRlZF91aW50NjQYIiADKAQSFwoPcmVwZWF0ZWRfc2ludDMyGCMgAygREhcK",
+ "D3JlcGVhdGVkX3NpbnQ2NBgkIAMoEhIYChByZXBlYXRlZF9maXhlZDMyGCUg",
+ "AygHEhgKEHJlcGVhdGVkX2ZpeGVkNjQYJiADKAYSGQoRcmVwZWF0ZWRfc2Zp",
+ "eGVkMzIYJyADKA8SGQoRcmVwZWF0ZWRfc2ZpeGVkNjQYKCADKBASFgoOcmVw",
+ "ZWF0ZWRfZmxvYXQYKSADKAISFwoPcmVwZWF0ZWRfZG91YmxlGCogAygBEhUK",
+ "DXJlcGVhdGVkX2Jvb2wYKyADKAgSFwoPcmVwZWF0ZWRfc3RyaW5nGCwgAygJ",
+ "EhYKDnJlcGVhdGVkX2J5dGVzGC0gAygMEkgKF3JlcGVhdGVkX25lc3RlZF9t",
+ "ZXNzYWdlGDAgAygLMicuY29uZm9ybWFuY2UuVGVzdEFsbFR5cGVzLk5lc3Rl",
+ "ZE1lc3NhZ2USPQoYcmVwZWF0ZWRfZm9yZWlnbl9tZXNzYWdlGDEgAygLMhsu",
+ "Y29uZm9ybWFuY2UuRm9yZWlnbk1lc3NhZ2USQgoUcmVwZWF0ZWRfbmVzdGVk",
+ "X2VudW0YMyADKA4yJC5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTmVzdGVk",
+ "RW51bRI3ChVyZXBlYXRlZF9mb3JlaWduX2VudW0YNCADKA4yGC5jb25mb3Jt",
+ "YW5jZS5Gb3JlaWduRW51bRIhChVyZXBlYXRlZF9zdHJpbmdfcGllY2UYNiAD",
+ "KAlCAggCEhkKDXJlcGVhdGVkX2NvcmQYNyADKAlCAggBEkUKD21hcF9pbnQz",
+ "Ml9pbnQzMhg4IAMoCzIsLmNvbmZvcm1hbmNlLlRlc3RBbGxUeXBlcy5NYXBJ",
+ "bnQzMkludDMyRW50cnkSRQoPbWFwX2ludDY0X2ludDY0GDkgAygLMiwuY29u",
+ "Zm9ybWFuY2UuVGVzdEFsbFR5cGVzLk1hcEludDY0SW50NjRFbnRyeRJJChFt",
+ "YXBfdWludDMyX3VpbnQzMhg6IAMoCzIuLmNvbmZvcm1hbmNlLlRlc3RBbGxU",
+ "eXBlcy5NYXBVaW50MzJVaW50MzJFbnRyeRJJChFtYXBfdWludDY0X3VpbnQ2",
+ "NBg7IAMoCzIuLmNvbmZvcm1hbmNlLlRlc3RBbGxUeXBlcy5NYXBVaW50NjRV",
+ "aW50NjRFbnRyeRJJChFtYXBfc2ludDMyX3NpbnQzMhg8IAMoCzIuLmNvbmZv",
+ "cm1hbmNlLlRlc3RBbGxUeXBlcy5NYXBTaW50MzJTaW50MzJFbnRyeRJJChFt",
+ "YXBfc2ludDY0X3NpbnQ2NBg9IAMoCzIuLmNvbmZvcm1hbmNlLlRlc3RBbGxU",
+ "eXBlcy5NYXBTaW50NjRTaW50NjRFbnRyeRJNChNtYXBfZml4ZWQzMl9maXhl",
+ "ZDMyGD4gAygLMjAuY29uZm9ybWFuY2UuVGVzdEFsbFR5cGVzLk1hcEZpeGVk",
+ "MzJGaXhlZDMyRW50cnkSTQoTbWFwX2ZpeGVkNjRfZml4ZWQ2NBg/IAMoCzIw",
+ "LmNvbmZvcm1hbmNlLlRlc3RBbGxUeXBlcy5NYXBGaXhlZDY0Rml4ZWQ2NEVu",
+ "dHJ5ElEKFW1hcF9zZml4ZWQzMl9zZml4ZWQzMhhAIAMoCzIyLmNvbmZvcm1h",
+ "bmNlLlRlc3RBbGxUeXBlcy5NYXBTZml4ZWQzMlNmaXhlZDMyRW50cnkSUQoV",
+ "bWFwX3NmaXhlZDY0X3NmaXhlZDY0GEEgAygLMjIuY29uZm9ybWFuY2UuVGVz",
+ "dEFsbFR5cGVzLk1hcFNmaXhlZDY0U2ZpeGVkNjRFbnRyeRJFCg9tYXBfaW50",
+ "MzJfZmxvYXQYQiADKAsyLC5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFw",
+ "SW50MzJGbG9hdEVudHJ5EkcKEG1hcF9pbnQzMl9kb3VibGUYQyADKAsyLS5j",
+ "b25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFwSW50MzJEb3VibGVFbnRyeRJB",
+ "Cg1tYXBfYm9vbF9ib29sGEQgAygLMiouY29uZm9ybWFuY2UuVGVzdEFsbFR5",
+ "cGVzLk1hcEJvb2xCb29sRW50cnkSSQoRbWFwX3N0cmluZ19zdHJpbmcYRSAD",
+ "KAsyLi5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMuTWFwU3RyaW5nU3RyaW5n",
+ "RW50cnkSRwoQbWFwX3N0cmluZ19ieXRlcxhGIAMoCzItLmNvbmZvcm1hbmNl",
+ "LlRlc3RBbGxUeXBlcy5NYXBTdHJpbmdCeXRlc0VudHJ5ElgKGW1hcF9zdHJp",
+ "bmdfbmVzdGVkX21lc3NhZ2UYRyADKAsyNS5jb25mb3JtYW5jZS5UZXN0QWxs",
+ "VHlwZXMuTWFwU3RyaW5nTmVzdGVkTWVzc2FnZUVudHJ5EloKGm1hcF9zdHJp",
+ "bmdfZm9yZWlnbl9tZXNzYWdlGEggAygLMjYuY29uZm9ybWFuY2UuVGVzdEFs",
+ "bFR5cGVzLk1hcFN0cmluZ0ZvcmVpZ25NZXNzYWdlRW50cnkSUgoWbWFwX3N0",
+ "cmluZ19uZXN0ZWRfZW51bRhJIAMoCzIyLmNvbmZvcm1hbmNlLlRlc3RBbGxU",
+ "eXBlcy5NYXBTdHJpbmdOZXN0ZWRFbnVtRW50cnkSVAoXbWFwX3N0cmluZ19m",
+ "b3JlaWduX2VudW0YSiADKAsyMy5jb25mb3JtYW5jZS5UZXN0QWxsVHlwZXMu",
+ "TWFwU3RyaW5nRm9yZWlnbkVudW1FbnRyeRIWCgxvbmVvZl91aW50MzIYbyAB",
+ "KA1IABJHChRvbmVvZl9uZXN0ZWRfbWVzc2FnZRhwIAEoCzInLmNvbmZvcm1h",
+ "bmNlLlRlc3RBbGxUeXBlcy5OZXN0ZWRNZXNzYWdlSAASFgoMb25lb2Zfc3Ry",
+ "aW5nGHEgASgJSAASFQoLb25lb2ZfYnl0ZXMYciABKAxIABI6ChVvcHRpb25h",
+ "bF9ib29sX3dyYXBwZXIYyQEgASgLMhouZ29vZ2xlLnByb3RvYnVmLkJvb2xW",
+ "YWx1ZRI8ChZvcHRpb25hbF9pbnQzMl93cmFwcGVyGMoBIAEoCzIbLmdvb2ds",
+ "ZS5wcm90b2J1Zi5JbnQzMlZhbHVlEjwKFm9wdGlvbmFsX2ludDY0X3dyYXBw",
+ "ZXIYywEgASgLMhsuZ29vZ2xlLnByb3RvYnVmLkludDY0VmFsdWUSPgoXb3B0",
+ "aW9uYWxfdWludDMyX3dyYXBwZXIYzAEgASgLMhwuZ29vZ2xlLnByb3RvYnVm",
+ "LlVJbnQzMlZhbHVlEj4KF29wdGlvbmFsX3VpbnQ2NF93cmFwcGVyGM0BIAEo",
+ "CzIcLmdvb2dsZS5wcm90b2J1Zi5VSW50NjRWYWx1ZRI8ChZvcHRpb25hbF9m",
+ "bG9hdF93cmFwcGVyGM4BIAEoCzIbLmdvb2dsZS5wcm90b2J1Zi5GbG9hdFZh",
+ "bHVlEj4KF29wdGlvbmFsX2RvdWJsZV93cmFwcGVyGM8BIAEoCzIcLmdvb2ds",
+ "ZS5wcm90b2J1Zi5Eb3VibGVWYWx1ZRI+ChdvcHRpb25hbF9zdHJpbmdfd3Jh",
+ "cHBlchjQASABKAsyHC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSPAoW",
+ "b3B0aW9uYWxfYnl0ZXNfd3JhcHBlchjRASABKAsyGy5nb29nbGUucHJvdG9i",
+ "dWYuQnl0ZXNWYWx1ZRI6ChVyZXBlYXRlZF9ib29sX3dyYXBwZXIY0wEgAygL",
+ "MhouZ29vZ2xlLnByb3RvYnVmLkJvb2xWYWx1ZRI8ChZyZXBlYXRlZF9pbnQz",
+ "Ml93cmFwcGVyGNQBIAMoCzIbLmdvb2dsZS5wcm90b2J1Zi5JbnQzMlZhbHVl",
+ "EjwKFnJlcGVhdGVkX2ludDY0X3dyYXBwZXIY1QEgAygLMhsuZ29vZ2xlLnBy",
+ "b3RvYnVmLkludDY0VmFsdWUSPgoXcmVwZWF0ZWRfdWludDMyX3dyYXBwZXIY",
+ "1gEgAygLMhwuZ29vZ2xlLnByb3RvYnVmLlVJbnQzMlZhbHVlEj4KF3JlcGVh",
+ "dGVkX3VpbnQ2NF93cmFwcGVyGNcBIAMoCzIcLmdvb2dsZS5wcm90b2J1Zi5V",
+ "SW50NjRWYWx1ZRI8ChZyZXBlYXRlZF9mbG9hdF93cmFwcGVyGNgBIAMoCzIb",
+ "Lmdvb2dsZS5wcm90b2J1Zi5GbG9hdFZhbHVlEj4KF3JlcGVhdGVkX2RvdWJs",
+ "ZV93cmFwcGVyGNkBIAMoCzIcLmdvb2dsZS5wcm90b2J1Zi5Eb3VibGVWYWx1",
+ "ZRI+ChdyZXBlYXRlZF9zdHJpbmdfd3JhcHBlchjaASADKAsyHC5nb29nbGUu",
+ "cHJvdG9idWYuU3RyaW5nVmFsdWUSPAoWcmVwZWF0ZWRfYnl0ZXNfd3JhcHBl",
+ "chjbASADKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZRI1ChFvcHRp",
+ "b25hbF9kdXJhdGlvbhitAiABKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRp",
+ "b24SNwoSb3B0aW9uYWxfdGltZXN0YW1wGK4CIAEoCzIaLmdvb2dsZS5wcm90",
+ "b2J1Zi5UaW1lc3RhbXASOAoTb3B0aW9uYWxfZmllbGRfbWFzaxivAiABKAsy",
+ "Gi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrEjEKD29wdGlvbmFsX3N0cnVj",
+ "dBiwAiABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0EisKDG9wdGlvbmFs",
+ "X2FueRixAiABKAsyFC5nb29nbGUucHJvdG9idWYuQW55Ei8KDm9wdGlvbmFs",
+ "X3ZhbHVlGLICIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5WYWx1ZRI1ChFyZXBl",
+ "YXRlZF9kdXJhdGlvbhi3AiADKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRp",
+ "b24SNwoScmVwZWF0ZWRfdGltZXN0YW1wGLgCIAMoCzIaLmdvb2dsZS5wcm90",
+ "b2J1Zi5UaW1lc3RhbXASNwoScmVwZWF0ZWRfZmllbGRtYXNrGLkCIAMoCzIa",
+ "Lmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2sSMQoPcmVwZWF0ZWRfc3RydWN0",
+ "GMQCIAMoCzIXLmdvb2dsZS5wcm90b2J1Zi5TdHJ1Y3QSKwoMcmVwZWF0ZWRf",
+ "YW55GLsCIAMoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnkSLwoOcmVwZWF0ZWRf",
+ "dmFsdWUYvAIgAygLMhYuZ29vZ2xlLnByb3RvYnVmLlZhbHVlEhMKCmZpZWxk",
+ "bmFtZTEYkQMgASgFEhQKC2ZpZWxkX25hbWUyGJIDIAEoBRIVCgxfZmllbGRf",
+ "bmFtZTMYkwMgASgFEhYKDWZpZWxkX19uYW1lNF8YlAMgASgFEhQKC2ZpZWxk",
+ "MG5hbWU1GJUDIAEoBRIWCg1maWVsZF8wX25hbWU2GJYDIAEoBRITCgpmaWVs",
+ "ZE5hbWU3GJcDIAEoBRITCgpGaWVsZE5hbWU4GJgDIAEoBRIUCgtmaWVsZF9O",
+ "YW1lORiZAyABKAUSFQoMRmllbGRfTmFtZTEwGJoDIAEoBRIVCgxGSUVMRF9O",
+ "QU1FMTEYmwMgASgFEhUKDEZJRUxEX25hbWUxMhicAyABKAUaSgoNTmVzdGVk",
+ "TWVzc2FnZRIJCgFhGAEgASgFEi4KC2NvcmVjdXJzaXZlGAIgASgLMhkuY29u",
+ "Zm9ybWFuY2UuVGVzdEFsbFR5cGVzGjQKEk1hcEludDMySW50MzJFbnRyeRIL",
+ "CgNrZXkYASABKAUSDQoFdmFsdWUYAiABKAU6AjgBGjQKEk1hcEludDY0SW50",
+ "NjRFbnRyeRILCgNrZXkYASABKAMSDQoFdmFsdWUYAiABKAM6AjgBGjYKFE1h",
+ "cFVpbnQzMlVpbnQzMkVudHJ5EgsKA2tleRgBIAEoDRINCgV2YWx1ZRgCIAEo",
+ "DToCOAEaNgoUTWFwVWludDY0VWludDY0RW50cnkSCwoDa2V5GAEgASgEEg0K",
+ "BXZhbHVlGAIgASgEOgI4ARo2ChRNYXBTaW50MzJTaW50MzJFbnRyeRILCgNr",
+ "ZXkYASABKBESDQoFdmFsdWUYAiABKBE6AjgBGjYKFE1hcFNpbnQ2NFNpbnQ2",
+ "NEVudHJ5EgsKA2tleRgBIAEoEhINCgV2YWx1ZRgCIAEoEjoCOAEaOAoWTWFw",
+ "Rml4ZWQzMkZpeGVkMzJFbnRyeRILCgNrZXkYASABKAcSDQoFdmFsdWUYAiAB",
+ "KAc6AjgBGjgKFk1hcEZpeGVkNjRGaXhlZDY0RW50cnkSCwoDa2V5GAEgASgG",
+ "Eg0KBXZhbHVlGAIgASgGOgI4ARo6ChhNYXBTZml4ZWQzMlNmaXhlZDMyRW50",
+ "cnkSCwoDa2V5GAEgASgPEg0KBXZhbHVlGAIgASgPOgI4ARo6ChhNYXBTZml4",
+ "ZWQ2NFNmaXhlZDY0RW50cnkSCwoDa2V5GAEgASgQEg0KBXZhbHVlGAIgASgQ",
+ "OgI4ARo0ChJNYXBJbnQzMkZsb2F0RW50cnkSCwoDa2V5GAEgASgFEg0KBXZh",
+ "bHVlGAIgASgCOgI4ARo1ChNNYXBJbnQzMkRvdWJsZUVudHJ5EgsKA2tleRgB",
+ "IAEoBRINCgV2YWx1ZRgCIAEoAToCOAEaMgoQTWFwQm9vbEJvb2xFbnRyeRIL",
+ "CgNrZXkYASABKAgSDQoFdmFsdWUYAiABKAg6AjgBGjYKFE1hcFN0cmluZ1N0",
+ "cmluZ0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaNQoT",
+ "TWFwU3RyaW5nQnl0ZXNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiAB",
+ "KAw6AjgBGmYKG01hcFN0cmluZ05lc3RlZE1lc3NhZ2VFbnRyeRILCgNrZXkY",
+ "ASABKAkSNgoFdmFsdWUYAiABKAsyJy5jb25mb3JtYW5jZS5UZXN0QWxsVHlw",
+ "ZXMuTmVzdGVkTWVzc2FnZToCOAEaWwocTWFwU3RyaW5nRm9yZWlnbk1lc3Nh",
+ "Z2VFbnRyeRILCgNrZXkYASABKAkSKgoFdmFsdWUYAiABKAsyGy5jb25mb3Jt",
+ "YW5jZS5Gb3JlaWduTWVzc2FnZToCOAEaYAoYTWFwU3RyaW5nTmVzdGVkRW51",
+ "bUVudHJ5EgsKA2tleRgBIAEoCRIzCgV2YWx1ZRgCIAEoDjIkLmNvbmZvcm1h",
+ "bmNlLlRlc3RBbGxUeXBlcy5OZXN0ZWRFbnVtOgI4ARpVChlNYXBTdHJpbmdG",
+ "b3JlaWduRW51bUVudHJ5EgsKA2tleRgBIAEoCRInCgV2YWx1ZRgCIAEoDjIY",
+ "LmNvbmZvcm1hbmNlLkZvcmVpZ25FbnVtOgI4ASI5CgpOZXN0ZWRFbnVtEgcK",
+ "A0ZPTxAAEgcKA0JBUhABEgcKA0JBWhACEhAKA05FRxD///////////8BQg0K",
+ "C29uZW9mX2ZpZWxkIhsKDkZvcmVpZ25NZXNzYWdlEgkKAWMYASABKAUqNQoK",
+ "V2lyZUZvcm1hdBIPCgtVTlNQRUNJRklFRBAAEgwKCFBST1RPQlVGEAESCAoE",
+ "SlNPThACKkAKC0ZvcmVpZ25FbnVtEg8KC0ZPUkVJR05fRk9PEAASDwoLRk9S",
+ "RUlHTl9CQVIQARIPCgtGT1JFSUdOX0JBWhACQiEKH2NvbS5nb29nbGUucHJv",
+ "dG9idWYuY29uZm9ybWFuY2ViBnByb3RvMw=="));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+ new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.ForeignEnum), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "RequestedOutputFormat" }, new[]{ "Payload" }, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped" }, new[]{ "Result" }, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.TestAllTypes), global::Conformance.TestAllTypes.Parser, new[]{ "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalNestedMessage", "OptionalForeignMessage", "OptionalNestedEnum", "OptionalForeignEnum", "OptionalStringPiece", "OptionalCord", "RecursiveMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedStringPiece", "RepeatedCord", "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapStringBytes", "MapStringNestedMessage", "MapStringForeignMessage", "MapStringNestedEnum", "MapStringForeignEnum", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes", "OptionalBoolWrapper", "OptionalInt32Wrapper", "OptionalInt64Wrapper", "OptionalUint32Wrapper", "OptionalUint64Wrapper", "OptionalFloatWrapper", "OptionalDoubleWrapper", "OptionalStringWrapper", "OptionalBytesWrapper", "RepeatedBoolWrapper", "RepeatedInt32Wrapper", "RepeatedInt64Wrapper", "RepeatedUint32Wrapper", "RepeatedUint64Wrapper", "RepeatedFloatWrapper", "RepeatedDoubleWrapper", "RepeatedStringWrapper", "RepeatedBytesWrapper", "OptionalDuration", "OptionalTimestamp", "OptionalFieldMask", "OptionalStruct", "OptionalAny", "OptionalValue", "RepeatedDuration", "RepeatedTimestamp", "RepeatedFieldmask", "RepeatedStruct", "RepeatedAny", "RepeatedValue", "Fieldname1", "FieldName2", "FieldName3", "FieldName4", "Field0Name5", "Field0Name6", "FieldName7", "FieldName8", "FieldName9", "FieldName10", "FIELDNAME11", "FIELDName12" }, new[]{ "OneofField" }, new[]{ typeof(global::Conformance.TestAllTypes.Types.NestedEnum) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.TestAllTypes.Types.NestedMessage), global::Conformance.TestAllTypes.Types.NestedMessage.Parser, new[]{ "A", "Corecursive" }, null, null, null),
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Conformance.ForeignMessage), global::Conformance.ForeignMessage.Parser, new[]{ "C" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ForeignMessage), global::Conformance.ForeignMessage.Parser, new[]{ "C" }, null, null, null)
}));
}
#endregion
@@ -211,7 +261,7 @@ namespace Conformance {
public pb::ByteString ProtobufPayload {
get { return payloadCase_ == PayloadOneofCase.ProtobufPayload ? (pb::ByteString) payload_ : pb::ByteString.Empty; }
set {
- payload_ = pb::Preconditions.CheckNotNull(value, "value");
+ payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
payloadCase_ = PayloadOneofCase.ProtobufPayload;
}
}
@@ -221,7 +271,7 @@ namespace Conformance {
public string JsonPayload {
get { return payloadCase_ == PayloadOneofCase.JsonPayload ? (string) payload_ : ""; }
set {
- payload_ = pb::Preconditions.CheckNotNull(value, "value");
+ payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
payloadCase_ = PayloadOneofCase.JsonPayload;
}
}
@@ -284,7 +334,7 @@ namespace Conformance {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -386,6 +436,9 @@ namespace Conformance {
case ResultOneofCase.ParseError:
ParseError = other.ParseError;
break;
+ case ResultOneofCase.SerializeError:
+ SerializeError = other.SerializeError;
+ break;
case ResultOneofCase.RuntimeError:
RuntimeError = other.RuntimeError;
break;
@@ -418,11 +471,26 @@ namespace Conformance {
public string ParseError {
get { return resultCase_ == ResultOneofCase.ParseError ? (string) result_ : ""; }
set {
- result_ = pb::Preconditions.CheckNotNull(value, "value");
+ result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
resultCase_ = ResultOneofCase.ParseError;
}
}
+ /// <summary>Field number for the "serialize_error" field.</summary>
+ public const int SerializeErrorFieldNumber = 6;
+ /// <summary>
+ /// If the input was successfully parsed but errors occurred when
+ /// serializing it to the requested output format, set the error message in
+ /// this field.
+ /// </summary>
+ public string SerializeError {
+ get { return resultCase_ == ResultOneofCase.SerializeError ? (string) result_ : ""; }
+ set {
+ result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ resultCase_ = ResultOneofCase.SerializeError;
+ }
+ }
+
/// <summary>Field number for the "runtime_error" field.</summary>
public const int RuntimeErrorFieldNumber = 2;
/// <summary>
@@ -433,7 +501,7 @@ namespace Conformance {
public string RuntimeError {
get { return resultCase_ == ResultOneofCase.RuntimeError ? (string) result_ : ""; }
set {
- result_ = pb::Preconditions.CheckNotNull(value, "value");
+ result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
resultCase_ = ResultOneofCase.RuntimeError;
}
}
@@ -447,7 +515,7 @@ namespace Conformance {
public pb::ByteString ProtobufPayload {
get { return resultCase_ == ResultOneofCase.ProtobufPayload ? (pb::ByteString) result_ : pb::ByteString.Empty; }
set {
- result_ = pb::Preconditions.CheckNotNull(value, "value");
+ result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
resultCase_ = ResultOneofCase.ProtobufPayload;
}
}
@@ -461,7 +529,7 @@ namespace Conformance {
public string JsonPayload {
get { return resultCase_ == ResultOneofCase.JsonPayload ? (string) result_ : ""; }
set {
- result_ = pb::Preconditions.CheckNotNull(value, "value");
+ result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
resultCase_ = ResultOneofCase.JsonPayload;
}
}
@@ -475,7 +543,7 @@ namespace Conformance {
public string Skipped {
get { return resultCase_ == ResultOneofCase.Skipped ? (string) result_ : ""; }
set {
- result_ = pb::Preconditions.CheckNotNull(value, "value");
+ result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
resultCase_ = ResultOneofCase.Skipped;
}
}
@@ -485,6 +553,7 @@ namespace Conformance {
public enum ResultOneofCase {
None = 0,
ParseError = 1,
+ SerializeError = 6,
RuntimeError = 2,
ProtobufPayload = 3,
JsonPayload = 4,
@@ -512,6 +581,7 @@ namespace Conformance {
return true;
}
if (ParseError != other.ParseError) return false;
+ if (SerializeError != other.SerializeError) return false;
if (RuntimeError != other.RuntimeError) return false;
if (ProtobufPayload != other.ProtobufPayload) return false;
if (JsonPayload != other.JsonPayload) return false;
@@ -523,6 +593,7 @@ namespace Conformance {
public override int GetHashCode() {
int hash = 1;
if (resultCase_ == ResultOneofCase.ParseError) hash ^= ParseError.GetHashCode();
+ if (resultCase_ == ResultOneofCase.SerializeError) hash ^= SerializeError.GetHashCode();
if (resultCase_ == ResultOneofCase.RuntimeError) hash ^= RuntimeError.GetHashCode();
if (resultCase_ == ResultOneofCase.ProtobufPayload) hash ^= ProtobufPayload.GetHashCode();
if (resultCase_ == ResultOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode();
@@ -532,7 +603,7 @@ namespace Conformance {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -556,6 +627,10 @@ namespace Conformance {
output.WriteRawTag(42);
output.WriteString(Skipped);
}
+ if (resultCase_ == ResultOneofCase.SerializeError) {
+ output.WriteRawTag(50);
+ output.WriteString(SerializeError);
+ }
}
public int CalculateSize() {
@@ -563,6 +638,9 @@ namespace Conformance {
if (resultCase_ == ResultOneofCase.ParseError) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(ParseError);
}
+ if (resultCase_ == ResultOneofCase.SerializeError) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(SerializeError);
+ }
if (resultCase_ == ResultOneofCase.RuntimeError) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(RuntimeError);
}
@@ -586,6 +664,9 @@ namespace Conformance {
case ResultOneofCase.ParseError:
ParseError = other.ParseError;
break;
+ case ResultOneofCase.SerializeError:
+ SerializeError = other.SerializeError;
+ break;
case ResultOneofCase.RuntimeError:
RuntimeError = other.RuntimeError;
break;
@@ -629,6 +710,10 @@ namespace Conformance {
Skipped = input.ReadString();
break;
}
+ case 50: {
+ SerializeError = input.ReadString();
+ break;
+ }
}
}
}
@@ -721,6 +806,48 @@ namespace Conformance {
mapStringForeignMessage_ = other.mapStringForeignMessage_.Clone();
mapStringNestedEnum_ = other.mapStringNestedEnum_.Clone();
mapStringForeignEnum_ = other.mapStringForeignEnum_.Clone();
+ OptionalBoolWrapper = other.OptionalBoolWrapper;
+ OptionalInt32Wrapper = other.OptionalInt32Wrapper;
+ OptionalInt64Wrapper = other.OptionalInt64Wrapper;
+ OptionalUint32Wrapper = other.OptionalUint32Wrapper;
+ OptionalUint64Wrapper = other.OptionalUint64Wrapper;
+ OptionalFloatWrapper = other.OptionalFloatWrapper;
+ OptionalDoubleWrapper = other.OptionalDoubleWrapper;
+ OptionalStringWrapper = other.OptionalStringWrapper;
+ OptionalBytesWrapper = other.OptionalBytesWrapper;
+ repeatedBoolWrapper_ = other.repeatedBoolWrapper_.Clone();
+ repeatedInt32Wrapper_ = other.repeatedInt32Wrapper_.Clone();
+ repeatedInt64Wrapper_ = other.repeatedInt64Wrapper_.Clone();
+ repeatedUint32Wrapper_ = other.repeatedUint32Wrapper_.Clone();
+ repeatedUint64Wrapper_ = other.repeatedUint64Wrapper_.Clone();
+ repeatedFloatWrapper_ = other.repeatedFloatWrapper_.Clone();
+ repeatedDoubleWrapper_ = other.repeatedDoubleWrapper_.Clone();
+ repeatedStringWrapper_ = other.repeatedStringWrapper_.Clone();
+ repeatedBytesWrapper_ = other.repeatedBytesWrapper_.Clone();
+ OptionalDuration = other.optionalDuration_ != null ? other.OptionalDuration.Clone() : null;
+ OptionalTimestamp = other.optionalTimestamp_ != null ? other.OptionalTimestamp.Clone() : null;
+ OptionalFieldMask = other.optionalFieldMask_ != null ? other.OptionalFieldMask.Clone() : null;
+ OptionalStruct = other.optionalStruct_ != null ? other.OptionalStruct.Clone() : null;
+ OptionalAny = other.optionalAny_ != null ? other.OptionalAny.Clone() : null;
+ OptionalValue = other.optionalValue_ != null ? other.OptionalValue.Clone() : null;
+ repeatedDuration_ = other.repeatedDuration_.Clone();
+ repeatedTimestamp_ = other.repeatedTimestamp_.Clone();
+ repeatedFieldmask_ = other.repeatedFieldmask_.Clone();
+ repeatedStruct_ = other.repeatedStruct_.Clone();
+ repeatedAny_ = other.repeatedAny_.Clone();
+ repeatedValue_ = other.repeatedValue_.Clone();
+ fieldname1_ = other.fieldname1_;
+ fieldName2_ = other.fieldName2_;
+ FieldName3_ = other.FieldName3_;
+ fieldName4_ = other.fieldName4_;
+ field0Name5_ = other.field0Name5_;
+ field0Name6_ = other.field0Name6_;
+ fieldName7_ = other.fieldName7_;
+ fieldName8_ = other.fieldName8_;
+ fieldName9_ = other.fieldName9_;
+ fieldName10_ = other.fieldName10_;
+ fIELDNAME11_ = other.fIELDNAME11_;
+ fIELDName12_ = other.fIELDName12_;
switch (other.OneofFieldCase) {
case OneofFieldOneofCase.OneofUint32:
OneofUint32 = other.OneofUint32;
@@ -881,7 +1008,7 @@ namespace Conformance {
public string OptionalString {
get { return optionalString_; }
set {
- optionalString_ = pb::Preconditions.CheckNotNull(value, "value");
+ optionalString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -891,7 +1018,7 @@ namespace Conformance {
public pb::ByteString OptionalBytes {
get { return optionalBytes_; }
set {
- optionalBytes_ = pb::Preconditions.CheckNotNull(value, "value");
+ optionalBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -941,7 +1068,7 @@ namespace Conformance {
public string OptionalStringPiece {
get { return optionalStringPiece_; }
set {
- optionalStringPiece_ = pb::Preconditions.CheckNotNull(value, "value");
+ optionalStringPiece_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -951,7 +1078,7 @@ namespace Conformance {
public string OptionalCord {
get { return optionalCord_; }
set {
- optionalCord_ = pb::Preconditions.CheckNotNull(value, "value");
+ optionalCord_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1356,7 +1483,7 @@ namespace Conformance {
public string OneofString {
get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) oneofField_ : ""; }
set {
- oneofField_ = pb::Preconditions.CheckNotNull(value, "value");
+ oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
oneofFieldCase_ = OneofFieldOneofCase.OneofString;
}
}
@@ -1366,11 +1493,431 @@ namespace Conformance {
public pb::ByteString OneofBytes {
get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
set {
- oneofField_ = pb::Preconditions.CheckNotNull(value, "value");
+ oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
oneofFieldCase_ = OneofFieldOneofCase.OneofBytes;
}
}
+ /// <summary>Field number for the "optional_bool_wrapper" field.</summary>
+ public const int OptionalBoolWrapperFieldNumber = 201;
+ private static readonly pb::FieldCodec<bool?> _single_optionalBoolWrapper_codec = pb::FieldCodec.ForStructWrapper<bool>(1610);
+ private bool? optionalBoolWrapper_;
+ /// <summary>
+ /// Well-known types
+ /// </summary>
+ public bool? OptionalBoolWrapper {
+ get { return optionalBoolWrapper_; }
+ set {
+ optionalBoolWrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_int32_wrapper" field.</summary>
+ public const int OptionalInt32WrapperFieldNumber = 202;
+ private static readonly pb::FieldCodec<int?> _single_optionalInt32Wrapper_codec = pb::FieldCodec.ForStructWrapper<int>(1618);
+ private int? optionalInt32Wrapper_;
+ public int? OptionalInt32Wrapper {
+ get { return optionalInt32Wrapper_; }
+ set {
+ optionalInt32Wrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_int64_wrapper" field.</summary>
+ public const int OptionalInt64WrapperFieldNumber = 203;
+ private static readonly pb::FieldCodec<long?> _single_optionalInt64Wrapper_codec = pb::FieldCodec.ForStructWrapper<long>(1626);
+ private long? optionalInt64Wrapper_;
+ public long? OptionalInt64Wrapper {
+ get { return optionalInt64Wrapper_; }
+ set {
+ optionalInt64Wrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_uint32_wrapper" field.</summary>
+ public const int OptionalUint32WrapperFieldNumber = 204;
+ private static readonly pb::FieldCodec<uint?> _single_optionalUint32Wrapper_codec = pb::FieldCodec.ForStructWrapper<uint>(1634);
+ private uint? optionalUint32Wrapper_;
+ public uint? OptionalUint32Wrapper {
+ get { return optionalUint32Wrapper_; }
+ set {
+ optionalUint32Wrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_uint64_wrapper" field.</summary>
+ public const int OptionalUint64WrapperFieldNumber = 205;
+ private static readonly pb::FieldCodec<ulong?> _single_optionalUint64Wrapper_codec = pb::FieldCodec.ForStructWrapper<ulong>(1642);
+ private ulong? optionalUint64Wrapper_;
+ public ulong? OptionalUint64Wrapper {
+ get { return optionalUint64Wrapper_; }
+ set {
+ optionalUint64Wrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_float_wrapper" field.</summary>
+ public const int OptionalFloatWrapperFieldNumber = 206;
+ private static readonly pb::FieldCodec<float?> _single_optionalFloatWrapper_codec = pb::FieldCodec.ForStructWrapper<float>(1650);
+ private float? optionalFloatWrapper_;
+ public float? OptionalFloatWrapper {
+ get { return optionalFloatWrapper_; }
+ set {
+ optionalFloatWrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_double_wrapper" field.</summary>
+ public const int OptionalDoubleWrapperFieldNumber = 207;
+ private static readonly pb::FieldCodec<double?> _single_optionalDoubleWrapper_codec = pb::FieldCodec.ForStructWrapper<double>(1658);
+ private double? optionalDoubleWrapper_;
+ public double? OptionalDoubleWrapper {
+ get { return optionalDoubleWrapper_; }
+ set {
+ optionalDoubleWrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_string_wrapper" field.</summary>
+ public const int OptionalStringWrapperFieldNumber = 208;
+ private static readonly pb::FieldCodec<string> _single_optionalStringWrapper_codec = pb::FieldCodec.ForClassWrapper<string>(1666);
+ private string optionalStringWrapper_;
+ public string OptionalStringWrapper {
+ get { return optionalStringWrapper_; }
+ set {
+ optionalStringWrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_bytes_wrapper" field.</summary>
+ public const int OptionalBytesWrapperFieldNumber = 209;
+ private static readonly pb::FieldCodec<pb::ByteString> _single_optionalBytesWrapper_codec = pb::FieldCodec.ForClassWrapper<pb::ByteString>(1674);
+ private pb::ByteString optionalBytesWrapper_;
+ public pb::ByteString OptionalBytesWrapper {
+ get { return optionalBytesWrapper_; }
+ set {
+ optionalBytesWrapper_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "repeated_bool_wrapper" field.</summary>
+ public const int RepeatedBoolWrapperFieldNumber = 211;
+ private static readonly pb::FieldCodec<bool?> _repeated_repeatedBoolWrapper_codec
+ = pb::FieldCodec.ForStructWrapper<bool>(1690);
+ private readonly pbc::RepeatedField<bool?> repeatedBoolWrapper_ = new pbc::RepeatedField<bool?>();
+ public pbc::RepeatedField<bool?> RepeatedBoolWrapper {
+ get { return repeatedBoolWrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_int32_wrapper" field.</summary>
+ public const int RepeatedInt32WrapperFieldNumber = 212;
+ private static readonly pb::FieldCodec<int?> _repeated_repeatedInt32Wrapper_codec
+ = pb::FieldCodec.ForStructWrapper<int>(1698);
+ private readonly pbc::RepeatedField<int?> repeatedInt32Wrapper_ = new pbc::RepeatedField<int?>();
+ public pbc::RepeatedField<int?> RepeatedInt32Wrapper {
+ get { return repeatedInt32Wrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_int64_wrapper" field.</summary>
+ public const int RepeatedInt64WrapperFieldNumber = 213;
+ private static readonly pb::FieldCodec<long?> _repeated_repeatedInt64Wrapper_codec
+ = pb::FieldCodec.ForStructWrapper<long>(1706);
+ private readonly pbc::RepeatedField<long?> repeatedInt64Wrapper_ = new pbc::RepeatedField<long?>();
+ public pbc::RepeatedField<long?> RepeatedInt64Wrapper {
+ get { return repeatedInt64Wrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_uint32_wrapper" field.</summary>
+ public const int RepeatedUint32WrapperFieldNumber = 214;
+ private static readonly pb::FieldCodec<uint?> _repeated_repeatedUint32Wrapper_codec
+ = pb::FieldCodec.ForStructWrapper<uint>(1714);
+ private readonly pbc::RepeatedField<uint?> repeatedUint32Wrapper_ = new pbc::RepeatedField<uint?>();
+ public pbc::RepeatedField<uint?> RepeatedUint32Wrapper {
+ get { return repeatedUint32Wrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_uint64_wrapper" field.</summary>
+ public const int RepeatedUint64WrapperFieldNumber = 215;
+ private static readonly pb::FieldCodec<ulong?> _repeated_repeatedUint64Wrapper_codec
+ = pb::FieldCodec.ForStructWrapper<ulong>(1722);
+ private readonly pbc::RepeatedField<ulong?> repeatedUint64Wrapper_ = new pbc::RepeatedField<ulong?>();
+ public pbc::RepeatedField<ulong?> RepeatedUint64Wrapper {
+ get { return repeatedUint64Wrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_float_wrapper" field.</summary>
+ public const int RepeatedFloatWrapperFieldNumber = 216;
+ private static readonly pb::FieldCodec<float?> _repeated_repeatedFloatWrapper_codec
+ = pb::FieldCodec.ForStructWrapper<float>(1730);
+ private readonly pbc::RepeatedField<float?> repeatedFloatWrapper_ = new pbc::RepeatedField<float?>();
+ public pbc::RepeatedField<float?> RepeatedFloatWrapper {
+ get { return repeatedFloatWrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_double_wrapper" field.</summary>
+ public const int RepeatedDoubleWrapperFieldNumber = 217;
+ private static readonly pb::FieldCodec<double?> _repeated_repeatedDoubleWrapper_codec
+ = pb::FieldCodec.ForStructWrapper<double>(1738);
+ private readonly pbc::RepeatedField<double?> repeatedDoubleWrapper_ = new pbc::RepeatedField<double?>();
+ public pbc::RepeatedField<double?> RepeatedDoubleWrapper {
+ get { return repeatedDoubleWrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_string_wrapper" field.</summary>
+ public const int RepeatedStringWrapperFieldNumber = 218;
+ private static readonly pb::FieldCodec<string> _repeated_repeatedStringWrapper_codec
+ = pb::FieldCodec.ForClassWrapper<string>(1746);
+ private readonly pbc::RepeatedField<string> repeatedStringWrapper_ = new pbc::RepeatedField<string>();
+ public pbc::RepeatedField<string> RepeatedStringWrapper {
+ get { return repeatedStringWrapper_; }
+ }
+
+ /// <summary>Field number for the "repeated_bytes_wrapper" field.</summary>
+ public const int RepeatedBytesWrapperFieldNumber = 219;
+ private static readonly pb::FieldCodec<pb::ByteString> _repeated_repeatedBytesWrapper_codec
+ = pb::FieldCodec.ForClassWrapper<pb::ByteString>(1754);
+ private readonly pbc::RepeatedField<pb::ByteString> repeatedBytesWrapper_ = new pbc::RepeatedField<pb::ByteString>();
+ public pbc::RepeatedField<pb::ByteString> RepeatedBytesWrapper {
+ get { return repeatedBytesWrapper_; }
+ }
+
+ /// <summary>Field number for the "optional_duration" field.</summary>
+ public const int OptionalDurationFieldNumber = 301;
+ private global::Google.Protobuf.WellKnownTypes.Duration optionalDuration_;
+ public global::Google.Protobuf.WellKnownTypes.Duration OptionalDuration {
+ get { return optionalDuration_; }
+ set {
+ optionalDuration_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_timestamp" field.</summary>
+ public const int OptionalTimestampFieldNumber = 302;
+ private global::Google.Protobuf.WellKnownTypes.Timestamp optionalTimestamp_;
+ public global::Google.Protobuf.WellKnownTypes.Timestamp OptionalTimestamp {
+ get { return optionalTimestamp_; }
+ set {
+ optionalTimestamp_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_field_mask" field.</summary>
+ public const int OptionalFieldMaskFieldNumber = 303;
+ private global::Google.Protobuf.WellKnownTypes.FieldMask optionalFieldMask_;
+ public global::Google.Protobuf.WellKnownTypes.FieldMask OptionalFieldMask {
+ get { return optionalFieldMask_; }
+ set {
+ optionalFieldMask_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_struct" field.</summary>
+ public const int OptionalStructFieldNumber = 304;
+ private global::Google.Protobuf.WellKnownTypes.Struct optionalStruct_;
+ public global::Google.Protobuf.WellKnownTypes.Struct OptionalStruct {
+ get { return optionalStruct_; }
+ set {
+ optionalStruct_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_any" field.</summary>
+ public const int OptionalAnyFieldNumber = 305;
+ private global::Google.Protobuf.WellKnownTypes.Any optionalAny_;
+ public global::Google.Protobuf.WellKnownTypes.Any OptionalAny {
+ get { return optionalAny_; }
+ set {
+ optionalAny_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "optional_value" field.</summary>
+ public const int OptionalValueFieldNumber = 306;
+ private global::Google.Protobuf.WellKnownTypes.Value optionalValue_;
+ public global::Google.Protobuf.WellKnownTypes.Value OptionalValue {
+ get { return optionalValue_; }
+ set {
+ optionalValue_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "repeated_duration" field.</summary>
+ public const int RepeatedDurationFieldNumber = 311;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.Duration> _repeated_repeatedDuration_codec
+ = pb::FieldCodec.ForMessage(2490, global::Google.Protobuf.WellKnownTypes.Duration.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Duration> repeatedDuration_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Duration>();
+ public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Duration> RepeatedDuration {
+ get { return repeatedDuration_; }
+ }
+
+ /// <summary>Field number for the "repeated_timestamp" field.</summary>
+ public const int RepeatedTimestampFieldNumber = 312;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.Timestamp> _repeated_repeatedTimestamp_codec
+ = pb::FieldCodec.ForMessage(2498, global::Google.Protobuf.WellKnownTypes.Timestamp.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Timestamp> repeatedTimestamp_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Timestamp>();
+ public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Timestamp> RepeatedTimestamp {
+ get { return repeatedTimestamp_; }
+ }
+
+ /// <summary>Field number for the "repeated_fieldmask" field.</summary>
+ public const int RepeatedFieldmaskFieldNumber = 313;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.FieldMask> _repeated_repeatedFieldmask_codec
+ = pb::FieldCodec.ForMessage(2506, global::Google.Protobuf.WellKnownTypes.FieldMask.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.FieldMask> repeatedFieldmask_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.FieldMask>();
+ public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.FieldMask> RepeatedFieldmask {
+ get { return repeatedFieldmask_; }
+ }
+
+ /// <summary>Field number for the "repeated_struct" field.</summary>
+ public const int RepeatedStructFieldNumber = 324;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.Struct> _repeated_repeatedStruct_codec
+ = pb::FieldCodec.ForMessage(2594, global::Google.Protobuf.WellKnownTypes.Struct.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Struct> repeatedStruct_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Struct>();
+ public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Struct> RepeatedStruct {
+ get { return repeatedStruct_; }
+ }
+
+ /// <summary>Field number for the "repeated_any" field.</summary>
+ public const int RepeatedAnyFieldNumber = 315;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.Any> _repeated_repeatedAny_codec
+ = pb::FieldCodec.ForMessage(2522, global::Google.Protobuf.WellKnownTypes.Any.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Any> repeatedAny_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Any>();
+ public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Any> RepeatedAny {
+ get { return repeatedAny_; }
+ }
+
+ /// <summary>Field number for the "repeated_value" field.</summary>
+ public const int RepeatedValueFieldNumber = 316;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.WellKnownTypes.Value> _repeated_repeatedValue_codec
+ = pb::FieldCodec.ForMessage(2530, global::Google.Protobuf.WellKnownTypes.Value.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Value> repeatedValue_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Value>();
+ public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Value> RepeatedValue {
+ get { return repeatedValue_; }
+ }
+
+ /// <summary>Field number for the "fieldname1" field.</summary>
+ public const int Fieldname1FieldNumber = 401;
+ private int fieldname1_;
+ /// <summary>
+ /// Test field-name-to-JSON-name convention.
+ /// </summary>
+ public int Fieldname1 {
+ get { return fieldname1_; }
+ set {
+ fieldname1_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "field_name2" field.</summary>
+ public const int FieldName2FieldNumber = 402;
+ private int fieldName2_;
+ public int FieldName2 {
+ get { return fieldName2_; }
+ set {
+ fieldName2_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "_field_name3" field.</summary>
+ public const int FieldName3FieldNumber = 403;
+ private int FieldName3_;
+ public int FieldName3 {
+ get { return FieldName3_; }
+ set {
+ FieldName3_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "field__name4_" field.</summary>
+ public const int FieldName4FieldNumber = 404;
+ private int fieldName4_;
+ public int FieldName4 {
+ get { return fieldName4_; }
+ set {
+ fieldName4_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "field0name5" field.</summary>
+ public const int Field0Name5FieldNumber = 405;
+ private int field0Name5_;
+ public int Field0Name5 {
+ get { return field0Name5_; }
+ set {
+ field0Name5_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "field_0_name6" field.</summary>
+ public const int Field0Name6FieldNumber = 406;
+ private int field0Name6_;
+ public int Field0Name6 {
+ get { return field0Name6_; }
+ set {
+ field0Name6_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "fieldName7" field.</summary>
+ public const int FieldName7FieldNumber = 407;
+ private int fieldName7_;
+ public int FieldName7 {
+ get { return fieldName7_; }
+ set {
+ fieldName7_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "FieldName8" field.</summary>
+ public const int FieldName8FieldNumber = 408;
+ private int fieldName8_;
+ public int FieldName8 {
+ get { return fieldName8_; }
+ set {
+ fieldName8_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "field_Name9" field.</summary>
+ public const int FieldName9FieldNumber = 409;
+ private int fieldName9_;
+ public int FieldName9 {
+ get { return fieldName9_; }
+ set {
+ fieldName9_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "Field_Name10" field.</summary>
+ public const int FieldName10FieldNumber = 410;
+ private int fieldName10_;
+ public int FieldName10 {
+ get { return fieldName10_; }
+ set {
+ fieldName10_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "FIELD_NAME11" field.</summary>
+ public const int FIELDNAME11FieldNumber = 411;
+ private int fIELDNAME11_;
+ public int FIELDNAME11 {
+ get { return fIELDNAME11_; }
+ set {
+ fIELDNAME11_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "FIELD_name12" field.</summary>
+ public const int FIELDName12FieldNumber = 412;
+ private int fIELDName12_;
+ public int FIELDName12 {
+ get { return fIELDName12_; }
+ set {
+ fIELDName12_ = value;
+ }
+ }
+
private object oneofField_;
/// <summary>Enum of possible cases for the "oneof_field" oneof.</summary>
public enum OneofFieldOneofCase {
@@ -1467,6 +2014,48 @@ namespace Conformance {
if (!object.Equals(OneofNestedMessage, other.OneofNestedMessage)) return false;
if (OneofString != other.OneofString) return false;
if (OneofBytes != other.OneofBytes) return false;
+ if (OptionalBoolWrapper != other.OptionalBoolWrapper) return false;
+ if (OptionalInt32Wrapper != other.OptionalInt32Wrapper) return false;
+ if (OptionalInt64Wrapper != other.OptionalInt64Wrapper) return false;
+ if (OptionalUint32Wrapper != other.OptionalUint32Wrapper) return false;
+ if (OptionalUint64Wrapper != other.OptionalUint64Wrapper) return false;
+ if (OptionalFloatWrapper != other.OptionalFloatWrapper) return false;
+ if (OptionalDoubleWrapper != other.OptionalDoubleWrapper) return false;
+ if (OptionalStringWrapper != other.OptionalStringWrapper) return false;
+ if (OptionalBytesWrapper != other.OptionalBytesWrapper) return false;
+ if(!repeatedBoolWrapper_.Equals(other.repeatedBoolWrapper_)) return false;
+ if(!repeatedInt32Wrapper_.Equals(other.repeatedInt32Wrapper_)) return false;
+ if(!repeatedInt64Wrapper_.Equals(other.repeatedInt64Wrapper_)) return false;
+ if(!repeatedUint32Wrapper_.Equals(other.repeatedUint32Wrapper_)) return false;
+ if(!repeatedUint64Wrapper_.Equals(other.repeatedUint64Wrapper_)) return false;
+ if(!repeatedFloatWrapper_.Equals(other.repeatedFloatWrapper_)) return false;
+ if(!repeatedDoubleWrapper_.Equals(other.repeatedDoubleWrapper_)) return false;
+ if(!repeatedStringWrapper_.Equals(other.repeatedStringWrapper_)) return false;
+ if(!repeatedBytesWrapper_.Equals(other.repeatedBytesWrapper_)) return false;
+ if (!object.Equals(OptionalDuration, other.OptionalDuration)) return false;
+ if (!object.Equals(OptionalTimestamp, other.OptionalTimestamp)) return false;
+ if (!object.Equals(OptionalFieldMask, other.OptionalFieldMask)) return false;
+ if (!object.Equals(OptionalStruct, other.OptionalStruct)) return false;
+ if (!object.Equals(OptionalAny, other.OptionalAny)) return false;
+ if (!object.Equals(OptionalValue, other.OptionalValue)) return false;
+ if(!repeatedDuration_.Equals(other.repeatedDuration_)) return false;
+ if(!repeatedTimestamp_.Equals(other.repeatedTimestamp_)) return false;
+ if(!repeatedFieldmask_.Equals(other.repeatedFieldmask_)) return false;
+ if(!repeatedStruct_.Equals(other.repeatedStruct_)) return false;
+ if(!repeatedAny_.Equals(other.repeatedAny_)) return false;
+ if(!repeatedValue_.Equals(other.repeatedValue_)) return false;
+ if (Fieldname1 != other.Fieldname1) return false;
+ if (FieldName2 != other.FieldName2) return false;
+ if (FieldName3 != other.FieldName3) return false;
+ if (FieldName4 != other.FieldName4) return false;
+ if (Field0Name5 != other.Field0Name5) return false;
+ if (Field0Name6 != other.Field0Name6) return false;
+ if (FieldName7 != other.FieldName7) return false;
+ if (FieldName8 != other.FieldName8) return false;
+ if (FieldName9 != other.FieldName9) return false;
+ if (FieldName10 != other.FieldName10) return false;
+ if (FIELDNAME11 != other.FIELDNAME11) return false;
+ if (FIELDName12 != other.FIELDName12) return false;
if (OneofFieldCase != other.OneofFieldCase) return false;
return true;
}
@@ -1539,12 +2128,54 @@ namespace Conformance {
if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) hash ^= OneofNestedMessage.GetHashCode();
if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) hash ^= OneofString.GetHashCode();
if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) hash ^= OneofBytes.GetHashCode();
+ if (optionalBoolWrapper_ != null) hash ^= OptionalBoolWrapper.GetHashCode();
+ if (optionalInt32Wrapper_ != null) hash ^= OptionalInt32Wrapper.GetHashCode();
+ if (optionalInt64Wrapper_ != null) hash ^= OptionalInt64Wrapper.GetHashCode();
+ if (optionalUint32Wrapper_ != null) hash ^= OptionalUint32Wrapper.GetHashCode();
+ if (optionalUint64Wrapper_ != null) hash ^= OptionalUint64Wrapper.GetHashCode();
+ if (optionalFloatWrapper_ != null) hash ^= OptionalFloatWrapper.GetHashCode();
+ if (optionalDoubleWrapper_ != null) hash ^= OptionalDoubleWrapper.GetHashCode();
+ if (optionalStringWrapper_ != null) hash ^= OptionalStringWrapper.GetHashCode();
+ if (optionalBytesWrapper_ != null) hash ^= OptionalBytesWrapper.GetHashCode();
+ hash ^= repeatedBoolWrapper_.GetHashCode();
+ hash ^= repeatedInt32Wrapper_.GetHashCode();
+ hash ^= repeatedInt64Wrapper_.GetHashCode();
+ hash ^= repeatedUint32Wrapper_.GetHashCode();
+ hash ^= repeatedUint64Wrapper_.GetHashCode();
+ hash ^= repeatedFloatWrapper_.GetHashCode();
+ hash ^= repeatedDoubleWrapper_.GetHashCode();
+ hash ^= repeatedStringWrapper_.GetHashCode();
+ hash ^= repeatedBytesWrapper_.GetHashCode();
+ if (optionalDuration_ != null) hash ^= OptionalDuration.GetHashCode();
+ if (optionalTimestamp_ != null) hash ^= OptionalTimestamp.GetHashCode();
+ if (optionalFieldMask_ != null) hash ^= OptionalFieldMask.GetHashCode();
+ if (optionalStruct_ != null) hash ^= OptionalStruct.GetHashCode();
+ if (optionalAny_ != null) hash ^= OptionalAny.GetHashCode();
+ if (optionalValue_ != null) hash ^= OptionalValue.GetHashCode();
+ hash ^= repeatedDuration_.GetHashCode();
+ hash ^= repeatedTimestamp_.GetHashCode();
+ hash ^= repeatedFieldmask_.GetHashCode();
+ hash ^= repeatedStruct_.GetHashCode();
+ hash ^= repeatedAny_.GetHashCode();
+ hash ^= repeatedValue_.GetHashCode();
+ if (Fieldname1 != 0) hash ^= Fieldname1.GetHashCode();
+ if (FieldName2 != 0) hash ^= FieldName2.GetHashCode();
+ if (FieldName3 != 0) hash ^= FieldName3.GetHashCode();
+ if (FieldName4 != 0) hash ^= FieldName4.GetHashCode();
+ if (Field0Name5 != 0) hash ^= Field0Name5.GetHashCode();
+ if (Field0Name6 != 0) hash ^= Field0Name6.GetHashCode();
+ if (FieldName7 != 0) hash ^= FieldName7.GetHashCode();
+ if (FieldName8 != 0) hash ^= FieldName8.GetHashCode();
+ if (FieldName9 != 0) hash ^= FieldName9.GetHashCode();
+ if (FieldName10 != 0) hash ^= FieldName10.GetHashCode();
+ if (FIELDNAME11 != 0) hash ^= FIELDNAME11.GetHashCode();
+ if (FIELDName12 != 0) hash ^= FIELDName12.GetHashCode();
hash ^= (int) oneofFieldCase_;
return hash;
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1692,6 +2323,120 @@ namespace Conformance {
output.WriteRawTag(146, 7);
output.WriteBytes(OneofBytes);
}
+ if (optionalBoolWrapper_ != null) {
+ _single_optionalBoolWrapper_codec.WriteTagAndValue(output, OptionalBoolWrapper);
+ }
+ if (optionalInt32Wrapper_ != null) {
+ _single_optionalInt32Wrapper_codec.WriteTagAndValue(output, OptionalInt32Wrapper);
+ }
+ if (optionalInt64Wrapper_ != null) {
+ _single_optionalInt64Wrapper_codec.WriteTagAndValue(output, OptionalInt64Wrapper);
+ }
+ if (optionalUint32Wrapper_ != null) {
+ _single_optionalUint32Wrapper_codec.WriteTagAndValue(output, OptionalUint32Wrapper);
+ }
+ if (optionalUint64Wrapper_ != null) {
+ _single_optionalUint64Wrapper_codec.WriteTagAndValue(output, OptionalUint64Wrapper);
+ }
+ if (optionalFloatWrapper_ != null) {
+ _single_optionalFloatWrapper_codec.WriteTagAndValue(output, OptionalFloatWrapper);
+ }
+ if (optionalDoubleWrapper_ != null) {
+ _single_optionalDoubleWrapper_codec.WriteTagAndValue(output, OptionalDoubleWrapper);
+ }
+ if (optionalStringWrapper_ != null) {
+ _single_optionalStringWrapper_codec.WriteTagAndValue(output, OptionalStringWrapper);
+ }
+ if (optionalBytesWrapper_ != null) {
+ _single_optionalBytesWrapper_codec.WriteTagAndValue(output, OptionalBytesWrapper);
+ }
+ repeatedBoolWrapper_.WriteTo(output, _repeated_repeatedBoolWrapper_codec);
+ repeatedInt32Wrapper_.WriteTo(output, _repeated_repeatedInt32Wrapper_codec);
+ repeatedInt64Wrapper_.WriteTo(output, _repeated_repeatedInt64Wrapper_codec);
+ repeatedUint32Wrapper_.WriteTo(output, _repeated_repeatedUint32Wrapper_codec);
+ repeatedUint64Wrapper_.WriteTo(output, _repeated_repeatedUint64Wrapper_codec);
+ repeatedFloatWrapper_.WriteTo(output, _repeated_repeatedFloatWrapper_codec);
+ repeatedDoubleWrapper_.WriteTo(output, _repeated_repeatedDoubleWrapper_codec);
+ repeatedStringWrapper_.WriteTo(output, _repeated_repeatedStringWrapper_codec);
+ repeatedBytesWrapper_.WriteTo(output, _repeated_repeatedBytesWrapper_codec);
+ if (optionalDuration_ != null) {
+ output.WriteRawTag(234, 18);
+ output.WriteMessage(OptionalDuration);
+ }
+ if (optionalTimestamp_ != null) {
+ output.WriteRawTag(242, 18);
+ output.WriteMessage(OptionalTimestamp);
+ }
+ if (optionalFieldMask_ != null) {
+ output.WriteRawTag(250, 18);
+ output.WriteMessage(OptionalFieldMask);
+ }
+ if (optionalStruct_ != null) {
+ output.WriteRawTag(130, 19);
+ output.WriteMessage(OptionalStruct);
+ }
+ if (optionalAny_ != null) {
+ output.WriteRawTag(138, 19);
+ output.WriteMessage(OptionalAny);
+ }
+ if (optionalValue_ != null) {
+ output.WriteRawTag(146, 19);
+ output.WriteMessage(OptionalValue);
+ }
+ repeatedDuration_.WriteTo(output, _repeated_repeatedDuration_codec);
+ repeatedTimestamp_.WriteTo(output, _repeated_repeatedTimestamp_codec);
+ repeatedFieldmask_.WriteTo(output, _repeated_repeatedFieldmask_codec);
+ repeatedAny_.WriteTo(output, _repeated_repeatedAny_codec);
+ repeatedValue_.WriteTo(output, _repeated_repeatedValue_codec);
+ repeatedStruct_.WriteTo(output, _repeated_repeatedStruct_codec);
+ if (Fieldname1 != 0) {
+ output.WriteRawTag(136, 25);
+ output.WriteInt32(Fieldname1);
+ }
+ if (FieldName2 != 0) {
+ output.WriteRawTag(144, 25);
+ output.WriteInt32(FieldName2);
+ }
+ if (FieldName3 != 0) {
+ output.WriteRawTag(152, 25);
+ output.WriteInt32(FieldName3);
+ }
+ if (FieldName4 != 0) {
+ output.WriteRawTag(160, 25);
+ output.WriteInt32(FieldName4);
+ }
+ if (Field0Name5 != 0) {
+ output.WriteRawTag(168, 25);
+ output.WriteInt32(Field0Name5);
+ }
+ if (Field0Name6 != 0) {
+ output.WriteRawTag(176, 25);
+ output.WriteInt32(Field0Name6);
+ }
+ if (FieldName7 != 0) {
+ output.WriteRawTag(184, 25);
+ output.WriteInt32(FieldName7);
+ }
+ if (FieldName8 != 0) {
+ output.WriteRawTag(192, 25);
+ output.WriteInt32(FieldName8);
+ }
+ if (FieldName9 != 0) {
+ output.WriteRawTag(200, 25);
+ output.WriteInt32(FieldName9);
+ }
+ if (FieldName10 != 0) {
+ output.WriteRawTag(208, 25);
+ output.WriteInt32(FieldName10);
+ }
+ if (FIELDNAME11 != 0) {
+ output.WriteRawTag(216, 25);
+ output.WriteInt32(FIELDNAME11);
+ }
+ if (FIELDName12 != 0) {
+ output.WriteRawTag(224, 25);
+ output.WriteInt32(FIELDName12);
+ }
}
public int CalculateSize() {
@@ -1814,6 +2559,102 @@ namespace Conformance {
if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
size += 2 + pb::CodedOutputStream.ComputeBytesSize(OneofBytes);
}
+ if (optionalBoolWrapper_ != null) {
+ size += _single_optionalBoolWrapper_codec.CalculateSizeWithTag(OptionalBoolWrapper);
+ }
+ if (optionalInt32Wrapper_ != null) {
+ size += _single_optionalInt32Wrapper_codec.CalculateSizeWithTag(OptionalInt32Wrapper);
+ }
+ if (optionalInt64Wrapper_ != null) {
+ size += _single_optionalInt64Wrapper_codec.CalculateSizeWithTag(OptionalInt64Wrapper);
+ }
+ if (optionalUint32Wrapper_ != null) {
+ size += _single_optionalUint32Wrapper_codec.CalculateSizeWithTag(OptionalUint32Wrapper);
+ }
+ if (optionalUint64Wrapper_ != null) {
+ size += _single_optionalUint64Wrapper_codec.CalculateSizeWithTag(OptionalUint64Wrapper);
+ }
+ if (optionalFloatWrapper_ != null) {
+ size += _single_optionalFloatWrapper_codec.CalculateSizeWithTag(OptionalFloatWrapper);
+ }
+ if (optionalDoubleWrapper_ != null) {
+ size += _single_optionalDoubleWrapper_codec.CalculateSizeWithTag(OptionalDoubleWrapper);
+ }
+ if (optionalStringWrapper_ != null) {
+ size += _single_optionalStringWrapper_codec.CalculateSizeWithTag(OptionalStringWrapper);
+ }
+ if (optionalBytesWrapper_ != null) {
+ size += _single_optionalBytesWrapper_codec.CalculateSizeWithTag(OptionalBytesWrapper);
+ }
+ size += repeatedBoolWrapper_.CalculateSize(_repeated_repeatedBoolWrapper_codec);
+ size += repeatedInt32Wrapper_.CalculateSize(_repeated_repeatedInt32Wrapper_codec);
+ size += repeatedInt64Wrapper_.CalculateSize(_repeated_repeatedInt64Wrapper_codec);
+ size += repeatedUint32Wrapper_.CalculateSize(_repeated_repeatedUint32Wrapper_codec);
+ size += repeatedUint64Wrapper_.CalculateSize(_repeated_repeatedUint64Wrapper_codec);
+ size += repeatedFloatWrapper_.CalculateSize(_repeated_repeatedFloatWrapper_codec);
+ size += repeatedDoubleWrapper_.CalculateSize(_repeated_repeatedDoubleWrapper_codec);
+ size += repeatedStringWrapper_.CalculateSize(_repeated_repeatedStringWrapper_codec);
+ size += repeatedBytesWrapper_.CalculateSize(_repeated_repeatedBytesWrapper_codec);
+ if (optionalDuration_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalDuration);
+ }
+ if (optionalTimestamp_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalTimestamp);
+ }
+ if (optionalFieldMask_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalFieldMask);
+ }
+ if (optionalStruct_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalStruct);
+ }
+ if (optionalAny_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalAny);
+ }
+ if (optionalValue_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalValue);
+ }
+ size += repeatedDuration_.CalculateSize(_repeated_repeatedDuration_codec);
+ size += repeatedTimestamp_.CalculateSize(_repeated_repeatedTimestamp_codec);
+ size += repeatedFieldmask_.CalculateSize(_repeated_repeatedFieldmask_codec);
+ size += repeatedStruct_.CalculateSize(_repeated_repeatedStruct_codec);
+ size += repeatedAny_.CalculateSize(_repeated_repeatedAny_codec);
+ size += repeatedValue_.CalculateSize(_repeated_repeatedValue_codec);
+ if (Fieldname1 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(Fieldname1);
+ }
+ if (FieldName2 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName2);
+ }
+ if (FieldName3 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName3);
+ }
+ if (FieldName4 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName4);
+ }
+ if (Field0Name5 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(Field0Name5);
+ }
+ if (Field0Name6 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(Field0Name6);
+ }
+ if (FieldName7 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName7);
+ }
+ if (FieldName8 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName8);
+ }
+ if (FieldName9 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName9);
+ }
+ if (FieldName10 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FieldName10);
+ }
+ if (FIELDNAME11 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FIELDNAME11);
+ }
+ if (FIELDName12 != 0) {
+ size += 2 + pb::CodedOutputStream.ComputeInt32Size(FIELDName12);
+ }
return size;
}
@@ -1936,6 +2777,138 @@ namespace Conformance {
mapStringForeignMessage_.Add(other.mapStringForeignMessage_);
mapStringNestedEnum_.Add(other.mapStringNestedEnum_);
mapStringForeignEnum_.Add(other.mapStringForeignEnum_);
+ if (other.optionalBoolWrapper_ != null) {
+ if (optionalBoolWrapper_ == null || other.OptionalBoolWrapper != false) {
+ OptionalBoolWrapper = other.OptionalBoolWrapper;
+ }
+ }
+ if (other.optionalInt32Wrapper_ != null) {
+ if (optionalInt32Wrapper_ == null || other.OptionalInt32Wrapper != 0) {
+ OptionalInt32Wrapper = other.OptionalInt32Wrapper;
+ }
+ }
+ if (other.optionalInt64Wrapper_ != null) {
+ if (optionalInt64Wrapper_ == null || other.OptionalInt64Wrapper != 0L) {
+ OptionalInt64Wrapper = other.OptionalInt64Wrapper;
+ }
+ }
+ if (other.optionalUint32Wrapper_ != null) {
+ if (optionalUint32Wrapper_ == null || other.OptionalUint32Wrapper != 0) {
+ OptionalUint32Wrapper = other.OptionalUint32Wrapper;
+ }
+ }
+ if (other.optionalUint64Wrapper_ != null) {
+ if (optionalUint64Wrapper_ == null || other.OptionalUint64Wrapper != 0UL) {
+ OptionalUint64Wrapper = other.OptionalUint64Wrapper;
+ }
+ }
+ if (other.optionalFloatWrapper_ != null) {
+ if (optionalFloatWrapper_ == null || other.OptionalFloatWrapper != 0F) {
+ OptionalFloatWrapper = other.OptionalFloatWrapper;
+ }
+ }
+ if (other.optionalDoubleWrapper_ != null) {
+ if (optionalDoubleWrapper_ == null || other.OptionalDoubleWrapper != 0D) {
+ OptionalDoubleWrapper = other.OptionalDoubleWrapper;
+ }
+ }
+ if (other.optionalStringWrapper_ != null) {
+ if (optionalStringWrapper_ == null || other.OptionalStringWrapper != "") {
+ OptionalStringWrapper = other.OptionalStringWrapper;
+ }
+ }
+ if (other.optionalBytesWrapper_ != null) {
+ if (optionalBytesWrapper_ == null || other.OptionalBytesWrapper != pb::ByteString.Empty) {
+ OptionalBytesWrapper = other.OptionalBytesWrapper;
+ }
+ }
+ repeatedBoolWrapper_.Add(other.repeatedBoolWrapper_);
+ repeatedInt32Wrapper_.Add(other.repeatedInt32Wrapper_);
+ repeatedInt64Wrapper_.Add(other.repeatedInt64Wrapper_);
+ repeatedUint32Wrapper_.Add(other.repeatedUint32Wrapper_);
+ repeatedUint64Wrapper_.Add(other.repeatedUint64Wrapper_);
+ repeatedFloatWrapper_.Add(other.repeatedFloatWrapper_);
+ repeatedDoubleWrapper_.Add(other.repeatedDoubleWrapper_);
+ repeatedStringWrapper_.Add(other.repeatedStringWrapper_);
+ repeatedBytesWrapper_.Add(other.repeatedBytesWrapper_);
+ if (other.optionalDuration_ != null) {
+ if (optionalDuration_ == null) {
+ optionalDuration_ = new global::Google.Protobuf.WellKnownTypes.Duration();
+ }
+ OptionalDuration.MergeFrom(other.OptionalDuration);
+ }
+ if (other.optionalTimestamp_ != null) {
+ if (optionalTimestamp_ == null) {
+ optionalTimestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ OptionalTimestamp.MergeFrom(other.OptionalTimestamp);
+ }
+ if (other.optionalFieldMask_ != null) {
+ if (optionalFieldMask_ == null) {
+ optionalFieldMask_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
+ }
+ OptionalFieldMask.MergeFrom(other.OptionalFieldMask);
+ }
+ if (other.optionalStruct_ != null) {
+ if (optionalStruct_ == null) {
+ optionalStruct_ = new global::Google.Protobuf.WellKnownTypes.Struct();
+ }
+ OptionalStruct.MergeFrom(other.OptionalStruct);
+ }
+ if (other.optionalAny_ != null) {
+ if (optionalAny_ == null) {
+ optionalAny_ = new global::Google.Protobuf.WellKnownTypes.Any();
+ }
+ OptionalAny.MergeFrom(other.OptionalAny);
+ }
+ if (other.optionalValue_ != null) {
+ if (optionalValue_ == null) {
+ optionalValue_ = new global::Google.Protobuf.WellKnownTypes.Value();
+ }
+ OptionalValue.MergeFrom(other.OptionalValue);
+ }
+ repeatedDuration_.Add(other.repeatedDuration_);
+ repeatedTimestamp_.Add(other.repeatedTimestamp_);
+ repeatedFieldmask_.Add(other.repeatedFieldmask_);
+ repeatedStruct_.Add(other.repeatedStruct_);
+ repeatedAny_.Add(other.repeatedAny_);
+ repeatedValue_.Add(other.repeatedValue_);
+ if (other.Fieldname1 != 0) {
+ Fieldname1 = other.Fieldname1;
+ }
+ if (other.FieldName2 != 0) {
+ FieldName2 = other.FieldName2;
+ }
+ if (other.FieldName3 != 0) {
+ FieldName3 = other.FieldName3;
+ }
+ if (other.FieldName4 != 0) {
+ FieldName4 = other.FieldName4;
+ }
+ if (other.Field0Name5 != 0) {
+ Field0Name5 = other.Field0Name5;
+ }
+ if (other.Field0Name6 != 0) {
+ Field0Name6 = other.Field0Name6;
+ }
+ if (other.FieldName7 != 0) {
+ FieldName7 = other.FieldName7;
+ }
+ if (other.FieldName8 != 0) {
+ FieldName8 = other.FieldName8;
+ }
+ if (other.FieldName9 != 0) {
+ FieldName9 = other.FieldName9;
+ }
+ if (other.FieldName10 != 0) {
+ FieldName10 = other.FieldName10;
+ }
+ if (other.FIELDNAME11 != 0) {
+ FIELDNAME11 = other.FIELDNAME11;
+ }
+ if (other.FIELDName12 != 0) {
+ FIELDName12 = other.FIELDName12;
+ }
switch (other.OneofFieldCase) {
case OneofFieldOneofCase.OneofUint32:
OneofUint32 = other.OneofUint32;
@@ -2253,6 +3226,219 @@ namespace Conformance {
OneofBytes = input.ReadBytes();
break;
}
+ case 1610: {
+ bool? value = _single_optionalBoolWrapper_codec.Read(input);
+ if (optionalBoolWrapper_ == null || value != false) {
+ OptionalBoolWrapper = value;
+ }
+ break;
+ }
+ case 1618: {
+ int? value = _single_optionalInt32Wrapper_codec.Read(input);
+ if (optionalInt32Wrapper_ == null || value != 0) {
+ OptionalInt32Wrapper = value;
+ }
+ break;
+ }
+ case 1626: {
+ long? value = _single_optionalInt64Wrapper_codec.Read(input);
+ if (optionalInt64Wrapper_ == null || value != 0L) {
+ OptionalInt64Wrapper = value;
+ }
+ break;
+ }
+ case 1634: {
+ uint? value = _single_optionalUint32Wrapper_codec.Read(input);
+ if (optionalUint32Wrapper_ == null || value != 0) {
+ OptionalUint32Wrapper = value;
+ }
+ break;
+ }
+ case 1642: {
+ ulong? value = _single_optionalUint64Wrapper_codec.Read(input);
+ if (optionalUint64Wrapper_ == null || value != 0UL) {
+ OptionalUint64Wrapper = value;
+ }
+ break;
+ }
+ case 1650: {
+ float? value = _single_optionalFloatWrapper_codec.Read(input);
+ if (optionalFloatWrapper_ == null || value != 0F) {
+ OptionalFloatWrapper = value;
+ }
+ break;
+ }
+ case 1658: {
+ double? value = _single_optionalDoubleWrapper_codec.Read(input);
+ if (optionalDoubleWrapper_ == null || value != 0D) {
+ OptionalDoubleWrapper = value;
+ }
+ break;
+ }
+ case 1666: {
+ string value = _single_optionalStringWrapper_codec.Read(input);
+ if (optionalStringWrapper_ == null || value != "") {
+ OptionalStringWrapper = value;
+ }
+ break;
+ }
+ case 1674: {
+ pb::ByteString value = _single_optionalBytesWrapper_codec.Read(input);
+ if (optionalBytesWrapper_ == null || value != pb::ByteString.Empty) {
+ OptionalBytesWrapper = value;
+ }
+ break;
+ }
+ case 1690: {
+ repeatedBoolWrapper_.AddEntriesFrom(input, _repeated_repeatedBoolWrapper_codec);
+ break;
+ }
+ case 1698: {
+ repeatedInt32Wrapper_.AddEntriesFrom(input, _repeated_repeatedInt32Wrapper_codec);
+ break;
+ }
+ case 1706: {
+ repeatedInt64Wrapper_.AddEntriesFrom(input, _repeated_repeatedInt64Wrapper_codec);
+ break;
+ }
+ case 1714: {
+ repeatedUint32Wrapper_.AddEntriesFrom(input, _repeated_repeatedUint32Wrapper_codec);
+ break;
+ }
+ case 1722: {
+ repeatedUint64Wrapper_.AddEntriesFrom(input, _repeated_repeatedUint64Wrapper_codec);
+ break;
+ }
+ case 1730: {
+ repeatedFloatWrapper_.AddEntriesFrom(input, _repeated_repeatedFloatWrapper_codec);
+ break;
+ }
+ case 1738: {
+ repeatedDoubleWrapper_.AddEntriesFrom(input, _repeated_repeatedDoubleWrapper_codec);
+ break;
+ }
+ case 1746: {
+ repeatedStringWrapper_.AddEntriesFrom(input, _repeated_repeatedStringWrapper_codec);
+ break;
+ }
+ case 1754: {
+ repeatedBytesWrapper_.AddEntriesFrom(input, _repeated_repeatedBytesWrapper_codec);
+ break;
+ }
+ case 2410: {
+ if (optionalDuration_ == null) {
+ optionalDuration_ = new global::Google.Protobuf.WellKnownTypes.Duration();
+ }
+ input.ReadMessage(optionalDuration_);
+ break;
+ }
+ case 2418: {
+ if (optionalTimestamp_ == null) {
+ optionalTimestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp();
+ }
+ input.ReadMessage(optionalTimestamp_);
+ break;
+ }
+ case 2426: {
+ if (optionalFieldMask_ == null) {
+ optionalFieldMask_ = new global::Google.Protobuf.WellKnownTypes.FieldMask();
+ }
+ input.ReadMessage(optionalFieldMask_);
+ break;
+ }
+ case 2434: {
+ if (optionalStruct_ == null) {
+ optionalStruct_ = new global::Google.Protobuf.WellKnownTypes.Struct();
+ }
+ input.ReadMessage(optionalStruct_);
+ break;
+ }
+ case 2442: {
+ if (optionalAny_ == null) {
+ optionalAny_ = new global::Google.Protobuf.WellKnownTypes.Any();
+ }
+ input.ReadMessage(optionalAny_);
+ break;
+ }
+ case 2450: {
+ if (optionalValue_ == null) {
+ optionalValue_ = new global::Google.Protobuf.WellKnownTypes.Value();
+ }
+ input.ReadMessage(optionalValue_);
+ break;
+ }
+ case 2490: {
+ repeatedDuration_.AddEntriesFrom(input, _repeated_repeatedDuration_codec);
+ break;
+ }
+ case 2498: {
+ repeatedTimestamp_.AddEntriesFrom(input, _repeated_repeatedTimestamp_codec);
+ break;
+ }
+ case 2506: {
+ repeatedFieldmask_.AddEntriesFrom(input, _repeated_repeatedFieldmask_codec);
+ break;
+ }
+ case 2522: {
+ repeatedAny_.AddEntriesFrom(input, _repeated_repeatedAny_codec);
+ break;
+ }
+ case 2530: {
+ repeatedValue_.AddEntriesFrom(input, _repeated_repeatedValue_codec);
+ break;
+ }
+ case 2594: {
+ repeatedStruct_.AddEntriesFrom(input, _repeated_repeatedStruct_codec);
+ break;
+ }
+ case 3208: {
+ Fieldname1 = input.ReadInt32();
+ break;
+ }
+ case 3216: {
+ FieldName2 = input.ReadInt32();
+ break;
+ }
+ case 3224: {
+ FieldName3 = input.ReadInt32();
+ break;
+ }
+ case 3232: {
+ FieldName4 = input.ReadInt32();
+ break;
+ }
+ case 3240: {
+ Field0Name5 = input.ReadInt32();
+ break;
+ }
+ case 3248: {
+ Field0Name6 = input.ReadInt32();
+ break;
+ }
+ case 3256: {
+ FieldName7 = input.ReadInt32();
+ break;
+ }
+ case 3264: {
+ FieldName8 = input.ReadInt32();
+ break;
+ }
+ case 3272: {
+ FieldName9 = input.ReadInt32();
+ break;
+ }
+ case 3280: {
+ FieldName10 = input.ReadInt32();
+ break;
+ }
+ case 3288: {
+ FIELDNAME11 = input.ReadInt32();
+ break;
+ }
+ case 3296: {
+ FIELDName12 = input.ReadInt32();
+ break;
+ }
}
}
}
@@ -2343,7 +3529,7 @@ namespace Conformance {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2471,7 +3657,7 @@ namespace Conformance {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Conformance/Program.cs b/csharp/src/Google.Protobuf.Conformance/Program.cs
index af92da9f..f3f7e295 100644
--- a/csharp/src/Google.Protobuf.Conformance/Program.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Program.cs
@@ -31,6 +31,7 @@
#endregion
using Conformance;
+using Google.Protobuf.Reflection;
using System;
using System.IO;
@@ -47,16 +48,17 @@ namespace Google.Protobuf.Conformance
// This way we get the binary streams instead of readers/writers.
var input = new BinaryReader(Console.OpenStandardInput());
var output = new BinaryWriter(Console.OpenStandardOutput());
+ var typeRegistry = TypeRegistry.FromMessages(TestAllTypes.Descriptor);
int count = 0;
- while (RunTest(input, output))
+ while (RunTest(input, output, typeRegistry))
{
count++;
}
Console.Error.WriteLine("Received EOF after {0} tests", count);
}
- private static bool RunTest(BinaryReader input, BinaryWriter output)
+ private static bool RunTest(BinaryReader input, BinaryWriter output, TypeRegistry typeRegistry)
{
int? size = ReadInt32(input);
if (size == null)
@@ -69,7 +71,7 @@ namespace Google.Protobuf.Conformance
throw new EndOfStreamException("Read " + inputData.Length + " bytes of data when expecting " + size);
}
ConformanceRequest request = ConformanceRequest.Parser.ParseFrom(inputData);
- ConformanceResponse response = PerformRequest(request);
+ ConformanceResponse response = PerformRequest(request, typeRegistry);
byte[] outputData = response.ToByteArray();
output.Write(outputData.Length);
output.Write(outputData);
@@ -77,34 +79,48 @@ namespace Google.Protobuf.Conformance
return true;
}
- private static ConformanceResponse PerformRequest(ConformanceRequest request)
+ private static ConformanceResponse PerformRequest(ConformanceRequest request, TypeRegistry typeRegistry)
{
TestAllTypes message;
- switch (request.PayloadCase)
+ try
{
- case ConformanceRequest.PayloadOneofCase.JsonPayload:
- return new ConformanceResponse { Skipped = "JSON parsing not implemented in C# yet" };
- case ConformanceRequest.PayloadOneofCase.ProtobufPayload:
- try
- {
+ switch (request.PayloadCase)
+ {
+ case ConformanceRequest.PayloadOneofCase.JsonPayload:
+ var parser = new JsonParser(new JsonParser.Settings(20, typeRegistry));
+ message = parser.Parse<TestAllTypes>(request.JsonPayload);
+ break;
+ case ConformanceRequest.PayloadOneofCase.ProtobufPayload:
message = TestAllTypes.Parser.ParseFrom(request.ProtobufPayload);
- }
- catch (InvalidProtocolBufferException e)
- {
- return new ConformanceResponse { ParseError = e.Message };
- }
- break;
- default:
- throw new Exception("Unsupported request payload: " + request.PayloadCase);
+ break;
+ default:
+ throw new Exception("Unsupported request payload: " + request.PayloadCase);
+ }
}
- switch (request.RequestedOutputFormat)
+ catch (InvalidProtocolBufferException e)
{
- case global::Conformance.WireFormat.JSON:
- return new ConformanceResponse { JsonPayload = JsonFormatter.Default.Format(message) };
- case global::Conformance.WireFormat.PROTOBUF:
- return new ConformanceResponse { ProtobufPayload = message.ToByteString() };
- default:
- throw new Exception("Unsupported request output format: " + request.PayloadCase);
+ return new ConformanceResponse { ParseError = e.Message };
+ }
+ catch (InvalidJsonException e)
+ {
+ return new ConformanceResponse { ParseError = e.Message };
+ }
+ try
+ {
+ switch (request.RequestedOutputFormat)
+ {
+ case global::Conformance.WireFormat.JSON:
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, typeRegistry));
+ return new ConformanceResponse { JsonPayload = formatter.Format(message) };
+ case global::Conformance.WireFormat.PROTOBUF:
+ return new ConformanceResponse { ProtobufPayload = message.ToByteString() };
+ default:
+ throw new Exception("Unsupported request output format: " + request.PayloadCase);
+ }
+ }
+ catch (InvalidOperationException e)
+ {
+ return new ConformanceResponse { SerializeError = e.Message };
}
}
diff --git a/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs b/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs
index ba82c0e8..9c845907 100644
--- a/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs
@@ -56,7 +56,7 @@ namespace Google.Protobuf.Collections
}
[Test]
- public void NullValues()
+ public void NullValuesProhibited()
{
TestNullValues<int?>(0);
TestNullValues("");
@@ -65,19 +65,12 @@ namespace Google.Protobuf.Collections
private void TestNullValues<T>(T nonNullValue)
{
- var map = new MapField<int, T>(false);
+ var map = new MapField<int, T>();
var nullValue = (T) (object) null;
Assert.Throws<ArgumentNullException>(() => map.Add(0, nullValue));
Assert.Throws<ArgumentNullException>(() => map[0] = nullValue);
map.Add(1, nonNullValue);
map[1] = nonNullValue;
-
- // Doesn't throw...
- map = new MapField<int, T>(true);
- map.Add(0, nullValue);
- map[0] = nullValue;
- map.Add(1, nonNullValue);
- map[1] = nonNullValue;
}
[Test]
@@ -161,27 +154,6 @@ namespace Google.Protobuf.Collections
}
[Test]
- public void EqualityHandlesNullValues()
- {
- var map1 = new MapField<string, ForeignMessage>();
- map1.Add("a", new ForeignMessage { C = 10 });
- map1.Add("b", null);
-
- var map2 = new MapField<string, ForeignMessage>();
- map2.Add("a", new ForeignMessage { C = 10 });
- map2.Add("b", null);
-
- EqualityTester.AssertEquality(map1, map2);
- // Check the null value isn't ignored entirely...
- Assert.IsTrue(map1.Remove("b"));
- EqualityTester.AssertInequality(map1, map2);
- map1.Add("b", new ForeignMessage());
- EqualityTester.AssertInequality(map1, map2);
- map1["b"] = null;
- EqualityTester.AssertEquality(map1, map2);
- }
-
- [Test]
public void Add_Dictionary()
{
var map1 = new MapField<string, string>
@@ -454,30 +426,6 @@ namespace Google.Protobuf.Collections
}
[Test]
- public void AllowNullValues_Property()
- {
- // Non-message reference type values are non-nullable by default, but can be overridden
- Assert.IsFalse(new MapField<int, string>().AllowsNullValues);
- Assert.IsFalse(new MapField<int, string>(false).AllowsNullValues);
- Assert.IsTrue(new MapField<int, string>(true).AllowsNullValues);
-
- // Non-nullable value type values are never nullable
- Assert.IsFalse(new MapField<int, int>().AllowsNullValues);
- Assert.IsFalse(new MapField<int, int>(false).AllowsNullValues);
- Assert.Throws<ArgumentException>(() => new MapField<int, int>(true));
-
- // Message type values are nullable by default, but can be overridden
- Assert.IsTrue(new MapField<int, TestAllTypes>().AllowsNullValues);
- Assert.IsFalse(new MapField<int, TestAllTypes>(false).AllowsNullValues);
- Assert.IsTrue(new MapField<int, TestAllTypes>(true).AllowsNullValues);
-
- // Nullable value type values are nullable by default, but can be overridden
- Assert.IsTrue(new MapField<int, int?>().AllowsNullValues);
- Assert.IsFalse(new MapField<int, int?>(false).AllowsNullValues);
- Assert.IsTrue(new MapField<int, int?>(true).AllowsNullValues);
- }
-
- [Test]
public void KeysReturnsLiveView()
{
var map = new MapField<string, string>();
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 1163f524..14cc6d19 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -221,7 +221,7 @@ namespace Google.Protobuf
},
MapInt32ForeignMessage = {
{ 0, new ForeignMessage { C = 10 } },
- { 5, null },
+ { 5, new ForeignMessage() },
},
MapInt32Enum = {
{ 1, MapEnum.MAP_ENUM_BAR },
@@ -269,6 +269,40 @@ namespace Google.Protobuf
}
[Test]
+ public void MapWithOnlyKey_PrimitiveValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a key.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
+ int key = 10;
+ output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+ }
+
+ [Test]
+ public void MapWithOnlyKey_MessageValue()
+ {
+ // Hand-craft the stream to contain a single entry with just a key.
+ var memoryStream = new MemoryStream();
+ var output = new CodedOutputStream(memoryStream);
+ output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
+ int key = 10;
+ output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
+ output.WriteTag(1, WireFormat.WireType.Varint);
+ output.WriteInt32(key);
+ output.Flush();
+
+ var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
+ Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+ }
+
+ [Test]
public void MapIgnoresExtraFieldsWithinEntryMessages()
{
// Hand-craft the stream to contain a single entry with three fields
@@ -661,5 +695,21 @@ namespace Google.Protobuf
var parsed = TestAllTypes.Parser.ParseFrom(stream);
Assert.AreEqual(new TestAllTypes { SingleFixed32 = 123 }, parsed);
}
+
+ [Test]
+ public void CustomDiagnosticMessage_DirectToStringCall()
+ {
+ var message = new ForeignMessage { C = 31 };
+ Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
+ Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
+ }
+
+ [Test]
+ public void CustomDiagnosticMessage_Nested()
+ {
+ var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
+ Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
+ Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
+ }
}
}
diff --git a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
index cd15b968..4f37c5e2 100644
--- a/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
+++ b/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
@@ -55,7 +55,7 @@
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<Prefer32Bit>false</Prefer32Bit>
<SignAssembly>True</SignAssembly>
- <AssemblyOriginatorKeyFile>C:\keys\Google.Protobuf.snk</AssemblyOriginatorKeyFile>
+ <AssemblyOriginatorKeyFile>..\..\keys\Google.Protobuf.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
@@ -99,8 +99,10 @@
<Compile Include="JsonTokenizerTest.cs" />
<Compile Include="Reflection\DescriptorsTest.cs" />
<Compile Include="Reflection\FieldAccessTest.cs" />
+ <Compile Include="Reflection\TypeRegistryTest.cs" />
<Compile Include="SampleEnum.cs" />
<Compile Include="SampleMessages.cs" />
+ <Compile Include="TestProtos\ForeignMessagePartial.cs" />
<Compile Include="TestProtos\MapUnittestProto3.cs" />
<Compile Include="TestProtos\UnittestImportProto3.cs" />
<Compile Include="TestProtos\UnittestImportPublicProto3.cs" />
@@ -113,6 +115,7 @@
<Compile Include="TestProtos\UnittestWellKnownTypes.cs" />
<Compile Include="WellKnownTypes\AnyTest.cs" />
<Compile Include="WellKnownTypes\DurationTest.cs" />
+ <Compile Include="WellKnownTypes\FieldMaskTest.cs" />
<Compile Include="WellKnownTypes\TimestampTest.cs" />
<Compile Include="WellKnownTypes\WrappersTest.cs" />
</ItemGroup>
diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
index 08cedad8..42455043 100644
--- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
@@ -35,6 +35,9 @@ using Google.Protobuf.TestProtos;
using NUnit.Framework;
using UnitTest.Issues.TestProtos;
using Google.Protobuf.WellKnownTypes;
+using Google.Protobuf.Reflection;
+
+using static Google.Protobuf.JsonParserTest; // For WrapInQuotes
namespace Google.Protobuf
{
@@ -162,41 +165,31 @@ namespace Google.Protobuf
}
[Test]
- public void UnknownEnumValueOmitted_SingleField()
+ public void UnknownEnumValueNumeric_SingleField()
{
var message = new TestAllTypes { SingleForeignEnum = (ForeignEnum) 100 };
- AssertJson("{ }", JsonFormatter.Default.Format(message));
+ AssertJson("{ 'singleForeignEnum': 100 }", JsonFormatter.Default.Format(message));
}
[Test]
- public void UnknownEnumValueOmitted_RepeatedField()
+ public void UnknownEnumValueNumeric_RepeatedField()
{
var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.FOREIGN_BAZ, (ForeignEnum) 100, ForeignEnum.FOREIGN_FOO } };
- AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));
+ AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 100, 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));
}
[Test]
- public void UnknownEnumValueOmitted_MapField()
+ public void UnknownEnumValueNumeric_MapField()
{
- // This matches the C++ behaviour.
var message = new TestMap { MapInt32Enum = { { 1, MapEnum.MAP_ENUM_FOO }, { 2, (MapEnum) 100 }, { 3, MapEnum.MAP_ENUM_BAR } } };
- AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));
+ AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '2': 100, '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));
}
[Test]
- public void UnknownEnumValueOmitted_RepeatedField_AllEntriesUnknown()
+ public void UnknownEnumValue_RepeatedField_AllEntriesUnknown()
{
- // *Maybe* we should hold off on writing the "[" until we find that we've got at least one value to write...
- // but this is what happens at the moment, and it doesn't seem too awful.
var message = new TestAllTypes { RepeatedForeignEnum = { (ForeignEnum) 200, (ForeignEnum) 100 } };
- AssertJson("{ 'repeatedForeignEnum': [ ] }", JsonFormatter.Default.Format(message));
- }
-
- [Test]
- public void NullValueForMessage()
- {
- var message = new TestMap { MapInt32ForeignMessage = { { 10, null } } };
- AssertJson("{ 'mapInt32ForeignMessage': { '10': null } }", JsonFormatter.Default.Format(message));
+ AssertJson("{ 'repeatedForeignEnum': [ 200, 100 ] }", JsonFormatter.Default.Format(message));
}
[Test]
@@ -318,27 +311,52 @@ namespace Google.Protobuf
}
[Test]
- public void TimestampStandalone()
- {
- Assert.AreEqual("1970-01-01T00:00:00Z", new Timestamp().ToString());
- Assert.AreEqual("1970-01-01T00:00:00.100Z", new Timestamp { Nanos = 100000000 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.120Z", new Timestamp { Nanos = 120000000 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123Z", new Timestamp { Nanos = 123000000 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123400Z", new Timestamp { Nanos = 123400000 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123450Z", new Timestamp { Nanos = 123450000 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123456Z", new Timestamp { Nanos = 123456000 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123456700Z", new Timestamp { Nanos = 123456700 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123456780Z", new Timestamp { Nanos = 123456780 }.ToString());
- Assert.AreEqual("1970-01-01T00:00:00.123456789Z", new Timestamp { Nanos = 123456789 }.ToString());
+ [TestCase("1970-01-01T00:00:00Z", 0)]
+ [TestCase("1970-01-01T00:00:00.000000001Z", 1)]
+ [TestCase("1970-01-01T00:00:00.000000010Z", 10)]
+ [TestCase("1970-01-01T00:00:00.000000100Z", 100)]
+ [TestCase("1970-01-01T00:00:00.000001Z", 1000)]
+ [TestCase("1970-01-01T00:00:00.000010Z", 10000)]
+ [TestCase("1970-01-01T00:00:00.000100Z", 100000)]
+ [TestCase("1970-01-01T00:00:00.001Z", 1000000)]
+ [TestCase("1970-01-01T00:00:00.010Z", 10000000)]
+ [TestCase("1970-01-01T00:00:00.100Z", 100000000)]
+ [TestCase("1970-01-01T00:00:00.100Z", 100000000)]
+ [TestCase("1970-01-01T00:00:00.120Z", 120000000)]
+ [TestCase("1970-01-01T00:00:00.123Z", 123000000)]
+ [TestCase("1970-01-01T00:00:00.123400Z", 123400000)]
+ [TestCase("1970-01-01T00:00:00.123450Z", 123450000)]
+ [TestCase("1970-01-01T00:00:00.123456Z", 123456000)]
+ [TestCase("1970-01-01T00:00:00.123456700Z", 123456700)]
+ [TestCase("1970-01-01T00:00:00.123456780Z", 123456780)]
+ [TestCase("1970-01-01T00:00:00.123456789Z", 123456789)]
+ public void TimestampStandalone(string expected, int nanos)
+ {
+ Assert.AreEqual(WrapInQuotes(expected), new Timestamp { Nanos = nanos }.ToString());
+ }
- // One before and one after the Unix epoch
- Assert.AreEqual("1673-06-19T12:34:56Z",
+ [Test]
+ public void TimestampStandalone_FromDateTime()
+ {
+ // One before and one after the Unix epoch, more easily represented via DateTime.
+ Assert.AreEqual("\"1673-06-19T12:34:56Z\"",
new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp().ToString());
- Assert.AreEqual("2015-07-31T10:29:34Z",
+ Assert.AreEqual("\"2015-07-31T10:29:34Z\"",
new DateTime(2015, 7, 31, 10, 29, 34, DateTimeKind.Utc).ToTimestamp().ToString());
}
[Test]
+ [TestCase(-1, -1)] // Would be valid as duration
+ [TestCase(1, Timestamp.MaxNanos + 1)]
+ [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]
+ [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, 0)]
+ public void TimestampStandalone_NonNormalized(long seconds, int nanoseconds)
+ {
+ var timestamp = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
+ Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(timestamp));
+ }
+
+ [Test]
public void TimestampField()
{
var message = new TestWellKnownTypes { TimestampField = new Timestamp() };
@@ -349,6 +367,14 @@ namespace Google.Protobuf
[TestCase(0, 0, "0s")]
[TestCase(1, 0, "1s")]
[TestCase(-1, 0, "-1s")]
+ [TestCase(0, 1, "0.000000001s")]
+ [TestCase(0, 10, "0.000000010s")]
+ [TestCase(0, 100, "0.000000100s")]
+ [TestCase(0, 1000, "0.000001s")]
+ [TestCase(0, 10000, "0.000010s")]
+ [TestCase(0, 100000, "0.000100s")]
+ [TestCase(0, 1000000, "0.001s")]
+ [TestCase(0, 10000000, "0.010s")]
[TestCase(0, 100000000, "0.100s")]
[TestCase(0, 120000000, "0.120s")]
[TestCase(0, 123000000, "0.123s")]
@@ -361,12 +387,19 @@ namespace Google.Protobuf
[TestCase(0, -100000000, "-0.100s")]
[TestCase(1, 100000000, "1.100s")]
[TestCase(-1, -100000000, "-1.100s")]
- // Non-normalized examples
- [TestCase(1, 2123456789, "3.123456789s")]
- [TestCase(1, -100000000, "0.900s")]
public void DurationStandalone(long seconds, int nanoseconds, string expected)
{
- Assert.AreEqual(expected, new Duration { Seconds = seconds, Nanos = nanoseconds }.ToString());
+ var json = JsonFormatter.Default.Format(new Duration { Seconds = seconds, Nanos = nanoseconds });
+ Assert.AreEqual(WrapInQuotes(expected), json);
+ }
+
+ [Test]
+ [TestCase(1, 2123456789)]
+ [TestCase(1, -100000000)]
+ public void DurationStandalone_NonNormalized(long seconds, int nanoseconds)
+ {
+ var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
+ Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(duration));
}
[Test]
@@ -395,14 +428,24 @@ namespace Google.Protobuf
}
[Test]
+ [TestCase("foo__bar")]
+ [TestCase("foo_3_ar")]
+ [TestCase("fooBar")]
+ public void FieldMaskInvalid(string input)
+ {
+ var mask = new FieldMask { Paths = { input } };
+ Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(mask));
+ }
+
+ [Test]
public void FieldMaskStandalone()
{
var fieldMask = new FieldMask { Paths = { "", "single", "with_underscore", "nested.field.name", "nested..double_dot" } };
- Assert.AreEqual(",single,withUnderscore,nested.field.name,nested..doubleDot", fieldMask.ToString());
+ Assert.AreEqual("\",single,withUnderscore,nested.field.name,nested..doubleDot\"", fieldMask.ToString());
// Invalid, but we shouldn't create broken JSON...
fieldMask = new FieldMask { Paths = { "x\\y" } };
- Assert.AreEqual(@"x\\y", fieldMask.ToString());
+ Assert.AreEqual(@"""x\\y""", fieldMask.ToString());
}
[Test]
@@ -420,6 +463,47 @@ namespace Google.Protobuf
AssertJson("{ 'fileName': 'foo.proto' }", JsonFormatter.Default.Format(message));
}
+ [Test]
+ public void AnyWellKnownType()
+ {
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(Timestamp.Descriptor)));
+ var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
+ var any = Any.Pack(timestamp);
+ AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));
+ }
+
+ [Test]
+ public void AnyMessageType()
+ {
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+ var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
+ var any = Any.Pack(message);
+ AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));
+ }
+
+ [Test]
+ public void AnyNested()
+ {
+ var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
+
+ // Nest an Any as the value of an Any.
+ var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
+ var nestedMessage = Any.Pack(doubleNestedMessage);
+ var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
+ AssertJson("{ 'anyField': { '@type': 'type.googleapis.com/google.protobuf.Any', 'value': { '@type': 'type.googleapis.com/protobuf_unittest.TestAllTypes', 'singleInt32': 20 } } }",
+ formatter.Format(message));
+ }
+
+ [Test]
+ public void AnyUnknownType()
+ {
+ // The default type registry doesn't have any types in it.
+ var message = new TestAllTypes();
+ var any = Any.Pack(message);
+ Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(any));
+ }
+
/// <summary>
/// Checks that the actual JSON is the same as the expected JSON - but after replacing
/// all apostrophes in the expected JSON with double quotes. This basically makes the tests easier
diff --git a/csharp/src/Google.Protobuf.Test/JsonParserTest.cs b/csharp/src/Google.Protobuf.Test/JsonParserTest.cs
index b3664770..d21da58a 100644
--- a/csharp/src/Google.Protobuf.Test/JsonParserTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonParserTest.cs
@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
+using Google.Protobuf.Reflection;
using Google.Protobuf.TestProtos;
using Google.Protobuf.WellKnownTypes;
using NUnit.Framework;
@@ -38,9 +39,7 @@ using System;
namespace Google.Protobuf
{
/// <summary>
- /// Unit tests for JSON parsing. Some tests are ignored at the moment as the desired behaviour
- /// isn't fully known, either in terms of which exceptions should be thrown or whether they should
- /// count as valid values.
+ /// Unit tests for JSON parsing.
/// </summary>
public class JsonParserTest
{
@@ -73,6 +72,14 @@ namespace Google.Protobuf
}
[Test]
+ public void OriginalFieldNameAccepted()
+ {
+ var json = "{ \"single_int32\": 10 }";
+ var expected = new TestAllTypes { SingleInt32 = 10 };
+ Assert.AreEqual(expected, TestAllTypes.Parser.ParseJson(json));
+ }
+
+ [Test]
public void SourceContextRoundtrip()
{
AssertRoundtrip(new SourceContext { FileName = "foo.proto" });
@@ -117,7 +124,9 @@ namespace Google.Protobuf
[Test]
public void SingularWrappers_ExplicitNulls()
{
- var message = new TestWellKnownTypes();
+ // When we parse the "valueField": null part, we remember it... basically, it's one case
+ // where explicit default values don't fully roundtrip.
+ var message = new TestWellKnownTypes { ValueField = Value.ForNull() };
var json = new JsonFormatter(new JsonFormatter.Settings(true)).Format(message);
var parsed = JsonParser.Default.Parse<TestWellKnownTypes>(json);
Assert.AreEqual(message, parsed);
@@ -144,11 +153,19 @@ namespace Google.Protobuf
}
[Test]
+ public void ExplicitNullValue()
+ {
+ string json = "{\"valueField\": null}";
+ var message = JsonParser.Default.Parse<TestWellKnownTypes>(json);
+ Assert.AreEqual(new TestWellKnownTypes { ValueField = Value.ForNull() }, message);
+ }
+
+ [Test]
public void BytesWrapper_Standalone()
{
ByteString data = ByteString.CopyFrom(1, 2, 3);
// Can't do this with attributes...
- var parsed = JsonParser.Default.Parse<BytesValue>("\"" + data.ToBase64() + "\"");
+ var parsed = JsonParser.Default.Parse<BytesValue>(WrapInQuotes(data.ToBase64()));
var expected = new BytesValue { Value = data };
Assert.AreEqual(expected, parsed);
}
@@ -172,6 +189,36 @@ namespace Google.Protobuf
}
[Test]
+ public void RepeatedField_NullElementProhibited()
+ {
+ string json = "{ \"repeated_foreign_message\": [null] }";
+ Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
+ }
+
+ [Test]
+ public void RepeatedField_NullOverallValueAllowed()
+ {
+ string json = "{ \"repeated_foreign_message\": null }";
+ Assert.AreEqual(new TestAllTypes(), TestAllTypes.Parser.ParseJson(json));
+ }
+
+ [Test]
+ [TestCase("{ \"mapInt32Int32\": { \"10\": null }")]
+ [TestCase("{ \"mapStringString\": { \"abc\": null }")]
+ [TestCase("{ \"mapInt32ForeignMessage\": { \"10\": null }")]
+ public void MapField_NullValueProhibited(string json)
+ {
+ Assert.Throws<InvalidProtocolBufferException>(() => TestMap.Parser.ParseJson(json));
+ }
+
+ [Test]
+ public void MapField_NullOverallValueAllowed()
+ {
+ string json = "{ \"mapInt32Int32\": null }";
+ Assert.AreEqual(new TestMap(), TestMap.Parser.ParseJson(json));
+ }
+
+ [Test]
public void IndividualWrapperTypes()
{
Assert.AreEqual(new StringValue { Value = "foo" }, StringValue.Parser.ParseJson("\"foo\""));
@@ -183,7 +230,7 @@ namespace Google.Protobuf
private static void AssertRoundtrip<T>(T message) where T : IMessage<T>, new()
{
var clone = message.Clone();
- var json = message.ToString();
+ var json = JsonFormatter.Default.Format(message);
var parsed = JsonParser.Default.Parse<T>(json);
Assert.AreEqual(clone, parsed);
}
@@ -204,6 +251,8 @@ namespace Google.Protobuf
[Test]
[TestCase("+0")]
+ [TestCase(" 1")]
+ [TestCase("1 ")]
[TestCase("00")]
[TestCase("-00")]
[TestCase("--1")]
@@ -317,7 +366,18 @@ namespace Google.Protobuf
[TestCase("1.0.0")]
[TestCase("+1")]
[TestCase("00")]
+ [TestCase("01")]
+ [TestCase("-00")]
+ [TestCase("-01")]
[TestCase("--1")]
+ [TestCase(" Infinity")]
+ [TestCase(" -Infinity")]
+ [TestCase("NaN ")]
+ [TestCase("Infinity ")]
+ [TestCase("-Infinity ")]
+ [TestCase(" NaN")]
+ [TestCase("INFINITY")]
+ [TestCase("nan")]
[TestCase("\u00BD")] // 1/2 as a single Unicode character. Just sanity checking...
public void StringToDouble_Invalid(string jsonValue)
{
@@ -362,6 +422,10 @@ namespace Google.Protobuf
[TestCase("-1", -1)]
[TestCase("2147483647", 2147483647)]
[TestCase("-2147483648", -2147483648)]
+ [TestCase("1e1", 10)]
+ [TestCase("-1e1", -10)]
+ [TestCase("10.00", 10)]
+ [TestCase("-10.00", -10)]
public void NumberToInt32_Valid(string jsonValue, int expectedParsedValue)
{
string json = "{ \"singleInt32\": " + jsonValue + "}";
@@ -375,7 +439,8 @@ namespace Google.Protobuf
[TestCase("-00", typeof(InvalidJsonException))]
[TestCase("--1", typeof(InvalidJsonException))]
[TestCase("+1", typeof(InvalidJsonException))]
- [TestCase("1.5", typeof(InvalidProtocolBufferException), Ignore = true, Reason = "Desired behaviour unclear")]
+ [TestCase("1.5", typeof(InvalidProtocolBufferException))]
+ // Value is out of range
[TestCase("1e10", typeof(InvalidProtocolBufferException))]
[TestCase("2147483648", typeof(InvalidProtocolBufferException))]
[TestCase("-2147483649", typeof(InvalidProtocolBufferException))]
@@ -410,8 +475,10 @@ namespace Google.Protobuf
[TestCase("0", 0L)]
[TestCase("1", 1L)]
[TestCase("-1", -1L)]
- [TestCase("9223372036854775807", 9223372036854775807, Ignore = true, Reason = "Desired behaviour unclear")]
- [TestCase("-9223372036854775808", -9223372036854775808, Ignore = true, Reason = "Desired behaviour unclear")]
+ // long.MaxValue isn't actually representable as a double. This string value is the highest
+ // representable value which isn't greater than long.MaxValue.
+ [TestCase("9223372036854774784", 9223372036854774784)]
+ [TestCase("-9223372036854775808", -9223372036854775808)]
public void NumberToInt64_Valid(string jsonValue, long expectedParsedValue)
{
string json = "{ \"singleInt64\": " + jsonValue + "}";
@@ -421,8 +488,11 @@ namespace Google.Protobuf
// Assume that anything non-bounds-related is covered in the Int32 case
[Test]
- [TestCase("-9223372036854775809", Ignore = true, Reason = "Desired behaviour unclear")]
- [TestCase("9223372036854775808", Ignore = true, Reason = "Desired behaviour unclear")]
+ [TestCase("9223372036854775808")]
+ // Theoretical bound would be -9223372036854775809, but when that is parsed to a double
+ // we end up with the exact value of long.MinValue due to lack of precision. The value here
+ // is the "next double down".
+ [TestCase("-9223372036854780000")]
public void NumberToInt64_Invalid(string jsonValue)
{
string json = "{ \"singleInt64\": " + jsonValue + "}";
@@ -432,7 +502,9 @@ namespace Google.Protobuf
[Test]
[TestCase("0", 0UL)]
[TestCase("1", 1UL)]
- [TestCase("18446744073709551615", 18446744073709551615, Ignore = true, Reason = "Desired behaviour unclear")]
+ // ulong.MaxValue isn't representable as a double. This value is the largest double within
+ // the range of ulong.
+ [TestCase("18446744073709549568", 18446744073709549568UL)]
public void NumberToUInt64_Valid(string jsonValue, ulong expectedParsedValue)
{
string json = "{ \"singleUint64\": " + jsonValue + "}";
@@ -474,9 +546,9 @@ namespace Google.Protobuf
}
[Test]
- [TestCase("1.7977e308", Ignore = true, Reason = "Desired behaviour unclear")]
- [TestCase("-1.7977e308", Ignore = true, Reason = "Desired behaviour unclear")]
- [TestCase("1e309", Ignore = true, Reason = "Desired behaviour unclear")]
+ [TestCase("1.7977e308")]
+ [TestCase("-1.7977e308")]
+ [TestCase("1e309")]
[TestCase("1,0")]
[TestCase("1.0.0")]
[TestCase("+1")]
@@ -564,9 +636,9 @@ namespace Google.Protobuf
public void Timestamp_Valid(string jsonValue, string expectedFormatted)
{
expectedFormatted = expectedFormatted ?? jsonValue;
- string json = "\"" + jsonValue + "\"";
+ string json = WrapInQuotes(jsonValue);
var parsed = Timestamp.Parser.ParseJson(json);
- Assert.AreEqual(expectedFormatted, parsed.ToString());
+ Assert.AreEqual(WrapInQuotes(expectedFormatted), parsed.ToString());
}
[Test]
@@ -591,7 +663,7 @@ namespace Google.Protobuf
[TestCase("2100-02-29T14:46:23.123456789Z", Description = "Feb 29th on a non-leap-year")]
public void Timestamp_Invalid(string jsonValue)
{
- string json = "\"" + jsonValue + "\"";
+ string json = WrapInQuotes(jsonValue);
Assert.Throws<InvalidProtocolBufferException>(() => Timestamp.Parser.ParseJson(json));
}
@@ -665,9 +737,9 @@ namespace Google.Protobuf
public void Duration_Valid(string jsonValue, string expectedFormatted)
{
expectedFormatted = expectedFormatted ?? jsonValue;
- string json = "\"" + jsonValue + "\"";
+ string json = WrapInQuotes(jsonValue);
var parsed = Duration.Parser.ParseJson(json);
- Assert.AreEqual(expectedFormatted, parsed.ToString());
+ Assert.AreEqual(WrapInQuotes(expectedFormatted), parsed.ToString());
}
// The simplest way of testing that the value has parsed correctly is to reformat it,
@@ -691,12 +763,11 @@ namespace Google.Protobuf
[TestCase("--0.123456789s", Description = "Double minus sign")]
// Violate upper/lower bounds in various ways
[TestCase("315576000001s", Description = "Integer part too large")]
- [TestCase("315576000000.000000001s", Description = "Integer part is upper bound; non-zero fraction")]
[TestCase("3155760000000s", Description = "Integer part too long (positive)")]
[TestCase("-3155760000000s", Description = "Integer part too long (negative)")]
public void Duration_Invalid(string jsonValue)
{
- string json = "\"" + jsonValue + "\"";
+ string json = WrapInQuotes(jsonValue);
Assert.Throws<InvalidProtocolBufferException>(() => Duration.Parser.ParseJson(json));
}
@@ -712,12 +783,76 @@ namespace Google.Protobuf
[TestCase("fooBar.bazQux", "foo_bar.baz_qux")]
public void FieldMask_Valid(string jsonValue, params string[] expectedPaths)
{
- string json = "\"" + jsonValue + "\"";
+ string json = WrapInQuotes(jsonValue);
var parsed = FieldMask.Parser.ParseJson(json);
CollectionAssert.AreEqual(expectedPaths, parsed.Paths);
}
[Test]
+ [TestCase("foo_bar")]
+ public void FieldMask_Invalid(string jsonValue)
+ {
+ string json = WrapInQuotes(jsonValue);
+ Assert.Throws<InvalidProtocolBufferException>(() => FieldMask.Parser.ParseJson(json));
+ }
+
+ [Test]
+ public void Any_RegularMessage()
+ {
+ var registry = TypeRegistry.FromMessages(TestAllTypes.Descriptor);
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+ var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
+ var original = Any.Pack(message);
+ var json = formatter.Format(original); // This is tested in JsonFormatterTest
+ var parser = new JsonParser(new JsonParser.Settings(10, registry));
+ Assert.AreEqual(original, parser.Parse<Any>(json));
+ string valueFirstJson = "{ \"singleInt32\": 10, \"singleNestedMessage\": { \"bb\": 20 }, \"@type\": \"type.googleapis.com/protobuf_unittest.TestAllTypes\" }";
+ Assert.AreEqual(original, parser.Parse<Any>(valueFirstJson));
+ }
+
+ [Test]
+ public void Any_UnknownType()
+ {
+ string json = "{ \"@type\": \"type.googleapis.com/bogus\" }";
+ Assert.Throws<InvalidOperationException>(() => Any.Parser.ParseJson(json));
+ }
+
+ [Test]
+ public void Any_NoTypeUrl()
+ {
+ string json = "{ \"foo\": \"bar\" }";
+ Assert.Throws<InvalidProtocolBufferException>(() => Any.Parser.ParseJson(json));
+ }
+
+ [Test]
+ public void Any_WellKnownType()
+ {
+ var registry = TypeRegistry.FromMessages(Timestamp.Descriptor);
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
+ var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
+ var original = Any.Pack(timestamp);
+ var json = formatter.Format(original); // This is tested in JsonFormatterTest
+ var parser = new JsonParser(new JsonParser.Settings(10, registry));
+ Assert.AreEqual(original, parser.Parse<Any>(json));
+ string valueFirstJson = "{ \"value\": \"1673-06-19T12:34:56Z\", \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\" }";
+ Assert.AreEqual(original, parser.Parse<Any>(valueFirstJson));
+ }
+
+ [Test]
+ public void Any_Nested()
+ {
+ var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
+ var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
+ var parser = new JsonParser(new JsonParser.Settings(10, registry));
+ var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
+ var nestedMessage = Any.Pack(doubleNestedMessage);
+ var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
+ var json = formatter.Format(message);
+ // Use the descriptor-based parser just for a change.
+ Assert.AreEqual(message, parser.Parse(json, TestWellKnownTypes.Descriptor));
+ }
+
+ [Test]
public void DataAfterObject()
{
string json = "{} 10";
@@ -739,7 +874,52 @@ namespace Google.Protobuf
var parser63 = new JsonParser(new JsonParser.Settings(63));
Assert.Throws<InvalidProtocolBufferException>(() => parser63.Parse<TestRecursiveMessage>(data64));
+ }
+ [Test]
+ [TestCase("AQI")]
+ [TestCase("_-==")]
+ public void Bytes_InvalidBase64(string badBase64)
+ {
+ string json = "{ \"singleBytes\": \"" + badBase64 + "\" }";
+ Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
+ }
+
+ [Test]
+ [TestCase("\"FOREIGN_BAR\"", ForeignEnum.FOREIGN_BAR)]
+ [TestCase("5", ForeignEnum.FOREIGN_BAR)]
+ [TestCase("100", (ForeignEnum) 100)]
+ public void EnumValid(string value, ForeignEnum expectedValue)
+ {
+ string json = "{ \"singleForeignEnum\": " + value + " }";
+ var parsed = TestAllTypes.Parser.ParseJson(json);
+ Assert.AreEqual(new TestAllTypes { SingleForeignEnum = expectedValue }, parsed);
+ }
+
+ [Test]
+ [TestCase("\"NOT_A_VALID_VALUE\"")]
+ [TestCase("5.5")]
+ public void Enum_Invalid(string value)
+ {
+ string json = "{ \"singleForeignEnum\": " + value + " }";
+ Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
+ }
+
+ [Test]
+ public void OneofDuplicate_Invalid()
+ {
+ string json = "{ \"oneofString\": \"x\", \"oneofUint32\": 10 }";
+ Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
+ }
+
+ /// <summary>
+ /// Various tests use strings which have quotes round them for parsing or as the result
+ /// of formatting, but without those quotes being specified in the tests (for the sake of readability).
+ /// This method simply returns the input, wrapped in double quotes.
+ /// </summary>
+ internal static string WrapInQuotes(string text)
+ {
+ return '"' + text + '"';
}
}
}
diff --git a/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs b/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs
index a38efeed..a0a62227 100644
--- a/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs
@@ -85,7 +85,7 @@ namespace Google.Protobuf
public void ObjectDepth()
{
string json = "{ \"foo\": { \"x\": 1, \"y\": [ 0 ] } }";
- var tokenizer = new JsonTokenizer(new StringReader(json));
+ var tokenizer = JsonTokenizer.FromTextReader(new StringReader(json));
// If we had more tests like this, I'd introduce a helper method... but for one test, it's not worth it.
Assert.AreEqual(0, tokenizer.ObjectDepth);
Assert.AreEqual(JsonToken.StartObject, tokenizer.Next());
@@ -118,7 +118,7 @@ namespace Google.Protobuf
public void ObjectDepth_WithPushBack()
{
string json = "{}";
- var tokenizer = new JsonTokenizer(new StringReader(json));
+ var tokenizer = JsonTokenizer.FromTextReader(new StringReader(json));
Assert.AreEqual(0, tokenizer.ObjectDepth);
var token = tokenizer.Next();
Assert.AreEqual(1, tokenizer.ObjectDepth);
@@ -275,7 +275,7 @@ namespace Google.Protobuf
// Note: we don't test that the earlier tokens are exactly as expected,
// partly because that's hard to parameterize.
var reader = new StringReader(json.Replace('\'', '"'));
- var tokenizer = new JsonTokenizer(reader);
+ var tokenizer = JsonTokenizer.FromTextReader(reader);
for (int i = 0; i < expectedValidTokens; i++)
{
Assert.IsNotNull(tokenizer.Next());
@@ -334,7 +334,7 @@ namespace Google.Protobuf
[Test]
public void NextAfterEndDocumentThrows()
{
- var tokenizer = new JsonTokenizer(new StringReader("null"));
+ var tokenizer = JsonTokenizer.FromTextReader(new StringReader("null"));
Assert.AreEqual(JsonToken.Null, tokenizer.Next());
Assert.AreEqual(JsonToken.EndDocument, tokenizer.Next());
Assert.Throws<InvalidOperationException>(() => tokenizer.Next());
@@ -343,7 +343,7 @@ namespace Google.Protobuf
[Test]
public void CanPushBackEndDocument()
{
- var tokenizer = new JsonTokenizer(new StringReader("null"));
+ var tokenizer = JsonTokenizer.FromTextReader(new StringReader("null"));
Assert.AreEqual(JsonToken.Null, tokenizer.Next());
Assert.AreEqual(JsonToken.EndDocument, tokenizer.Next());
tokenizer.PushBack(JsonToken.EndDocument);
@@ -373,7 +373,7 @@ namespace Google.Protobuf
private static void AssertTokensNoReplacement(string json, params JsonToken[] expectedTokens)
{
var reader = new StringReader(json);
- var tokenizer = new JsonTokenizer(reader);
+ var tokenizer = JsonTokenizer.FromTextReader(reader);
for (int i = 0; i < expectedTokens.Length; i++)
{
var actualToken = tokenizer.Next();
@@ -393,7 +393,7 @@ namespace Google.Protobuf
private static void AssertThrowsAfter(string json, params JsonToken[] expectedTokens)
{
var reader = new StringReader(json);
- var tokenizer = new JsonTokenizer(reader);
+ var tokenizer = JsonTokenizer.FromTextReader(reader);
for (int i = 0; i < expectedTokens.Length; i++)
{
var actualToken = tokenizer.Next();
diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
index 51eff053..086a4e98 100644
--- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
+++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
@@ -63,7 +63,7 @@ namespace Google.Protobuf.Reflection
Assert.AreEqual(UnittestImportProto3Reflection.Descriptor, file.Dependencies[0]);
MessageDescriptor messageType = TestAllTypes.Descriptor;
- Assert.AreSame(typeof(TestAllTypes), messageType.GeneratedType);
+ Assert.AreSame(typeof(TestAllTypes), messageType.ClrType);
Assert.AreSame(TestAllTypes.Parser, messageType.Parser);
Assert.AreEqual(messageType, file.MessageTypes[0]);
Assert.AreEqual(messageType, file.FindTypeByName<MessageDescriptor>("TestAllTypes"));
@@ -227,18 +227,12 @@ namespace Google.Protobuf.Reflection
}
[Test]
- public void ConstructionWithoutGeneratedCodeInfo()
+ public void MapEntryMessageDescriptor()
{
- var data = UnittestIssuesReflection.Descriptor.Proto.ToByteArray();
- var newDescriptor = Google.Protobuf.Reflection.FileDescriptor.InternalBuildGeneratedFileFrom(data, new Reflection.FileDescriptor[] { }, null);
-
- // We should still be able to get at a field...
- var messageDescriptor = newDescriptor.FindTypeByName<MessageDescriptor>("ItemField");
- var fieldDescriptor = messageDescriptor.FindFieldByName("item");
- // But there shouldn't be an accessor (or a generated type for the message, or parser)
- Assert.IsNull(fieldDescriptor.Accessor);
- Assert.IsNull(messageDescriptor.GeneratedType);
- Assert.IsNull(messageDescriptor.Parser);
+ var descriptor = MapWellKnownTypes.Descriptor.NestedTypes[0];
+ Assert.IsNull(descriptor.Parser);
+ Assert.IsNull(descriptor.ClrType);
+ Assert.IsNull(descriptor.Fields[1].Accessor);
}
// From TestFieldOrdering:
diff --git a/csharp/src/Google.Protobuf.Test/Reflection/TypeRegistryTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/TypeRegistryTest.cs
new file mode 100644
index 00000000..5be7ca23
--- /dev/null
+++ b/csharp/src/Google.Protobuf.Test/Reflection/TypeRegistryTest.cs
@@ -0,0 +1,94 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.TestProtos;
+using Google.Protobuf.WellKnownTypes;
+using NUnit.Framework;
+
+namespace Google.Protobuf.Reflection
+{
+ public class TypeRegistryTest
+ {
+ // Most of our tests use messages. Simple test that we really can use files...
+ [Test]
+ public void CreateWithFileDescriptor()
+ {
+ var registry = TypeRegistry.FromFiles(DurationReflection.Descriptor, StructReflection.Descriptor);
+ AssertDescriptorPresent(registry, Duration.Descriptor);
+ AssertDescriptorPresent(registry, ListValue.Descriptor);
+ AssertDescriptorAbsent(registry, Timestamp.Descriptor);
+ }
+
+ [Test]
+ public void TypesFromSameFile()
+ {
+ // Just for kicks, let's start with a nested type
+ var registry = TypeRegistry.FromMessages(TestAllTypes.Types.NestedMessage.Descriptor);
+ // Top-level...
+ AssertDescriptorPresent(registry, TestFieldOrderings.Descriptor);
+ // ... and nested (not the same as the original NestedMessage!)
+ AssertDescriptorPresent(registry, TestFieldOrderings.Types.NestedMessage.Descriptor);
+ }
+
+ [Test]
+ public void DependenciesAreIncluded()
+ {
+ var registry = TypeRegistry.FromMessages(TestAllTypes.Descriptor);
+ // Direct dependencies
+ AssertDescriptorPresent(registry, ImportMessage.Descriptor);
+ // Public dependencies
+ AssertDescriptorPresent(registry, PublicImportMessage.Descriptor);
+ }
+
+ [Test]
+ public void DuplicateFiles()
+ {
+ // Duplicates via dependencies and simply via repetition
+ var registry = TypeRegistry.FromFiles(
+ UnittestProto3Reflection.Descriptor, UnittestImportProto3Reflection.Descriptor,
+ TimestampReflection.Descriptor, TimestampReflection.Descriptor);
+ AssertDescriptorPresent(registry, TestAllTypes.Descriptor);
+ AssertDescriptorPresent(registry, ImportMessage.Descriptor);
+ AssertDescriptorPresent(registry, Timestamp.Descriptor);
+ }
+
+ private static void AssertDescriptorPresent(TypeRegistry registry, MessageDescriptor descriptor)
+ {
+ Assert.AreSame(descriptor, registry.Find(descriptor.FullName));
+ }
+
+ private static void AssertDescriptorAbsent(TypeRegistry registry, MessageDescriptor descriptor)
+ {
+ Assert.IsNull(registry.Find(descriptor.FullName));
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/ForeignMessagePartial.cs b/csharp/src/Google.Protobuf.Test/TestProtos/ForeignMessagePartial.cs
new file mode 100644
index 00000000..5663a699
--- /dev/null
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/ForeignMessagePartial.cs
@@ -0,0 +1,45 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+namespace Google.Protobuf.TestProtos
+{
+ /// <summary>
+ /// A message with custom diagnostics (to test that they work).
+ /// </summary>
+ public partial class ForeignMessage : ICustomDiagnosticMessage
+ {
+ public string ToDiagnosticString()
+ {
+ return $"{{ \"c\": {C}, \"@cInHex\": \"{C:x}\" }}";
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
index 66b9d842..27c04787 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs
@@ -147,16 +147,16 @@ namespace Google.Protobuf.TestProtos {
"dmFsdWUYAiABKAU6AjgBKj8KB01hcEVudW0SEAoMTUFQX0VOVU1fRk9PEAAS",
"EAoMTUFQX0VOVU1fQkFSEAESEAoMTUFQX0VOVU1fQkFaEAJCIPgBAaoCGkdv",
"b2dsZS5Qcm90b2J1Zi5UZXN0UHJvdG9zYgZwcm90bzM="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestProto3Reflection.Descriptor, },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.MapEnum), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestMap), global::Google.Protobuf.TestProtos.TestMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapInt32Bytes", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, new pbr::GeneratedCodeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestMapSubmessage), global::Google.Protobuf.TestProtos.TestMapSubmessage.Parser, new[]{ "TestMap" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestMessageMap), global::Google.Protobuf.TestProtos.TestMessageMap.Parser, new[]{ "MapInt32Message" }, null, null, new pbr::GeneratedCodeInfo[] { null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestSameTypeMap), global::Google.Protobuf.TestProtos.TestSameTypeMap.Parser, new[]{ "Map1", "Map2" }, null, null, new pbr::GeneratedCodeInfo[] { null, null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestArenaMap), global::Google.Protobuf.TestProtos.TestArenaMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, new pbr::GeneratedCodeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType), global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Parser, new[]{ "Type" }, null, new[]{ typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Types.Type) }, new pbr::GeneratedCodeInfo[] { null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry), global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry.Parser, new[]{ "Entry" }, null, null, new pbr::GeneratedCodeInfo[] { null, })
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.MapEnum), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMap), global::Google.Protobuf.TestProtos.TestMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapStringString", "MapInt32Bytes", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMapSubmessage), global::Google.Protobuf.TestProtos.TestMapSubmessage.Parser, new[]{ "TestMap" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMessageMap), global::Google.Protobuf.TestProtos.TestMessageMap.Parser, new[]{ "MapInt32Message" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestSameTypeMap), global::Google.Protobuf.TestProtos.TestSameTypeMap.Parser, new[]{ "Map1", "Map2" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, }),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestArenaMap), global::Google.Protobuf.TestProtos.TestArenaMap.Parser, new[]{ "MapInt32Int32", "MapInt64Int64", "MapUint32Uint32", "MapUint64Uint64", "MapSint32Sint32", "MapSint64Sint64", "MapFixed32Fixed32", "MapFixed64Fixed64", "MapSfixed32Sfixed32", "MapSfixed64Sfixed64", "MapInt32Float", "MapInt32Double", "MapBoolBool", "MapInt32Enum", "MapInt32ForeignMessage" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, }),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType), global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Parser, new[]{ "Type" }, null, new[]{ typeof(global::Google.Protobuf.TestProtos.MessageContainingEnumCalledType.Types.Type) }, new pbr::GeneratedClrTypeInfo[] { null, }),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry), global::Google.Protobuf.TestProtos.MessageContainingMapCalledEntry.Parser, new[]{ "Entry" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, })
}));
}
#endregion
@@ -425,7 +425,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -633,7 +633,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -741,7 +741,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -851,7 +851,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1121,7 +1121,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1318,7 +1318,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1427,7 +1427,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
index 78a6bc14..7b824dc7 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs
@@ -31,10 +31,10 @@ namespace Google.Protobuf.TestProtos {
"UhAIEg4KCklNUE9SVF9CQVoQCUI8Chhjb20uZ29vZ2xlLnByb3RvYnVmLnRl",
"c3RIAfgBAaoCGkdvb2dsZS5Qcm90b2J1Zi5UZXN0UHJvdG9zUABiBnByb3Rv",
"Mw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestImportPublicProto3Reflection.Descriptor, },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ImportEnum), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.ImportMessage), global::Google.Protobuf.TestProtos.ImportMessage.Parser, new[]{ "D" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ImportEnum), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ImportMessage), global::Google.Protobuf.TestProtos.ImportMessage.Parser, new[]{ "D" }, null, null, null)
}));
}
#endregion
@@ -110,7 +110,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
index d69b37e4..b471a8cf 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportPublicProto3.cs
@@ -27,10 +27,10 @@ namespace Google.Protobuf.TestProtos {
"bzMucHJvdG8SGHByb3RvYnVmX3VuaXR0ZXN0X2ltcG9ydCIgChNQdWJsaWNJ",
"bXBvcnRNZXNzYWdlEgkKAWUYASABKAVCNwoYY29tLmdvb2dsZS5wcm90b2J1",
"Zi50ZXN0qgIaR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3NiBnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), global::Google.Protobuf.TestProtos.PublicImportMessage.Parser, new[]{ "E" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), global::Google.Protobuf.TestProtos.PublicImportMessage.Parser, new[]{ "E" }, null, null, null)
}));
}
#endregion
@@ -96,7 +96,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
index 5709b65c..16176a33 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs
@@ -46,16 +46,16 @@ namespace UnitTest.Issues.TestProtos {
"aXZlQmVsb3cQ+///////////ARIVCghNaW51c09uZRD///////////8BKi4K",
"DkRlcHJlY2F0ZWRFbnVtEhMKD0RFUFJFQ0FURURfWkVSTxAAEgcKA29uZRAB",
"Qh9IAaoCGlVuaXRUZXN0Lklzc3Vlcy5UZXN0UHJvdG9zYgZwcm90bzM="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307), global::UnitTest.Issues.TestProtos.Issue307.Parser, null, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Parser, null, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice.Parser, null, null, null, null)})}),
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), global::UnitTest.Issues.TestProtos.NegativeEnumMessage.Parser, new[]{ "Value", "Values", "PackedValues" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage.Parser, new[]{ "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null)}),
- new pbr::GeneratedCodeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null)
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307), global::UnitTest.Issues.TestProtos.Issue307.Parser, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Parser, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice.Parser, null, null, null, null)})}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), global::UnitTest.Issues.TestProtos.NegativeEnumMessage.Parser, new[]{ "Value", "Values", "PackedValues" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage.Parser, new[]{ "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null)
}));
}
#endregion
@@ -126,7 +126,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -204,7 +204,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -282,7 +282,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -405,7 +405,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -511,7 +511,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -668,7 +668,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -831,7 +831,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -947,7 +947,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1056,7 +1056,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1164,7 +1164,7 @@ namespace UnitTest.Issues.TestProtos {
public string O1String {
get { return o1Case_ == O1OneofCase.O1String ? (string) o1_ : ""; }
set {
- o1_ = pb::Preconditions.CheckNotNull(value, "value");
+ o1_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
o1Case_ = O1OneofCase.O1String;
}
}
@@ -1185,7 +1185,7 @@ namespace UnitTest.Issues.TestProtos {
public string PlainString {
get { return plainString_; }
set {
- plainString_ = pb::Preconditions.CheckNotNull(value, "value");
+ plainString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1204,7 +1204,7 @@ namespace UnitTest.Issues.TestProtos {
public string O2String {
get { return o2Case_ == O2OneofCase.O2String ? (string) o2_ : ""; }
set {
- o2_ = pb::Preconditions.CheckNotNull(value, "value");
+ o2_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
o2Case_ = O2OneofCase.O2String;
}
}
@@ -1279,7 +1279,7 @@ namespace UnitTest.Issues.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
index f027c4ee..d8465448 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs
@@ -150,42 +150,42 @@ namespace Google.Protobuf.TestProtos {
"HS5wcm90b2J1Zl91bml0dGVzdC5CYXJSZXF1ZXN0Gh4ucHJvdG9idWZfdW5p",
"dHRlc3QuQmFyUmVzcG9uc2VCOkINVW5pdHRlc3RQcm90b0gBgAEBiAEBkAEB",
"+AEBqgIaR29vZ2xlLlByb3RvYnVmLlRlc3RQcm90b3NiBnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.TestProtos.UnittestImportProto3Reflection.Descriptor, },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ForeignEnum), typeof(global::Google.Protobuf.TestProtos.TestEnumWithDupValue), typeof(global::Google.Protobuf.TestProtos.TestSparseEnum), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes), global::Google.Protobuf.TestProtos.TestAllTypes.Parser, new[]{ "SingleInt32", "SingleInt64", "SingleUint32", "SingleUint64", "SingleSint32", "SingleSint64", "SingleFixed32", "SingleFixed64", "SingleSfixed32", "SingleSfixed64", "SingleFloat", "SingleDouble", "SingleBool", "SingleString", "SingleBytes", "SingleNestedMessage", "SingleForeignMessage", "SingleImportMessage", "SingleNestedEnum", "SingleForeignEnum", "SingleImportEnum", "SinglePublicImportMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedImportMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedImportEnum", "RepeatedPublicImportMessage", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes" }, new[]{ "OneofField" }, new[]{ typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum) }, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage.Parser, new[]{ "Bb" }, null, null, null)}),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.NestedTestAllTypes), global::Google.Protobuf.TestProtos.NestedTestAllTypes.Parser, new[]{ "Child", "Payload", "RepeatedChild" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestDeprecatedFields), global::Google.Protobuf.TestProtos.TestDeprecatedFields.Parser, new[]{ "DeprecatedInt32" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.ForeignMessage), global::Google.Protobuf.TestProtos.ForeignMessage.Parser, new[]{ "C" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestReservedFields), global::Google.Protobuf.TestProtos.TestReservedFields.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestForeignNested), global::Google.Protobuf.TestProtos.TestForeignNested.Parser, new[]{ "ForeignNested" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber), global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber.Parser, new[]{ "A", "Bb" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestRecursiveMessage), global::Google.Protobuf.TestProtos.TestRecursiveMessage.Parser, new[]{ "A", "I" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionA), global::Google.Protobuf.TestProtos.TestMutualRecursionA.Parser, new[]{ "Bb" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionB), global::Google.Protobuf.TestProtos.TestMutualRecursionB.Parser, new[]{ "A", "OptionalInt32" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames), global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames.Parser, new[]{ "PrimitiveField", "StringField", "EnumField", "MessageField", "RepeatedPrimitiveField", "RepeatedStringField", "RepeatedEnumField", "RepeatedMessageField" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings), global::Google.Protobuf.TestProtos.TestFieldOrderings.Parser, new[]{ "MyString", "MyInt", "MyFloat", "SingleNestedMessage" }, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage.Parser, new[]{ "Oo", "Bb" }, null, null, null)}),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.SparseEnumMessage), global::Google.Protobuf.TestProtos.SparseEnumMessage.Parser, new[]{ "SparseEnum" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.OneString), global::Google.Protobuf.TestProtos.OneString.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MoreString), global::Google.Protobuf.TestProtos.MoreString.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.OneBytes), global::Google.Protobuf.TestProtos.OneBytes.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MoreBytes), global::Google.Protobuf.TestProtos.MoreBytes.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.Int32Message), global::Google.Protobuf.TestProtos.Int32Message.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.Uint32Message), global::Google.Protobuf.TestProtos.Uint32Message.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.Int64Message), global::Google.Protobuf.TestProtos.Int64Message.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.Uint64Message), global::Google.Protobuf.TestProtos.Uint64Message.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.BoolMessage), global::Google.Protobuf.TestProtos.BoolMessage.Parser, new[]{ "Data" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestOneof), global::Google.Protobuf.TestProtos.TestOneof.Parser, new[]{ "FooInt", "FooString", "FooMessage" }, new[]{ "Foo" }, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestPackedTypes), global::Google.Protobuf.TestProtos.TestPackedTypes.Parser, new[]{ "PackedInt32", "PackedInt64", "PackedUint32", "PackedUint64", "PackedSint32", "PackedSint64", "PackedFixed32", "PackedFixed64", "PackedSfixed32", "PackedSfixed64", "PackedFloat", "PackedDouble", "PackedBool", "PackedEnum" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestUnpackedTypes), global::Google.Protobuf.TestProtos.TestUnpackedTypes.Parser, new[]{ "UnpackedInt32", "UnpackedInt64", "UnpackedUint32", "UnpackedUint64", "UnpackedSint32", "UnpackedSint64", "UnpackedFixed32", "UnpackedFixed64", "UnpackedSfixed32", "UnpackedSfixed64", "UnpackedFloat", "UnpackedDouble", "UnpackedBool", "UnpackedEnum" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes), global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes.Parser, new[]{ "RepeatedFixed32", "RepeatedInt32", "RepeatedFixed64", "RepeatedInt64", "RepeatedFloat", "RepeatedUint64" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestCommentInjectionMessage), global::Google.Protobuf.TestProtos.TestCommentInjectionMessage.Parser, new[]{ "A" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.FooRequest), global::Google.Protobuf.TestProtos.FooRequest.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.FooResponse), global::Google.Protobuf.TestProtos.FooResponse.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.FooClientMessage), global::Google.Protobuf.TestProtos.FooClientMessage.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.FooServerMessage), global::Google.Protobuf.TestProtos.FooServerMessage.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.BarRequest), global::Google.Protobuf.TestProtos.BarRequest.Parser, null, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.BarResponse), global::Google.Protobuf.TestProtos.BarResponse.Parser, null, null, null, null)
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.TestProtos.ForeignEnum), typeof(global::Google.Protobuf.TestProtos.TestEnumWithDupValue), typeof(global::Google.Protobuf.TestProtos.TestSparseEnum), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes), global::Google.Protobuf.TestProtos.TestAllTypes.Parser, new[]{ "SingleInt32", "SingleInt64", "SingleUint32", "SingleUint64", "SingleSint32", "SingleSint64", "SingleFixed32", "SingleFixed64", "SingleSfixed32", "SingleSfixed64", "SingleFloat", "SingleDouble", "SingleBool", "SingleString", "SingleBytes", "SingleNestedMessage", "SingleForeignMessage", "SingleImportMessage", "SingleNestedEnum", "SingleForeignEnum", "SingleImportEnum", "SinglePublicImportMessage", "RepeatedInt32", "RepeatedInt64", "RepeatedUint32", "RepeatedUint64", "RepeatedSint32", "RepeatedSint64", "RepeatedFixed32", "RepeatedFixed64", "RepeatedSfixed32", "RepeatedSfixed64", "RepeatedFloat", "RepeatedDouble", "RepeatedBool", "RepeatedString", "RepeatedBytes", "RepeatedNestedMessage", "RepeatedForeignMessage", "RepeatedImportMessage", "RepeatedNestedEnum", "RepeatedForeignEnum", "RepeatedImportEnum", "RepeatedPublicImportMessage", "OneofUint32", "OneofNestedMessage", "OneofString", "OneofBytes" }, new[]{ "OneofField" }, new[]{ typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum) }, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedMessage.Parser, new[]{ "Bb" }, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.NestedTestAllTypes), global::Google.Protobuf.TestProtos.NestedTestAllTypes.Parser, new[]{ "Child", "Payload", "RepeatedChild" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestDeprecatedFields), global::Google.Protobuf.TestProtos.TestDeprecatedFields.Parser, new[]{ "DeprecatedInt32" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.ForeignMessage), global::Google.Protobuf.TestProtos.ForeignMessage.Parser, new[]{ "C" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestReservedFields), global::Google.Protobuf.TestProtos.TestReservedFields.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestForeignNested), global::Google.Protobuf.TestProtos.TestForeignNested.Parser, new[]{ "ForeignNested" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber), global::Google.Protobuf.TestProtos.TestReallyLargeTagNumber.Parser, new[]{ "A", "Bb" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestRecursiveMessage), global::Google.Protobuf.TestProtos.TestRecursiveMessage.Parser, new[]{ "A", "I" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionA), global::Google.Protobuf.TestProtos.TestMutualRecursionA.Parser, new[]{ "Bb" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestMutualRecursionB), global::Google.Protobuf.TestProtos.TestMutualRecursionB.Parser, new[]{ "A", "OptionalInt32" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames), global::Google.Protobuf.TestProtos.TestCamelCaseFieldNames.Parser, new[]{ "PrimitiveField", "StringField", "EnumField", "MessageField", "RepeatedPrimitiveField", "RepeatedStringField", "RepeatedEnumField", "RepeatedMessageField" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings), global::Google.Protobuf.TestProtos.TestFieldOrderings.Parser, new[]{ "MyString", "MyInt", "MyFloat", "SingleNestedMessage" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage), global::Google.Protobuf.TestProtos.TestFieldOrderings.Types.NestedMessage.Parser, new[]{ "Oo", "Bb" }, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.SparseEnumMessage), global::Google.Protobuf.TestProtos.SparseEnumMessage.Parser, new[]{ "SparseEnum" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneString), global::Google.Protobuf.TestProtos.OneString.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MoreString), global::Google.Protobuf.TestProtos.MoreString.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneBytes), global::Google.Protobuf.TestProtos.OneBytes.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MoreBytes), global::Google.Protobuf.TestProtos.MoreBytes.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Int32Message), global::Google.Protobuf.TestProtos.Int32Message.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Uint32Message), global::Google.Protobuf.TestProtos.Uint32Message.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Int64Message), global::Google.Protobuf.TestProtos.Int64Message.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.Uint64Message), global::Google.Protobuf.TestProtos.Uint64Message.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BoolMessage), global::Google.Protobuf.TestProtos.BoolMessage.Parser, new[]{ "Data" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestOneof), global::Google.Protobuf.TestProtos.TestOneof.Parser, new[]{ "FooInt", "FooString", "FooMessage" }, new[]{ "Foo" }, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestPackedTypes), global::Google.Protobuf.TestProtos.TestPackedTypes.Parser, new[]{ "PackedInt32", "PackedInt64", "PackedUint32", "PackedUint64", "PackedSint32", "PackedSint64", "PackedFixed32", "PackedFixed64", "PackedSfixed32", "PackedSfixed64", "PackedFloat", "PackedDouble", "PackedBool", "PackedEnum" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestUnpackedTypes), global::Google.Protobuf.TestProtos.TestUnpackedTypes.Parser, new[]{ "UnpackedInt32", "UnpackedInt64", "UnpackedUint32", "UnpackedUint64", "UnpackedSint32", "UnpackedSint64", "UnpackedFixed32", "UnpackedFixed64", "UnpackedSfixed32", "UnpackedSfixed64", "UnpackedFloat", "UnpackedDouble", "UnpackedBool", "UnpackedEnum" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes), global::Google.Protobuf.TestProtos.TestRepeatedScalarDifferentTagSizes.Parser, new[]{ "RepeatedFixed32", "RepeatedInt32", "RepeatedFixed64", "RepeatedInt64", "RepeatedFloat", "RepeatedUint64" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestCommentInjectionMessage), global::Google.Protobuf.TestProtos.TestCommentInjectionMessage.Parser, new[]{ "A" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooRequest), global::Google.Protobuf.TestProtos.FooRequest.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooResponse), global::Google.Protobuf.TestProtos.FooResponse.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooClientMessage), global::Google.Protobuf.TestProtos.FooClientMessage.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.FooServerMessage), global::Google.Protobuf.TestProtos.FooServerMessage.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BarRequest), global::Google.Protobuf.TestProtos.BarRequest.Parser, null, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.BarResponse), global::Google.Protobuf.TestProtos.BarResponse.Parser, null, null, null, null)
}));
}
#endregion
@@ -459,7 +459,7 @@ namespace Google.Protobuf.TestProtos {
public string SingleString {
get { return singleString_; }
set {
- singleString_ = pb::Preconditions.CheckNotNull(value, "value");
+ singleString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -469,7 +469,7 @@ namespace Google.Protobuf.TestProtos {
public pb::ByteString SingleBytes {
get { return singleBytes_; }
set {
- singleBytes_ = pb::Preconditions.CheckNotNull(value, "value");
+ singleBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -775,7 +775,7 @@ namespace Google.Protobuf.TestProtos {
public string OneofString {
get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) oneofField_ : ""; }
set {
- oneofField_ = pb::Preconditions.CheckNotNull(value, "value");
+ oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
oneofFieldCase_ = OneofFieldOneofCase.OneofString;
}
}
@@ -785,7 +785,7 @@ namespace Google.Protobuf.TestProtos {
public pb::ByteString OneofBytes {
get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
set {
- oneofField_ = pb::Preconditions.CheckNotNull(value, "value");
+ oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
oneofFieldCase_ = OneofFieldOneofCase.OneofBytes;
}
}
@@ -927,7 +927,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1600,7 +1600,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1736,7 +1736,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1873,7 +1873,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1980,7 +1980,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2070,7 +2070,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2162,7 +2162,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2291,7 +2291,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2421,7 +2421,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2547,7 +2547,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2669,7 +2669,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2787,7 +2787,7 @@ namespace Google.Protobuf.TestProtos {
public string StringField {
get { return stringField_; }
set {
- stringField_ = pb::Preconditions.CheckNotNull(value, "value");
+ stringField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2883,7 +2883,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3045,7 +3045,7 @@ namespace Google.Protobuf.TestProtos {
public string MyString {
get { return myString_; }
set {
- myString_ = pb::Preconditions.CheckNotNull(value, "value");
+ myString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -3107,7 +3107,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3278,7 +3278,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3400,7 +3400,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3480,7 +3480,7 @@ namespace Google.Protobuf.TestProtos {
public string Data {
get { return data_; }
set {
- data_ = pb::Preconditions.CheckNotNull(value, "value");
+ data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -3506,7 +3506,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3608,7 +3608,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3678,7 +3678,7 @@ namespace Google.Protobuf.TestProtos {
public pb::ByteString Data {
get { return data_; }
set {
- data_ = pb::Preconditions.CheckNotNull(value, "value");
+ data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -3704,7 +3704,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3781,7 +3781,7 @@ namespace Google.Protobuf.TestProtos {
public pb::ByteString Data {
get { return data_; }
set {
- data_ = pb::Preconditions.CheckNotNull(value, "value");
+ data_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -3807,7 +3807,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3913,7 +3913,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4016,7 +4016,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4119,7 +4119,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4222,7 +4222,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4325,7 +4325,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4425,7 +4425,7 @@ namespace Google.Protobuf.TestProtos {
public string FooString {
get { return fooCase_ == FooOneofCase.FooString ? (string) foo_ : ""; }
set {
- foo_ = pb::Preconditions.CheckNotNull(value, "value");
+ foo_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
fooCase_ = FooOneofCase.FooString;
}
}
@@ -4486,7 +4486,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4780,7 +4780,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5140,7 +5140,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5414,7 +5414,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5528,7 +5528,7 @@ namespace Google.Protobuf.TestProtos {
public string A {
get { return a_; }
set {
- a_ = pb::Preconditions.CheckNotNull(value, "value");
+ a_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -5554,7 +5554,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5647,7 +5647,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5723,7 +5723,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5799,7 +5799,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5875,7 +5875,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5951,7 +5951,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -6027,7 +6027,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
index 3cad3e7a..ae12f4a4 100644
--- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
+++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestWellKnownTypes.cs
@@ -31,7 +31,7 @@ namespace Google.Protobuf.TestProtos {
"L3Byb3RvYnVmL3NvdXJjZV9jb250ZXh0LnByb3RvGhxnb29nbGUvcHJvdG9i",
"dWYvc3RydWN0LnByb3RvGh9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnBy",
"b3RvGhpnb29nbGUvcHJvdG9idWYvdHlwZS5wcm90bxoeZ29vZ2xlL3Byb3Rv",
- "YnVmL3dyYXBwZXJzLnByb3RvIpEHChJUZXN0V2VsbEtub3duVHlwZXMSJwoJ",
+ "YnVmL3dyYXBwZXJzLnByb3RvIr4HChJUZXN0V2VsbEtub3duVHlwZXMSJwoJ",
"YW55X2ZpZWxkGAEgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRInCglhcGlf",
"ZmllbGQYAiABKAsyFC5nb29nbGUucHJvdG9idWYuQXBpEjEKDmR1cmF0aW9u",
"X2ZpZWxkGAMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uEisKC2Vt",
@@ -51,7 +51,8 @@ namespace Google.Protobuf.TestProtos {
"cm90b2J1Zi5VSW50MzJWYWx1ZRIuCgpib29sX2ZpZWxkGBAgASgLMhouZ29v",
"Z2xlLnByb3RvYnVmLkJvb2xWYWx1ZRIyCgxzdHJpbmdfZmllbGQYESABKAsy",
"HC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUSMAoLYnl0ZXNfZmllbGQY",
- "EiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZSKVBwoWUmVwZWF0",
+ "EiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZRIrCgt2YWx1ZV9m",
+ "aWVsZBgTIAEoCzIWLmdvb2dsZS5wcm90b2J1Zi5WYWx1ZSKVBwoWUmVwZWF0",
"ZWRXZWxsS25vd25UeXBlcxInCglhbnlfZmllbGQYASADKAsyFC5nb29nbGUu",
"cHJvdG9idWYuQW55EicKCWFwaV9maWVsZBgCIAMoCzIULmdvb2dsZS5wcm90",
"b2J1Zi5BcGkSMQoOZHVyYXRpb25fZmllbGQYAyADKAsyGS5nb29nbGUucHJv",
@@ -159,13 +160,13 @@ namespace Google.Protobuf.TestProtos {
"AiABKAsyGy5nb29nbGUucHJvdG9idWYuQnl0ZXNWYWx1ZToCOAFCOQoYY29t",
"Lmdvb2dsZS5wcm90b2J1Zi50ZXN0UAGqAhpHb29nbGUuUHJvdG9idWYuVGVz",
"dFByb3Rvc2IGcHJvdG8z"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.ApiReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.DurationReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.FieldMaskReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.TestWellKnownTypes), global::Google.Protobuf.TestProtos.TestWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes), global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.OneofWellKnownTypes), global::Google.Protobuf.TestProtos.OneofWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, new[]{ "OneofField" }, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.TestProtos.MapWellKnownTypes), global::Google.Protobuf.TestProtos.MapWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, new pbr::GeneratedCodeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, })
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.TestWellKnownTypes), global::Google.Protobuf.TestProtos.TestWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField", "ValueField" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes), global::Google.Protobuf.TestProtos.RepeatedWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.OneofWellKnownTypes), global::Google.Protobuf.TestProtos.OneofWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, new[]{ "OneofField" }, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.TestProtos.MapWellKnownTypes), global::Google.Protobuf.TestProtos.MapWellKnownTypes.Parser, new[]{ "AnyField", "ApiField", "DurationField", "EmptyField", "FieldMaskField", "SourceContextField", "StructField", "TimestampField", "TypeField", "DoubleField", "FloatField", "Int64Field", "Uint64Field", "Int32Field", "Uint32Field", "BoolField", "StringField", "BytesField" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, })
}));
}
#endregion
@@ -215,6 +216,7 @@ namespace Google.Protobuf.TestProtos {
BoolField = other.BoolField;
StringField = other.StringField;
BytesField = other.BytesField;
+ ValueField = other.valueField_ != null ? other.ValueField.Clone() : null;
}
public TestWellKnownTypes Clone() {
@@ -410,6 +412,19 @@ namespace Google.Protobuf.TestProtos {
}
}
+ /// <summary>Field number for the "value_field" field.</summary>
+ public const int ValueFieldFieldNumber = 19;
+ private global::Google.Protobuf.WellKnownTypes.Value valueField_;
+ /// <summary>
+ /// Part of struct, but useful to be able to test separately
+ /// </summary>
+ public global::Google.Protobuf.WellKnownTypes.Value ValueField {
+ get { return valueField_; }
+ set {
+ valueField_ = value;
+ }
+ }
+
public override bool Equals(object other) {
return Equals(other as TestWellKnownTypes);
}
@@ -439,6 +454,7 @@ namespace Google.Protobuf.TestProtos {
if (BoolField != other.BoolField) return false;
if (StringField != other.StringField) return false;
if (BytesField != other.BytesField) return false;
+ if (!object.Equals(ValueField, other.ValueField)) return false;
return true;
}
@@ -462,11 +478,12 @@ namespace Google.Protobuf.TestProtos {
if (boolField_ != null) hash ^= BoolField.GetHashCode();
if (stringField_ != null) hash ^= StringField.GetHashCode();
if (bytesField_ != null) hash ^= BytesField.GetHashCode();
+ if (valueField_ != null) hash ^= ValueField.GetHashCode();
return hash;
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -533,6 +550,10 @@ namespace Google.Protobuf.TestProtos {
if (bytesField_ != null) {
_single_bytesField_codec.WriteTagAndValue(output, BytesField);
}
+ if (valueField_ != null) {
+ output.WriteRawTag(154, 1);
+ output.WriteMessage(ValueField);
+ }
}
public int CalculateSize() {
@@ -591,6 +612,9 @@ namespace Google.Protobuf.TestProtos {
if (bytesField_ != null) {
size += _single_bytesField_codec.CalculateSizeWithTag(BytesField);
}
+ if (valueField_ != null) {
+ size += 2 + pb::CodedOutputStream.ComputeMessageSize(ValueField);
+ }
return size;
}
@@ -697,6 +721,12 @@ namespace Google.Protobuf.TestProtos {
BytesField = other.BytesField;
}
}
+ if (other.valueField_ != null) {
+ if (valueField_ == null) {
+ valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
+ }
+ ValueField.MergeFrom(other.ValueField);
+ }
}
public void MergeFrom(pb::CodedInputStream input) {
@@ -832,6 +862,13 @@ namespace Google.Protobuf.TestProtos {
}
break;
}
+ case 154: {
+ if (valueField_ == null) {
+ valueField_ = new global::Google.Protobuf.WellKnownTypes.Value();
+ }
+ input.ReadMessage(valueField_);
+ break;
+ }
}
}
}
@@ -1106,7 +1143,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1625,7 +1662,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2080,7 +2117,7 @@ namespace Google.Protobuf.TestProtos {
public const int DoubleFieldFieldNumber = 10;
private static readonly pbc::MapField<int, double?>.Codec _map_doubleField_codec
= new pbc::MapField<int, double?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<double>(18), 82);
- private readonly pbc::MapField<int, double?> doubleField_ = new pbc::MapField<int, double?>(true);
+ private readonly pbc::MapField<int, double?> doubleField_ = new pbc::MapField<int, double?>();
public pbc::MapField<int, double?> DoubleField {
get { return doubleField_; }
}
@@ -2089,7 +2126,7 @@ namespace Google.Protobuf.TestProtos {
public const int FloatFieldFieldNumber = 11;
private static readonly pbc::MapField<int, float?>.Codec _map_floatField_codec
= new pbc::MapField<int, float?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<float>(18), 90);
- private readonly pbc::MapField<int, float?> floatField_ = new pbc::MapField<int, float?>(true);
+ private readonly pbc::MapField<int, float?> floatField_ = new pbc::MapField<int, float?>();
public pbc::MapField<int, float?> FloatField {
get { return floatField_; }
}
@@ -2098,7 +2135,7 @@ namespace Google.Protobuf.TestProtos {
public const int Int64FieldFieldNumber = 12;
private static readonly pbc::MapField<int, long?>.Codec _map_int64Field_codec
= new pbc::MapField<int, long?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<long>(18), 98);
- private readonly pbc::MapField<int, long?> int64Field_ = new pbc::MapField<int, long?>(true);
+ private readonly pbc::MapField<int, long?> int64Field_ = new pbc::MapField<int, long?>();
public pbc::MapField<int, long?> Int64Field {
get { return int64Field_; }
}
@@ -2107,7 +2144,7 @@ namespace Google.Protobuf.TestProtos {
public const int Uint64FieldFieldNumber = 13;
private static readonly pbc::MapField<int, ulong?>.Codec _map_uint64Field_codec
= new pbc::MapField<int, ulong?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<ulong>(18), 106);
- private readonly pbc::MapField<int, ulong?> uint64Field_ = new pbc::MapField<int, ulong?>(true);
+ private readonly pbc::MapField<int, ulong?> uint64Field_ = new pbc::MapField<int, ulong?>();
public pbc::MapField<int, ulong?> Uint64Field {
get { return uint64Field_; }
}
@@ -2116,7 +2153,7 @@ namespace Google.Protobuf.TestProtos {
public const int Int32FieldFieldNumber = 14;
private static readonly pbc::MapField<int, int?>.Codec _map_int32Field_codec
= new pbc::MapField<int, int?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<int>(18), 114);
- private readonly pbc::MapField<int, int?> int32Field_ = new pbc::MapField<int, int?>(true);
+ private readonly pbc::MapField<int, int?> int32Field_ = new pbc::MapField<int, int?>();
public pbc::MapField<int, int?> Int32Field {
get { return int32Field_; }
}
@@ -2125,7 +2162,7 @@ namespace Google.Protobuf.TestProtos {
public const int Uint32FieldFieldNumber = 15;
private static readonly pbc::MapField<int, uint?>.Codec _map_uint32Field_codec
= new pbc::MapField<int, uint?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<uint>(18), 122);
- private readonly pbc::MapField<int, uint?> uint32Field_ = new pbc::MapField<int, uint?>(true);
+ private readonly pbc::MapField<int, uint?> uint32Field_ = new pbc::MapField<int, uint?>();
public pbc::MapField<int, uint?> Uint32Field {
get { return uint32Field_; }
}
@@ -2134,7 +2171,7 @@ namespace Google.Protobuf.TestProtos {
public const int BoolFieldFieldNumber = 16;
private static readonly pbc::MapField<int, bool?>.Codec _map_boolField_codec
= new pbc::MapField<int, bool?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<bool>(18), 130);
- private readonly pbc::MapField<int, bool?> boolField_ = new pbc::MapField<int, bool?>(true);
+ private readonly pbc::MapField<int, bool?> boolField_ = new pbc::MapField<int, bool?>();
public pbc::MapField<int, bool?> BoolField {
get { return boolField_; }
}
@@ -2143,7 +2180,7 @@ namespace Google.Protobuf.TestProtos {
public const int StringFieldFieldNumber = 17;
private static readonly pbc::MapField<int, string>.Codec _map_stringField_codec
= new pbc::MapField<int, string>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForClassWrapper<string>(18), 138);
- private readonly pbc::MapField<int, string> stringField_ = new pbc::MapField<int, string>(true);
+ private readonly pbc::MapField<int, string> stringField_ = new pbc::MapField<int, string>();
public pbc::MapField<int, string> StringField {
get { return stringField_; }
}
@@ -2152,7 +2189,7 @@ namespace Google.Protobuf.TestProtos {
public const int BytesFieldFieldNumber = 18;
private static readonly pbc::MapField<int, pb::ByteString>.Codec _map_bytesField_codec
= new pbc::MapField<int, pb::ByteString>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForClassWrapper<pb::ByteString>(18), 146);
- private readonly pbc::MapField<int, pb::ByteString> bytesField_ = new pbc::MapField<int, pb::ByteString>(true);
+ private readonly pbc::MapField<int, pb::ByteString> bytesField_ = new pbc::MapField<int, pb::ByteString>();
public pbc::MapField<int, pb::ByteString> BytesField {
get { return bytesField_; }
}
@@ -2213,7 +2250,7 @@ namespace Google.Protobuf.TestProtos {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs
index 0a2b8b32..f3593e5f 100644
--- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs
@@ -62,5 +62,28 @@ namespace Google.Protobuf.WellKnownTypes
var unpacked = any.Unpack<TestAllTypes>();
Assert.AreEqual(message, unpacked);
}
+
+ [Test]
+ public void ToString_WithValues()
+ {
+ var message = SampleMessages.CreateFullTestAllTypes();
+ var any = Any.Pack(message);
+ var text = any.ToString();
+ Assert.That(text, Is.StringContaining("\"@value\": \"" + message.ToByteString().ToBase64() + "\""));
+ }
+
+ [Test]
+ public void ToString_Empty()
+ {
+ var any = new Any();
+ Assert.AreEqual("{ \"@type\": \"\", \"@value\": \"\" }", any.ToString());
+ }
+
+ [Test]
+ public void ToString_MessageContainingAny()
+ {
+ var message = new TestWellKnownTypes { AnyField = new Any() };
+ Assert.AreEqual("{ \"anyField\": { \"@type\": \"\", \"@value\": \"\" } }", message.ToString());
+ }
}
}
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
index 36012e63..141faf80 100644
--- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
@@ -50,11 +50,6 @@ namespace Google.Protobuf.WellKnownTypes
// Rounding is towards 0
Assert.AreEqual(TimeSpan.FromTicks(2), new Duration { Nanos = 250 }.ToTimeSpan());
Assert.AreEqual(TimeSpan.FromTicks(-2), new Duration { Nanos = -250 }.ToTimeSpan());
-
- // Non-normalized durations
- Assert.AreEqual(TimeSpan.FromSeconds(3), new Duration { Seconds = 1, Nanos = 2 * Duration.NanosecondsPerSecond }.ToTimeSpan());
- Assert.AreEqual(TimeSpan.FromSeconds(1), new Duration { Seconds = 3, Nanos = -2 * Duration.NanosecondsPerSecond }.ToTimeSpan());
- Assert.AreEqual(TimeSpan.FromSeconds(-1), new Duration { Seconds = 1, Nanos = -2 * Duration.NanosecondsPerSecond }.ToTimeSpan());
}
[Test]
@@ -100,5 +95,38 @@ namespace Google.Protobuf.WellKnownTypes
Assert.AreEqual(new Duration { Seconds = 1 }, Duration.FromTimeSpan(TimeSpan.FromSeconds(1)));
Assert.AreEqual(new Duration { Nanos = Duration.NanosecondsPerTick }, Duration.FromTimeSpan(TimeSpan.FromTicks(1)));
}
+
+ [Test]
+ [TestCase(0, Duration.MaxNanoseconds + 1)]
+ [TestCase(0, Duration.MinNanoseconds - 1)]
+ [TestCase(Duration.MinSeconds - 1, 0)]
+ [TestCase(Duration.MaxSeconds + 1, 0)]
+ [TestCase(1, -1)]
+ [TestCase(-1, 1)]
+ public void ToTimeSpan_Invalid(long seconds, int nanoseconds)
+ {
+ var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
+ Assert.Throws<InvalidOperationException>(() => duration.ToTimeSpan());
+ }
+
+ [Test]
+ [TestCase(0, Duration.MaxNanoseconds)]
+ [TestCase(0, Duration.MinNanoseconds)]
+ [TestCase(Duration.MinSeconds, Duration.MinNanoseconds)]
+ [TestCase(Duration.MaxSeconds, Duration.MaxNanoseconds)]
+ public void ToTimeSpan_Valid(long seconds, int nanoseconds)
+ {
+ // Only testing that these values don't throw, unlike their similar tests in ToTimeSpan_Invalid
+ var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
+ duration.ToTimeSpan();
+ }
+
+ [Test]
+ public void ToString_NonNormalized()
+ {
+ // Just a single example should be sufficient...
+ var duration = new Duration { Seconds = 1, Nanos = -1 };
+ Assert.AreEqual("{ \"@warning\": \"Invalid Duration\", \"seconds\": \"1\", \"nanos\": -1 }", duration.ToString());
+ }
}
}
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/FieldMaskTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/FieldMaskTest.cs
new file mode 100644
index 00000000..89bc8275
--- /dev/null
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/FieldMaskTest.cs
@@ -0,0 +1,62 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+
+using NUnit.Framework;
+
+namespace Google.Protobuf.WellKnownTypes
+{
+ public class FieldMaskTest
+ {
+ [Test]
+ [TestCase("foo__bar")]
+ [TestCase("foo_3_ar")]
+ [TestCase("fooBar")]
+ public void ToString_Invalid(string input)
+ {
+ var mask = new FieldMask { Paths = { input } };
+ var text = mask.ToString();
+ // More specific test below
+ Assert.That(text, Is.StringContaining("@warning"));
+ Assert.That(text, Is.StringContaining(input));
+ }
+
+ [Test]
+ public void ToString_Invalid_Precise()
+ {
+ var mask = new FieldMask { Paths = { "x", "foo__bar", @"x\y" } };
+ Assert.AreEqual(
+ "{ \"@warning\": \"Invalid FieldMask\", \"paths\": [ \"x\", \"foo__bar\", \"x\\\\y\" ] }",
+ mask.ToString());
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
index 597539eb..9ecd24c6 100644
--- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
@@ -61,6 +61,29 @@ namespace Google.Protobuf.WellKnownTypes
Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 59).AddMilliseconds(1), t2.ToDateTime());
}
+ [Test]
+ [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, Timestamp.MaxNanos)]
+ [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]
+ [TestCase(0, -1)]
+ [TestCase(0, Timestamp.MaxNanos + 1)]
+ public void ToDateTime_OutOfRange(long seconds, int nanoseconds)
+ {
+ var value = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
+ Assert.Throws<InvalidOperationException>(() => value.ToDateTime());
+ }
+
+ // 1ns larger or smaller than the above values
+ [Test]
+ [TestCase(Timestamp.UnixSecondsAtBclMinValue, 0)]
+ [TestCase(Timestamp.UnixSecondsAtBclMaxValue, Timestamp.MaxNanos)]
+ [TestCase(0, 0)]
+ [TestCase(0, Timestamp.MaxNanos)]
+ public void ToDateTime_ValidBoundaries(long seconds, int nanoseconds)
+ {
+ var value = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
+ value.ToDateTime();
+ }
+
private static void AssertRoundtrip(Timestamp timestamp, DateTime dateTime)
{
Assert.AreEqual(timestamp, Timestamp.FromDateTime(dateTime));
@@ -80,5 +103,13 @@ namespace Google.Protobuf.WellKnownTypes
Assert.AreEqual(t1, t2 + difference);
Assert.AreEqual(t2, t1 - difference);
}
+
+ [Test]
+ public void ToString_NonNormalized()
+ {
+ // Just a single example should be sufficient...
+ var duration = new Timestamp { Seconds = 1, Nanos = -1 };
+ Assert.AreEqual("{ \"@warning\": \"Invalid Timestamp\", \"seconds\": \"1\", \"nanos\": -1 }", duration.ToString());
+ }
}
}
diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
index c87ceb2f..a2c833fe 100644
--- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
+++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs
@@ -149,8 +149,34 @@ namespace Google.Protobuf.WellKnownTypes
}
[Test]
+ public void RepeatedWrappersBinaryFormat()
+ {
+ // At one point we accidentally used a packed format for repeated wrappers, which is wrong (and weird).
+ // This test is just to prove that we use the right format.
+
+ var rawOutput = new MemoryStream();
+ var output = new CodedOutputStream(rawOutput);
+ // Write a value of 5
+ output.WriteTag(RepeatedWellKnownTypes.Int32FieldFieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(2);
+ output.WriteTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint);
+ output.WriteInt32(5);
+ // Write a value of 0 (empty message)
+ output.WriteTag(RepeatedWellKnownTypes.Int32FieldFieldNumber, WireFormat.WireType.LengthDelimited);
+ output.WriteLength(0);
+ output.Flush();
+ var expectedBytes = rawOutput.ToArray();
+
+ var message = new RepeatedWellKnownTypes { Int32Field = { 5, 0 } };
+ var actualBytes = message.ToByteArray();
+ Assert.AreEqual(expectedBytes, actualBytes);
+ }
+
+ [Test]
public void MapWrappersSerializeDeserialize()
{
+ // Note: no null values here, as they are prohibited in map fields
+ // (despite being representable).
var message = new MapWellKnownTypes
{
BoolField = { { 10, false }, { 20, true } },
@@ -158,13 +184,12 @@ namespace Google.Protobuf.WellKnownTypes
{ -1, ByteString.CopyFrom(1, 2, 3) },
{ 10, ByteString.CopyFrom(4, 5, 6) },
{ 1000, ByteString.Empty },
- { 10000, null }
},
DoubleField = { { 1, 12.5 }, { 10, -1.5 }, { 20, 0d } },
FloatField = { { 2, 123.25f }, { 3, -20f }, { 4, 0f } },
Int32Field = { { 5, int.MaxValue }, { 6, int.MinValue }, { 7, 0 } },
Int64Field = { { 8, long.MaxValue }, { 9, long.MinValue }, { 10, 0L } },
- StringField = { { 11, "First" }, { 12, "Second" }, { 13, "" }, { 14, null } },
+ StringField = { { 11, "First" }, { 12, "Second" }, { 13, "" } },
Uint32Field = { { 15, uint.MaxValue }, { 16, uint.MinValue }, { 17, 0U } },
Uint64Field = { { 18, ulong.MaxValue }, { 19, ulong.MinValue }, { 20, 0UL } },
};
@@ -224,13 +249,11 @@ namespace Google.Protobuf.WellKnownTypes
[Test]
public void Reflection_MapFields()
{
- // Just a single example... note that we can't have a null value here
- var message = new MapWellKnownTypes { Int32Field = { { 1, 2 }, { 3, null } } };
+ // Just a single example... note that we can't have a null value here despite the value type being int?
+ var message = new MapWellKnownTypes { Int32Field = { { 1, 2 } } };
var fields = MapWellKnownTypes.Descriptor.Fields;
var dictionary = (IDictionary) fields[MapWellKnownTypes.Int32FieldFieldNumber].Accessor.GetValue(message);
Assert.AreEqual(2, dictionary[1]);
- Assert.IsNull(dictionary[3]);
- Assert.IsTrue(dictionary.Contains(3));
}
[Test]
diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs
index 65d0e710..91bed8e3 100644
--- a/csharp/src/Google.Protobuf/CodedInputStream.cs
+++ b/csharp/src/Google.Protobuf/CodedInputStream.cs
@@ -115,7 +115,7 @@ namespace Google.Protobuf
/// <summary>
/// Creates a new CodedInputStream reading data from the given byte array.
/// </summary>
- public CodedInputStream(byte[] buffer) : this(null, Preconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length)
+ public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length)
{
}
@@ -123,7 +123,7 @@ namespace Google.Protobuf
/// Creates a new CodedInputStream that reads from the given byte array slice.
/// </summary>
public CodedInputStream(byte[] buffer, int offset, int length)
- : this(null, Preconditions.CheckNotNull(buffer, "buffer"), offset, offset + length)
+ : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length)
{
if (offset < 0 || offset > buffer.Length)
{
@@ -140,7 +140,7 @@ namespace Google.Protobuf
/// </summary>
public CodedInputStream(Stream input) : this(input, new byte[BufferSize], 0, 0)
{
- Preconditions.CheckNotNull(input, "input");
+ ProtoPreconditions.CheckNotNull(input, "input");
}
/// <summary>
diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs
index c0ed28ae..90a5ff1a 100644
--- a/csharp/src/Google.Protobuf/Collections/MapField.cs
+++ b/csharp/src/Google.Protobuf/Collections/MapField.cs
@@ -53,6 +53,13 @@ namespace Google.Protobuf.Collections
/// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.
/// </para>
/// <para>
+ /// Null values are not permitted in the map, either for wrapper types or regular messages.
+ /// If a map is deserialized from a data stream and the value is missing from an entry, a default value
+ /// is created instead. For primitive types, that is the regular default value (0, the empty string and so
+ /// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length
+ /// encoded value for the field.
+ /// </para>
+ /// <para>
/// This implementation does not generally prohibit the use of key/value types which are not
/// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee
/// that all operations will work in such cases.
@@ -61,35 +68,11 @@ namespace Google.Protobuf.Collections
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
{
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
- private readonly bool allowNullValues;
private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>();
private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
/// <summary>
- /// Constructs a new map field, defaulting the value nullability to only allow null values for message types
- /// and non-nullable value types.
- /// </summary>
- public MapField() : this(typeof(IMessage).IsAssignableFrom(typeof(TValue)) || Nullable.GetUnderlyingType(typeof(TValue)) != null)
- {
- }
-
- /// <summary>
- /// Constructs a new map field, overriding the choice of whether null values are permitted in the map.
- /// This is used by wrapper types, where maps with string and bytes wrappers as the value types
- /// support null values.
- /// </summary>
- /// <param name="allowNullValues">Whether null values are permitted in the map or not.</param>
- public MapField(bool allowNullValues)
- {
- if (allowNullValues && typeof(TValue).IsValueType() && Nullable.GetUnderlyingType(typeof(TValue)) == null)
- {
- throw new ArgumentException("allowNullValues", "Non-nullable value types do not support null values");
- }
- this.allowNullValues = allowNullValues;
- }
-
- /// <summary>
/// Creates a deep clone of this object.
/// </summary>
/// <returns>
@@ -97,13 +80,13 @@ namespace Google.Protobuf.Collections
/// </returns>
public MapField<TKey, TValue> Clone()
{
- var clone = new MapField<TKey, TValue>(allowNullValues);
+ var clone = new MapField<TKey, TValue>();
// Keys are never cloneable. Values might be.
if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
{
foreach (var pair in list)
{
- clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>)pair.Value).Clone());
+ clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clone());
}
}
else
@@ -140,7 +123,7 @@ namespace Google.Protobuf.Collections
/// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns>
public bool ContainsKey(TKey key)
{
- Preconditions.CheckNotNullUnconstrained(key, "key");
+ ProtoPreconditions.CheckNotNullUnconstrained(key, "key");
return map.ContainsKey(key);
}
@@ -157,7 +140,7 @@ namespace Google.Protobuf.Collections
/// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns>
public bool Remove(TKey key)
{
- Preconditions.CheckNotNullUnconstrained(key, "key");
+ ProtoPreconditions.CheckNotNullUnconstrained(key, "key");
LinkedListNode<KeyValuePair<TKey, TValue>> node;
if (map.TryGetValue(key, out node))
{
@@ -205,7 +188,7 @@ namespace Google.Protobuf.Collections
{
get
{
- Preconditions.CheckNotNullUnconstrained(key, "key");
+ ProtoPreconditions.CheckNotNullUnconstrained(key, "key");
TValue value;
if (TryGetValue(key, out value))
{
@@ -215,11 +198,11 @@ namespace Google.Protobuf.Collections
}
set
{
- Preconditions.CheckNotNullUnconstrained(key, "key");
+ ProtoPreconditions.CheckNotNullUnconstrained(key, "key");
// value == null check here is redundant, but avoids boxing.
- if (value == null && !allowNullValues)
+ if (value == null)
{
- Preconditions.CheckNotNullUnconstrained(value, "value");
+ ProtoPreconditions.CheckNotNullUnconstrained(value, "value");
}
LinkedListNode<KeyValuePair<TKey, TValue>> node;
var pair = new KeyValuePair<TKey, TValue>(key, value);
@@ -246,12 +229,12 @@ namespace Google.Protobuf.Collections
public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }
/// <summary>
- /// Adds the specified entries to the map.
+ /// Adds the specified entries to the map. The keys and values are not automatically cloned.
/// </summary>
/// <param name="entries">The entries to add to the map.</param>
public void Add(IDictionary<TKey, TValue> entries)
{
- Preconditions.CheckNotNull(entries, "entries");
+ ProtoPreconditions.CheckNotNull(entries, "entries");
foreach (var pair in entries)
{
Add(pair.Key, pair.Value);
@@ -347,11 +330,6 @@ namespace Google.Protobuf.Collections
}
/// <summary>
- /// Returns whether or not this map allows values to be null.
- /// </summary>
- public bool AllowsNullValues { get { return allowNullValues; } }
-
- /// <summary>
/// Gets the number of elements contained in the map.
/// </summary>
public int Count { get { return list.Count; } }
@@ -523,7 +501,7 @@ namespace Google.Protobuf.Collections
void IDictionary.Remove(object key)
{
- Preconditions.CheckNotNull(key, "key");
+ ProtoPreconditions.CheckNotNull(key, "key");
if (!(key is TKey))
{
return;
@@ -552,7 +530,7 @@ namespace Google.Protobuf.Collections
{
get
{
- Preconditions.CheckNotNull(key, "key");
+ ProtoPreconditions.CheckNotNull(key, "key");
if (!(key is TKey))
{
return null;
@@ -632,6 +610,8 @@ namespace Google.Protobuf.Collections
/// </summary>
internal class MessageAdapter : IMessage
{
+ private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
+
private readonly Codec codec;
internal TKey Key { get; set; }
internal TValue Value { get; set; }
@@ -665,6 +645,13 @@ namespace Google.Protobuf.Collections
input.SkipLastField();
}
}
+
+ // Corner case: a map entry with a key but no value, where the value type is a message.
+ // Read it as if we'd seen an input stream with no data (i.e. create a "default" message).
+ if (Value == null)
+ {
+ Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
+ }
}
public void WriteTo(CodedOutputStream output)
diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
index e3f65afe..1cde03bc 100644
--- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
+++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
@@ -34,7 +34,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
-using Google.Protobuf.Compatibility;
namespace Google.Protobuf.Collections
{
@@ -96,8 +95,8 @@ namespace Google.Protobuf.Collections
// iteration.
uint tag = input.LastTag;
var reader = codec.ValueReader;
- // Value types can be packed or not.
- if (typeof(T).IsValueType() && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
+ // Non-nullable value types can be packed or not.
+ if (FieldCodec<T>.IsPackedRepeatedField(tag))
{
int length = input.ReadLength();
if (length > 0)
@@ -134,7 +133,7 @@ namespace Google.Protobuf.Collections
return 0;
}
uint tag = codec.Tag;
- if (typeof(T).IsValueType() && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
+ if (codec.PackedRepeatedField)
{
int dataSize = CalculatePackedDataSize(codec);
return CodedOutputStream.ComputeRawVarint32Size(tag) +
@@ -186,7 +185,7 @@ namespace Google.Protobuf.Collections
}
var writer = codec.ValueWriter;
var tag = codec.Tag;
- if (typeof(T).IsValueType() && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
+ if (codec.PackedRepeatedField)
{
// Packed primitive type
uint size = (uint)CalculatePackedDataSize(codec);
diff --git a/csharp/src/Google.Protobuf/FieldCodec.cs b/csharp/src/Google.Protobuf/FieldCodec.cs
index 6ee8e2f9..98313088 100644
--- a/csharp/src/Google.Protobuf/FieldCodec.cs
+++ b/csharp/src/Google.Protobuf/FieldCodec.cs
@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
+using Google.Protobuf.Compatibility;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
@@ -329,17 +330,24 @@ namespace Google.Protobuf
}
/// <summary>
+ /// <para>
/// An encode/decode pair for a single field. This effectively encapsulates
/// all the information needed to read or write the field value from/to a coded
/// stream.
+ /// </para>
+ /// <para>
+ /// This class is public and has to be as it is used by generated code, but its public
+ /// API is very limited - just what the generated code needs to call directly.
+ /// </para>
/// </summary>
/// <remarks>
- /// This never writes default values to the stream, and is not currently designed
- /// to play well with packed arrays.
+ /// This never writes default values to the stream, and does not address "packedness"
+ /// in repeated fields itself, other than to know whether or not the field *should* be packed.
/// </remarks>
public sealed class FieldCodec<T>
{
private static readonly T DefaultDefault;
+ private static readonly bool TypeSupportsPacking = typeof(T).IsValueType() && Nullable.GetUnderlyingType(typeof(T)) == null;
static FieldCodec()
{
@@ -354,80 +362,31 @@ namespace Google.Protobuf
// Otherwise it's the default value of the CLR type
}
- private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check)
- {
- return (Func<T, bool>)(object)check;
- }
-
- private readonly Func<CodedInputStream, T> reader;
- private readonly Action<CodedOutputStream, T> writer;
- private readonly Func<T, int> sizeCalculator;
- private readonly uint tag;
- private readonly int tagSize;
- private readonly int fixedSize;
- // Default value for this codec. Usually the same for every instance of the same type, but
- // for string/ByteString wrapper fields the codec's default value is null, whereas for
- // other string/ByteString fields it's "" or ByteString.Empty.
- private readonly T defaultValue;
+ internal static bool IsPackedRepeatedField(uint tag) =>
+ TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited;
- internal FieldCodec(
- Func<CodedInputStream, T> reader,
- Action<CodedOutputStream, T> writer,
- Func<T, int> sizeCalculator,
- uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault)
- {
- }
-
- internal FieldCodec(
- Func<CodedInputStream, T> reader,
- Action<CodedOutputStream, T> writer,
- Func<T, int> sizeCalculator,
- uint tag,
- T defaultValue)
- {
- this.reader = reader;
- this.writer = writer;
- this.sizeCalculator = sizeCalculator;
- this.fixedSize = 0;
- this.tag = tag;
- this.defaultValue = defaultValue;
- tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
- }
-
- internal FieldCodec(
- Func<CodedInputStream, T> reader,
- Action<CodedOutputStream, T> writer,
- int fixedSize,
- uint tag)
- {
- this.reader = reader;
- this.writer = writer;
- this.sizeCalculator = _ => fixedSize;
- this.fixedSize = fixedSize;
- this.tag = tag;
- tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
- }
+ internal bool PackedRepeatedField { get; }
/// <summary>
- /// Returns the size calculator for just a value.
+ /// Returns a delegate to write a value (unconditionally) to a coded output stream.
/// </summary>
- internal Func<T, int> ValueSizeCalculator { get { return sizeCalculator; } }
+ internal Action<CodedOutputStream, T> ValueWriter { get; }
/// <summary>
- /// Returns a delegate to write a value (unconditionally) to a coded output stream.
+ /// Returns the size calculator for just a value.
/// </summary>
- internal Action<CodedOutputStream, T> ValueWriter { get { return writer; } }
+ internal Func<T, int> ValueSizeCalculator { get; }
/// <summary>
/// Returns a delegate to read a value from a coded input stream. It is assumed that
/// the stream is already positioned on the appropriate tag.
/// </summary>
- internal Func<CodedInputStream, T> ValueReader { get { return reader; } }
+ internal Func<CodedInputStream, T> ValueReader { get; }
/// <summary>
/// Returns the fixed size for an entry, or 0 if sizes vary.
/// </summary>
- internal int FixedSize { get { return fixedSize; } }
+ internal int FixedSize { get; }
/// <summary>
/// Gets the tag of the codec.
@@ -435,15 +394,54 @@ namespace Google.Protobuf
/// <value>
/// The tag of the codec.
/// </value>
- public uint Tag { get { return tag; } }
+ internal uint Tag { get; }
/// <summary>
- /// Gets the default value of the codec's type.
+ /// Default value for this codec. Usually the same for every instance of the same type, but
+ /// for string/ByteString wrapper fields the codec's default value is null, whereas for
+ /// other string/ByteString fields it's "" or ByteString.Empty.
/// </summary>
/// <value>
/// The default value of the codec's type.
/// </value>
- public T DefaultValue { get { return defaultValue; } }
+ internal T DefaultValue { get; }
+
+ private readonly int tagSize;
+
+ internal FieldCodec(
+ Func<CodedInputStream, T> reader,
+ Action<CodedOutputStream, T> writer,
+ int fixedSize,
+ uint tag) : this(reader, writer, _ => fixedSize, tag)
+ {
+ FixedSize = fixedSize;
+ }
+
+ internal FieldCodec(
+ Func<CodedInputStream, T> reader,
+ Action<CodedOutputStream, T> writer,
+ Func<T, int> sizeCalculator,
+ uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault)
+ {
+ }
+
+ internal FieldCodec(
+ Func<CodedInputStream, T> reader,
+ Action<CodedOutputStream, T> writer,
+ Func<T, int> sizeCalculator,
+ uint tag,
+ T defaultValue)
+ {
+ ValueReader = reader;
+ ValueWriter = writer;
+ ValueSizeCalculator = sizeCalculator;
+ FixedSize = 0;
+ Tag = tag;
+ DefaultValue = defaultValue;
+ tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
+ // Detect packed-ness once, so we can check for it within RepeatedField<T>.
+ PackedRepeatedField = IsPackedRepeatedField(tag);
+ }
/// <summary>
/// Write a tag and the given value, *if* the value is not the default.
@@ -452,8 +450,8 @@ namespace Google.Protobuf
{
if (!IsDefault(value))
{
- output.WriteTag(tag);
- writer(output, value);
+ output.WriteTag(Tag);
+ ValueWriter(output, value);
}
}
@@ -462,23 +460,14 @@ namespace Google.Protobuf
/// </summary>
/// <param name="input">The input stream to read from.</param>
/// <returns>The value read from the stream.</returns>
- public T Read(CodedInputStream input)
- {
- return reader(input);
- }
+ public T Read(CodedInputStream input) => ValueReader(input);
/// <summary>
/// Calculates the size required to write the given value, with a tag,
/// if the value is not the default.
/// </summary>
- public int CalculateSizeWithTag(T value)
- {
- return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize;
- }
+ public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize;
- private bool IsDefault(T value)
- {
- return EqualityComparer<T>.Default.Equals(value, defaultValue);
- }
+ private bool IsDefault(T value) => EqualityComparer<T>.Default.Equals(value, DefaultValue);
}
}
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
index 9179ab17..ef524baf 100644
--- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj
+++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
@@ -16,6 +16,8 @@
<FileAlignment>512</FileAlignment>
<OldToolsVersion>3.5</OldToolsVersion>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+ <NuGetPackageImportStamp>
+ </NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -62,7 +64,7 @@
<NoStdLib>true</NoStdLib>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<SignAssembly>True</SignAssembly>
- <AssemblyOriginatorKeyFile>C:\keys\Google.Protobuf.snk</AssemblyOriginatorKeyFile>
+ <AssemblyOriginatorKeyFile>..\..\keys\Google.Protobuf.snk</AssemblyOriginatorKeyFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
@@ -83,6 +85,7 @@
<Compile Include="Compatibility\TypeExtensions.cs" />
<Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" />
+ <Compile Include="ICustomDiagnosticMessage.cs" />
<Compile Include="IDeepCloneable.cs" />
<Compile Include="InvalidJsonException.cs" />
<Compile Include="JsonFormatter.cs" />
@@ -106,7 +109,7 @@
<Compile Include="Reflection\FieldDescriptor.cs" />
<Compile Include="Reflection\FieldType.cs" />
<Compile Include="Reflection\FileDescriptor.cs" />
- <Compile Include="Reflection\GeneratedCodeInfo.cs" />
+ <Compile Include="Reflection\GeneratedClrTypeInfo.cs" />
<Compile Include="Reflection\IDescriptor.cs" />
<Compile Include="Reflection\IFieldAccessor.cs" />
<Compile Include="Reflection\MapFieldAccessor.cs" />
@@ -120,7 +123,8 @@
<Compile Include="Reflection\RepeatedFieldAccessor.cs" />
<Compile Include="Reflection\ServiceDescriptor.cs" />
<Compile Include="Reflection\SingleFieldAccessor.cs" />
- <Compile Include="Preconditions.cs" />
+ <Compile Include="ProtoPreconditions.cs" />
+ <Compile Include="Reflection\TypeRegistry.cs" />
<Compile Include="WellKnownTypes\Any.cs" />
<Compile Include="WellKnownTypes\AnyPartial.cs" />
<Compile Include="WellKnownTypes\Api.cs" />
@@ -128,6 +132,7 @@
<Compile Include="WellKnownTypes\DurationPartial.cs" />
<Compile Include="WellKnownTypes\Empty.cs" />
<Compile Include="WellKnownTypes\FieldMask.cs" />
+ <Compile Include="WellKnownTypes\FieldMaskPartial.cs" />
<Compile Include="WellKnownTypes\SourceContext.cs" />
<Compile Include="WellKnownTypes\Struct.cs" />
<Compile Include="WellKnownTypes\TimeExtensions.cs" />
@@ -141,9 +146,17 @@
</ItemGroup>
<ItemGroup>
<None Include="Google.Protobuf.nuspec" />
+ <None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+ <Import Project="..\packages\NuSpec.ReferenceGenerator.1.4.1\build\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\NuSpec.ReferenceGenerator.targets" Condition="Exists('..\packages\NuSpec.ReferenceGenerator.1.4.1\build\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\NuSpec.ReferenceGenerator.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('..\packages\NuSpec.ReferenceGenerator.1.4.1\build\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\NuSpec.ReferenceGenerator.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NuSpec.ReferenceGenerator.1.4.1\build\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\NuSpec.ReferenceGenerator.targets'))" />
+ </Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.nuspec b/csharp/src/Google.Protobuf/Google.Protobuf.nuspec
index 9542138a..d5302544 100644
--- a/csharp/src/Google.Protobuf/Google.Protobuf.nuspec
+++ b/csharp/src/Google.Protobuf/Google.Protobuf.nuspec
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>Google.Protobuf</id>
<title>Google Protocol Buffers C#</title>
<summary>C# runtime library for Protocol Buffers - Google's data interchange format.</summary>
<description>See project site for more info.</description>
- <version>3.0.0-alpha4</version>
+ <version>3.0.0-beta2</version>
<authors>Google Inc.</authors>
<owners>protobuf-packages</owners>
<licenseUrl>https://github.com/google/protobuf/blob/master/LICENSE</licenseUrl>
@@ -14,24 +14,53 @@
<releaseNotes>C# proto3 support</releaseNotes>
<copyright>Copyright 2015, Google Inc.</copyright>
<tags>Protocol Buffers Binary Serialization Format Google proto proto3</tags>
+ <dependencies>
+ <!-- Dependencies for older, monolithic-assembly platforms -->
+ <group targetFramework="net45" />
+ <group targetFramework="wp8" />
+ <group targetFramework="win8" />
+ <group targetFramework="wpa81" />
+ <group targetFramework="xamarin.ios" />
+ <group targetFramework="monotouch" />
+ <group targetFramework="monoandroid" />
+
+ <!-- Dependencies for newer, more granular platforms (.NET Core etc) -->
+ <group targetFramework="dotnet">
+ <dependency id="System.Collections" version="4.0.0" />
+ <dependency id="System.Diagnostics.Debug" version="4.0.0" />
+ <dependency id="System.Globalization" version="4.0.0" />
+ <dependency id="System.IO" version="4.0.0" />
+ <dependency id="System.Linq" version="4.0.0" />
+ <dependency id="System.Linq.Expressions" version="4.0.0" />
+ <dependency id="System.ObjectModel" version="4.0.0" />
+ <dependency id="System.Reflection" version="4.0.0" />
+ <dependency id="System.Runtime" version="4.0.0" />
+ <dependency id="System.Runtime.Extensions" version="4.0.0" />
+ <dependency id="System.Text.Encoding" version="4.0.0" />
+ <dependency id="System.Text.RegularExpressions" version="4.0.0" />
+ </group>
+ </dependencies>
</metadata>
<files>
<file src="bin/ReleaseSigned/Google.Protobuf.dll" target="lib/portable-net45+netcore45+wpa81+wp8" />
- <file src="bin/ReleaseSigned/Google.Protobuf.pdb" target="lib/portable-net45+netcore45+wpa81+wp8" />
- <file src="bin/ReleaseSigned/Google.Protobuf.xml" target="lib/portable-net45+netcore45+wpa81+wp8" />
- <file src="**\*.cs" target="src" />
- <file src="..\..\..\cmake\Release\protoc.exe" target="tools" />
- <file src="..\..\..\src\google\protobuf\any.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\api.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\descriptor.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\duration.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\empty.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\field_mask.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\source_context.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\struct.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\timestamp.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\any.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\type.proto" target="tools\google\protobuf" />
- <file src="..\..\..\src\google\protobuf\wrappers.proto" target="tools\google\protobuf" />
+ <file src="bin/ReleaseSigned/Google.Protobuf.pdb" target="lib/portable-net45+netcore45+wpa81+wp8" />
+ <file src="bin/ReleaseSigned/Google.Protobuf.xml" target="lib/portable-net45+netcore45+wpa81+wp8" />
+ <file src="bin/ReleaseSigned/Google.Protobuf.dll" target="lib/dotnet" />
+ <file src="bin/ReleaseSigned/Google.Protobuf.pdb" target="lib/dotnet" />
+ <file src="bin/ReleaseSigned/Google.Protobuf.xml" target="lib/dotnet" />
+ <file src="**\*.cs" target="src" />
+ <file src="..\..\..\cmake\Release\protoc.exe" target="tools" />
+ <file src="..\..\..\src\google\protobuf\any.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\api.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\descriptor.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\duration.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\empty.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\field_mask.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\source_context.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\struct.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\timestamp.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\any.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\type.proto" target="tools\google\protobuf" />
+ <file src="..\..\..\src\google\protobuf\wrappers.proto" target="tools\google\protobuf" />
</files>
-</package>
+</package> \ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs b/csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs
new file mode 100644
index 00000000..a0090569
--- /dev/null
+++ b/csharp/src/Google.Protobuf/ICustomDiagnosticMessage.cs
@@ -0,0 +1,69 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+namespace Google.Protobuf
+{
+ /// <summary>
+ /// A message type that has a custom string format for diagnostic purposes.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Calling <see cref="object.ToString"/> on a generated message type normally
+ /// returns the JSON representation. If a message type implements this interface,
+ /// then the <see cref="ToDiagnosticString"/> method will be called instead of the regular
+ /// JSON formatting code, but only when <c>ToString()</c> is called either on the message itself
+ /// or on another message which contains it. This does not affect the normal JSON formatting of
+ /// the message.
+ /// </para>
+ /// <para>
+ /// For example, if you create a proto message representing a GUID, the internal
+ /// representation may be a <c>bytes</c> field or four <c>fixed32</c> fields. However, when debugging
+ /// it may be more convenient to see a result in the same format as <see cref="System.Guid"/> provides.
+ /// </para>
+ /// <para>This interface extends <see cref="IMessage"/> to avoid it accidentally being implemented
+ /// on types other than messages, where it would not be used by anything in the framework.</para>
+ /// </remarks>
+ public interface ICustomDiagnosticMessage : IMessage
+ {
+ /// <summary>
+ /// Returns a string representation of this object, for diagnostic purposes.
+ /// </summary>
+ /// <remarks>
+ /// This method is called when a message is formatted as part of a <see cref="object.ToString"/>
+ /// call. It does not affect the JSON representation used by <see cref="JsonFormatter"/> other than
+ /// in calls to <see cref="JsonFormatter.ToDiagnosticString(IMessage)"/>. While it is recommended
+ /// that the result is valid JSON, this is never assumed by the Protobuf library.
+ /// </remarks>
+ /// <returns>A string representation of this object, for diagnostic purposes.</returns>
+ string ToDiagnosticString();
+ }
+}
diff --git a/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs b/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
index cacda648..eeb0f13a 100644
--- a/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
+++ b/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs
@@ -30,6 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
+using System;
using System.IO;
namespace Google.Protobuf
@@ -45,6 +46,11 @@ namespace Google.Protobuf
{
}
+ internal InvalidProtocolBufferException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
internal static InvalidProtocolBufferException MoreDataAvailable()
{
return new InvalidProtocolBufferException(
@@ -82,6 +88,11 @@ namespace Google.Protobuf
"Protocol message contained an invalid tag (zero).");
}
+ internal static InvalidProtocolBufferException InvalidBase64(Exception innerException)
+ {
+ return new InvalidProtocolBufferException("Invalid base64 data", innerException);
+ }
+
internal static InvalidProtocolBufferException InvalidEndTag()
{
return new InvalidProtocolBufferException(
diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs
index 08e3142e..60f61fc8 100644
--- a/csharp/src/Google.Protobuf/JsonFormatter.cs
+++ b/csharp/src/Google.Protobuf/JsonFormatter.cs
@@ -37,6 +37,7 @@ using System.Text;
using Google.Protobuf.Reflection;
using Google.Protobuf.WellKnownTypes;
using System.Linq;
+using System.Collections.Generic;
namespace Google.Protobuf
{
@@ -55,12 +56,20 @@ namespace Google.Protobuf
/// </remarks>
public sealed class JsonFormatter
{
- private static JsonFormatter defaultInstance = new JsonFormatter(Settings.Default);
+ internal const string AnyTypeUrlField = "@type";
+ internal const string AnyDiagnosticValueField = "@value";
+ internal const string AnyWellKnownTypeValueField = "value";
+ private const string TypeUrlPrefix = "type.googleapis.com";
+ private const string NameValueSeparator = ": ";
+ private const string PropertySeparator = ", ";
/// <summary>
/// Returns a formatter using the default settings.
/// </summary>
- public static JsonFormatter Default { get { return defaultInstance; } }
+ public static JsonFormatter Default { get; } = new JsonFormatter(Settings.Default);
+
+ // A JSON formatter which *only* exists
+ private static readonly JsonFormatter diagnosticFormatter = new JsonFormatter(Settings.Default);
/// <summary>
/// The JSON representation of the first 160 characters of Unicode.
@@ -114,6 +123,8 @@ namespace Google.Protobuf
private readonly Settings settings;
+ private bool DiagnosticOnly => ReferenceEquals(this, diagnosticFormatter);
+
/// <summary>
/// Creates a new formatted with the given settings.
/// </summary>
@@ -130,11 +141,11 @@ namespace Google.Protobuf
/// <returns>The formatted message.</returns>
public string Format(IMessage message)
{
- Preconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(message, nameof(message));
StringBuilder builder = new StringBuilder();
if (message.Descriptor.IsWellKnownType)
{
- WriteWellKnownTypeValue(builder, message.Descriptor, message, false);
+ WriteWellKnownTypeValue(builder, message.Descriptor, message);
}
else
{
@@ -143,6 +154,29 @@ namespace Google.Protobuf
return builder.ToString();
}
+ /// <summary>
+ /// Converts a message to JSON for diagnostic purposes with no extra context.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// This differs from calling <see cref="Format(IMessage)"/> on the default JSON
+ /// formatter in its handling of <see cref="Any"/>. As no type registry is available
+ /// in <see cref="object.ToString"/> calls, the normal way of resolving the type of
+ /// an <c>Any</c> message cannot be applied. Instead, a JSON property named <c>@value</c>
+ /// is included with the base64 data from the <see cref="Any.Value"/> property of the message.
+ /// </para>
+ /// <para>The value returned by this method is only designed to be used for diagnostic
+ /// purposes. It may not be parsable by <see cref="JsonParser"/>, and may not be parsable
+ /// by other Protocol Buffer implementations.</para>
+ /// </remarks>
+ /// <param name="message">The message to format for diagnostic purposes.</param>
+ /// <returns>The diagnostic-only JSON representation of the message</returns>
+ public static string ToDiagnosticString(IMessage message)
+ {
+ ProtoPreconditions.CheckNotNull(message, nameof(message));
+ return diagnosticFormatter.Format(message);
+ }
+
private void WriteMessage(StringBuilder builder, IMessage message)
{
if (message == null)
@@ -150,14 +184,28 @@ namespace Google.Protobuf
WriteNull(builder);
return;
}
+ if (DiagnosticOnly)
+ {
+ ICustomDiagnosticMessage customDiagnosticMessage = message as ICustomDiagnosticMessage;
+ if (customDiagnosticMessage != null)
+ {
+ builder.Append(customDiagnosticMessage.ToDiagnosticString());
+ return;
+ }
+ }
builder.Append("{ ");
+ bool writtenFields = WriteMessageFields(builder, message, false);
+ builder.Append(writtenFields ? " }" : "}");
+ }
+
+ private bool WriteMessageFields(StringBuilder builder, IMessage message, bool assumeFirstFieldWritten)
+ {
var fields = message.Descriptor.Fields;
- bool first = true;
+ bool first = !assumeFirstFieldWritten;
// First non-oneof fields
foreach (var field in fields.InFieldNumberOrder())
{
var accessor = field.Accessor;
- // Oneofs are written later
if (field.ContainingOneof != null && field.ContainingOneof.Accessor.GetCaseFieldDescriptor(message) != field)
{
continue;
@@ -169,23 +217,43 @@ namespace Google.Protobuf
{
continue;
}
- // Omit awkward (single) values such as unknown enum values
- if (!field.IsRepeated && !field.IsMap && !CanWriteSingleValue(value))
- {
- continue;
- }
// Okay, all tests complete: let's write the field value...
if (!first)
{
- builder.Append(", ");
+ builder.Append(PropertySeparator);
}
WriteString(builder, ToCamelCase(accessor.Descriptor.Name));
- builder.Append(": ");
+ builder.Append(NameValueSeparator);
WriteValue(builder, value);
first = false;
}
- builder.Append(first ? "}" : " }");
+ return !first;
+ }
+
+ /// <summary>
+ /// Camel-case converter with added strictness for field mask formatting.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The field mask is invalid for JSON representation</exception>
+ private static string ToCamelCaseForFieldMask(string input)
+ {
+ for (int i = 0; i < input.Length; i++)
+ {
+ char c = input[i];
+ if (c >= 'A' && c <= 'Z')
+ {
+ throw new InvalidOperationException($"Invalid field mask to be converted to JSON: {input}");
+ }
+ if (c == '_' && i < input.Length - 1)
+ {
+ char next = input[i + 1];
+ if (next < 'a' || next > 'z')
+ {
+ throw new InvalidOperationException($"Invalid field mask to be converted to JSON: {input}");
+ }
+ }
+ }
+ return ToCamelCase(input);
}
// Converted from src/google/protobuf/util/internal/utility.cc ToCamelCase
@@ -336,7 +404,14 @@ namespace Google.Protobuf
}
else if (value is System.Enum)
{
- WriteString(builder, value.ToString());
+ if (System.Enum.IsDefined(value.GetType(), value))
+ {
+ WriteString(builder, value.ToString());
+ }
+ else
+ {
+ WriteValue(builder, (int) value);
+ }
}
else if (value is float || value is double)
{
@@ -357,7 +432,7 @@ namespace Google.Protobuf
IMessage message = (IMessage) value;
if (message.Descriptor.IsWellKnownType)
{
- WriteWellKnownTypeValue(builder, message.Descriptor, value, true);
+ WriteWellKnownTypeValue(builder, message.Descriptor, value);
}
else
{
@@ -376,8 +451,10 @@ namespace Google.Protobuf
/// values are using the embedded well-known types, in order to allow for dynamic messages
/// in the future.
/// </summary>
- private void WriteWellKnownTypeValue(StringBuilder builder, MessageDescriptor descriptor, object value, bool inField)
+ private void WriteWellKnownTypeValue(StringBuilder builder, MessageDescriptor descriptor, object value)
{
+ // Currently, we can never actually get here, because null values are always handled by the caller. But if we *could*,
+ // this would do the right thing.
if (value == null)
{
WriteNull(builder);
@@ -388,8 +465,7 @@ namespace Google.Protobuf
// If it's the message form, we can extract the value first, which *will* be the (possibly boxed) native value,
// and then proceed, writing it as if we were definitely in a field. (We never need to wrap it in an extra string...
// WriteValue will do the right thing.)
- // TODO: Detect this differently when we have dynamic messages.
- if (descriptor.File == Int32Value.Descriptor.File)
+ if (descriptor.IsWrapperType)
{
if (value is IMessage)
{
@@ -401,17 +477,17 @@ namespace Google.Protobuf
}
if (descriptor.FullName == Timestamp.Descriptor.FullName)
{
- MaybeWrapInString(builder, value, WriteTimestamp, inField);
+ WriteTimestamp(builder, (IMessage) value);
return;
}
if (descriptor.FullName == Duration.Descriptor.FullName)
{
- MaybeWrapInString(builder, value, WriteDuration, inField);
+ WriteDuration(builder, (IMessage) value);
return;
}
if (descriptor.FullName == FieldMask.Descriptor.FullName)
{
- MaybeWrapInString(builder, value, WriteFieldMask, inField);
+ WriteFieldMask(builder, (IMessage) value);
return;
}
if (descriptor.FullName == Struct.Descriptor.FullName)
@@ -430,25 +506,12 @@ namespace Google.Protobuf
WriteStructFieldValue(builder, (IMessage) value);
return;
}
- WriteMessage(builder, (IMessage) value);
- }
-
- /// <summary>
- /// Some well-known types end up as string values... so they need wrapping in quotes, but only
- /// when they're being used as fields within another message.
- /// </summary>
- private void MaybeWrapInString(StringBuilder builder, object value, Action<StringBuilder, IMessage> action, bool inField)
- {
- if (inField)
+ if (descriptor.FullName == Any.Descriptor.FullName)
{
- builder.Append('"');
- action(builder, (IMessage) value);
- builder.Append('"');
- }
- else
- {
- action(builder, (IMessage) value);
+ WriteAny(builder, (IMessage) value);
+ return;
}
+ WriteMessage(builder, (IMessage) value);
}
private void WriteTimestamp(StringBuilder builder, IMessage value)
@@ -459,15 +522,7 @@ namespace Google.Protobuf
// it still works in that case.
int nanos = (int) value.Descriptor.Fields[Timestamp.NanosFieldNumber].Accessor.GetValue(value);
long seconds = (long) value.Descriptor.Fields[Timestamp.SecondsFieldNumber].Accessor.GetValue(value);
-
- // Even if the original message isn't using the built-in classes, we can still build one... and then
- // rely on it being normalized.
- Timestamp normalized = Timestamp.Normalize(seconds, nanos);
- // Use .NET's formatting for the value down to the second, including an opening double quote (as it's a string value)
- DateTime dateTime = normalized.ToDateTime();
- builder.Append(dateTime.ToString("yyyy'-'MM'-'dd'T'HH:mm:ss", CultureInfo.InvariantCulture));
- AppendNanoseconds(builder, Math.Abs(normalized.Nanos));
- builder.Append('Z');
+ builder.Append(Timestamp.ToJson(seconds, nanos, DiagnosticOnly));
}
private void WriteDuration(StringBuilder builder, IMessage value)
@@ -475,51 +530,76 @@ namespace Google.Protobuf
// TODO: Same as for WriteTimestamp
int nanos = (int) value.Descriptor.Fields[Duration.NanosFieldNumber].Accessor.GetValue(value);
long seconds = (long) value.Descriptor.Fields[Duration.SecondsFieldNumber].Accessor.GetValue(value);
+ builder.Append(Duration.ToJson(seconds, nanos, DiagnosticOnly));
+ }
- // Even if the original message isn't using the built-in classes, we can still build one... and then
- // rely on it being normalized.
- Duration normalized = Duration.Normalize(seconds, nanos);
+ private void WriteFieldMask(StringBuilder builder, IMessage value)
+ {
+ var paths = (IList<string>) value.Descriptor.Fields[FieldMask.PathsFieldNumber].Accessor.GetValue(value);
+ builder.Append(FieldMask.ToJson(paths, DiagnosticOnly));
+ }
+
+ private void WriteAny(StringBuilder builder, IMessage value)
+ {
+ if (DiagnosticOnly)
+ {
+ WriteDiagnosticOnlyAny(builder, value);
+ return;
+ }
- // The seconds part will normally provide the minus sign if we need it, but not if it's 0...
- if (normalized.Seconds == 0 && normalized.Nanos < 0)
+ string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value);
+ ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value);
+ string typeName = GetTypeName(typeUrl);
+ MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName);
+ if (descriptor == null)
{
- builder.Append('-');
+ throw new InvalidOperationException($"Type registry has no descriptor for type name '{typeName}'");
}
+ IMessage message = descriptor.Parser.ParseFrom(data);
+ builder.Append("{ ");
+ WriteString(builder, AnyTypeUrlField);
+ builder.Append(NameValueSeparator);
+ WriteString(builder, typeUrl);
- builder.Append(normalized.Seconds.ToString("d", CultureInfo.InvariantCulture));
- AppendNanoseconds(builder, Math.Abs(normalized.Nanos));
- builder.Append('s');
+ if (descriptor.IsWellKnownType)
+ {
+ builder.Append(PropertySeparator);
+ WriteString(builder, AnyWellKnownTypeValueField);
+ builder.Append(NameValueSeparator);
+ WriteWellKnownTypeValue(builder, descriptor, message);
+ }
+ else
+ {
+ WriteMessageFields(builder, message, true);
+ }
+ builder.Append(" }");
}
- private void WriteFieldMask(StringBuilder builder, IMessage value)
+ private void WriteDiagnosticOnlyAny(StringBuilder builder, IMessage value)
{
- IList paths = (IList) value.Descriptor.Fields[FieldMask.PathsFieldNumber].Accessor.GetValue(value);
- AppendEscapedString(builder, string.Join(",", paths.Cast<string>().Select(ToCamelCase)));
+ string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value);
+ ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value);
+ builder.Append("{ ");
+ WriteString(builder, AnyTypeUrlField);
+ builder.Append(NameValueSeparator);
+ WriteString(builder, typeUrl);
+ builder.Append(PropertySeparator);
+ WriteString(builder, AnyDiagnosticValueField);
+ builder.Append(NameValueSeparator);
+ builder.Append('"');
+ builder.Append(data.ToBase64());
+ builder.Append('"');
+ builder.Append(" }");
}
- /// <summary>
- /// Appends a number of nanoseconds to a StringBuilder. Either 0 digits are added (in which
- /// case no "." is appended), or 3 6 or 9 digits.
- /// </summary>
- private static void AppendNanoseconds(StringBuilder builder, int nanos)
+ internal static string GetTypeName(String typeUrl)
{
- if (nanos != 0)
+ string[] parts = typeUrl.Split('/');
+ if (parts.Length != 2 || parts[0] != TypeUrlPrefix)
{
- builder.Append('.');
- // Output to 3, 6 or 9 digits.
- if (nanos % 1000000 == 0)
- {
- builder.Append((nanos / 1000000).ToString("d", CultureInfo.InvariantCulture));
- }
- else if (nanos % 1000 == 0)
- {
- builder.Append((nanos / 1000).ToString("d", CultureInfo.InvariantCulture));
- }
- else
- {
- builder.Append(nanos.ToString("d", CultureInfo.InvariantCulture));
- }
+ throw new InvalidProtocolBufferException($"Invalid type url: {typeUrl}");
}
+ return parts[1];
}
private void WriteStruct(StringBuilder builder, IMessage message)
@@ -538,10 +618,10 @@ namespace Google.Protobuf
if (!first)
{
- builder.Append(", ");
+ builder.Append(PropertySeparator);
}
WriteString(builder, key);
- builder.Append(": ");
+ builder.Append(NameValueSeparator);
WriteStructFieldValue(builder, value);
first = false;
}
@@ -569,7 +649,7 @@ namespace Google.Protobuf
case Value.ListValueFieldNumber:
// Structs and ListValues are nested messages, and already well-known types.
var nestedMessage = (IMessage) specifiedField.Accessor.GetValue(message);
- WriteWellKnownTypeValue(builder, nestedMessage.Descriptor, nestedMessage, true);
+ WriteWellKnownTypeValue(builder, nestedMessage.Descriptor, nestedMessage);
return;
case Value.NullValueFieldNumber:
WriteNull(builder);
@@ -585,13 +665,9 @@ namespace Google.Protobuf
bool first = true;
foreach (var value in list)
{
- if (!CanWriteSingleValue(value))
- {
- continue;
- }
if (!first)
{
- builder.Append(", ");
+ builder.Append(PropertySeparator);
}
WriteValue(builder, value);
first = false;
@@ -606,13 +682,9 @@ namespace Google.Protobuf
// This will box each pair. Could use IDictionaryEnumerator, but that's ugly in terms of disposal.
foreach (DictionaryEntry pair in dictionary)
{
- if (!CanWriteSingleValue(pair.Value))
- {
- continue;
- }
if (!first)
{
- builder.Append(", ");
+ builder.Append(PropertySeparator);
}
string keyText;
if (pair.Key is string)
@@ -636,7 +708,7 @@ namespace Google.Protobuf
throw new ArgumentException("Unhandled dictionary key type: " + pair.Key.GetType());
}
WriteString(builder, keyText);
- builder.Append(": ");
+ builder.Append(NameValueSeparator);
WriteValue(builder, pair.Value);
first = false;
}
@@ -663,18 +735,9 @@ namespace Google.Protobuf
/// <remarks>
/// Other than surrogate pair handling, this code is mostly taken from src/google/protobuf/util/internal/json_escaping.cc.
/// </remarks>
- private void WriteString(StringBuilder builder, string text)
+ internal static void WriteString(StringBuilder builder, string text)
{
builder.Append('"');
- AppendEscapedString(builder, text);
- builder.Append('"');
- }
-
- /// <summary>
- /// Appends the given text to the string builder, escaping as required.
- /// </summary>
- private void AppendEscapedString(StringBuilder builder, string text)
- {
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
@@ -734,6 +797,7 @@ namespace Google.Protobuf
break;
}
}
+ builder.Append('"');
}
private const string Hex = "0123456789abcdef";
@@ -751,28 +815,50 @@ namespace Google.Protobuf
/// </summary>
public sealed class Settings
{
- private static readonly Settings defaultInstance = new Settings(false);
-
/// <summary>
/// Default settings, as used by <see cref="JsonFormatter.Default"/>
/// </summary>
- public static Settings Default { get { return defaultInstance; } }
+ public static Settings Default { get; }
- private readonly bool formatDefaultValues;
+ // Workaround for the Mono compiler complaining about XML comments not being on
+ // valid language elements.
+ static Settings()
+ {
+ Default = new Settings(false);
+ }
/// <summary>
/// Whether fields whose values are the default for the field type (e.g. 0 for integers)
/// should be formatted (true) or omitted (false).
/// </summary>
- public bool FormatDefaultValues { get { return formatDefaultValues; } }
+ public bool FormatDefaultValues { get; }
+
+ /// <summary>
+ /// The type registry used to format <see cref="Any"/> messages.
+ /// </summary>
+ public TypeRegistry TypeRegistry { get; }
+
+ // TODO: Work out how we're going to scale this to multiple settings. "WithXyz" methods?
+
+ /// <summary>
+ /// Creates a new <see cref="Settings"/> object with the specified formatting of default values
+ /// and an empty type registry.
+ /// </summary>
+ /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
+ public Settings(bool formatDefaultValues) : this(formatDefaultValues, TypeRegistry.Empty)
+ {
+ }
/// <summary>
- /// Creates a new <see cref="Settings"/> object with the specified formatting of default values.
+ /// Creates a new <see cref="Settings"/> object with the specified formatting of default values
+ /// and type registry.
/// </summary>
/// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
- public Settings(bool formatDefaultValues)
+ /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages.</param>
+ public Settings(bool formatDefaultValues, TypeRegistry typeRegistry)
{
- this.formatDefaultValues = formatDefaultValues;
+ FormatDefaultValues = formatDefaultValues;
+ TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nameof(typeRegistry));
}
}
}
diff --git a/csharp/src/Google.Protobuf/JsonParser.cs b/csharp/src/Google.Protobuf/JsonParser.cs
index b4a35d7c..80d3013d 100644
--- a/csharp/src/Google.Protobuf/JsonParser.cs
+++ b/csharp/src/Google.Protobuf/JsonParser.cs
@@ -77,6 +77,7 @@ namespace Google.Protobuf
{ ListValue.Descriptor.FullName, (parser, message, tokenizer) =>
parser.MergeRepeatedField(message, message.Descriptor.Fields[ListValue.ValuesFieldNumber], tokenizer) },
{ Struct.Descriptor.FullName, (parser, message, tokenizer) => parser.MergeStruct(message, tokenizer) },
+ { Any.Descriptor.FullName, (parser, message, tokenizer) => parser.MergeAny(message, tokenizer) },
{ FieldMask.Descriptor.FullName, (parser, message, tokenizer) => MergeFieldMask(message, tokenizer.Next()) },
{ Int32Value.Descriptor.FullName, MergeWrapperField },
{ Int64Value.Descriptor.FullName, MergeWrapperField },
@@ -128,7 +129,7 @@ namespace Google.Protobuf
/// <param name="jsonReader">Reader providing the JSON to parse.</param>
internal void Merge(IMessage message, TextReader jsonReader)
{
- var tokenizer = new JsonTokenizer(jsonReader);
+ var tokenizer = JsonTokenizer.FromTextReader(jsonReader);
Merge(message, tokenizer);
var lastToken = tokenizer.Next();
if (lastToken != JsonToken.EndDocument)
@@ -167,6 +168,10 @@ namespace Google.Protobuf
}
var descriptor = message.Descriptor;
var jsonFieldMap = descriptor.Fields.ByJsonName();
+ // All the oneof fields we've already accounted for - we can only see each of them once.
+ // The set is created lazily to avoid the overhead of creating a set for every message
+ // we parsed, when oneofs are relatively rare.
+ HashSet<OneofDescriptor> seenOneofs = null;
while (true)
{
token = tokenizer.Next();
@@ -182,6 +187,17 @@ namespace Google.Protobuf
FieldDescriptor field;
if (jsonFieldMap.TryGetValue(name, out field))
{
+ if (field.ContainingOneof != null)
+ {
+ if (seenOneofs == null)
+ {
+ seenOneofs = new HashSet<OneofDescriptor>();
+ }
+ if (!seenOneofs.Add(field.ContainingOneof))
+ {
+ throw new InvalidProtocolBufferException($"Multiple values specified for oneof {field.ContainingOneof.Name}");
+ }
+ }
MergeField(message, field, tokenizer);
}
else
@@ -199,10 +215,15 @@ namespace Google.Protobuf
var token = tokenizer.Next();
if (token.Type == JsonToken.TokenType.Null)
{
+ // Clear the field if we see a null token, unless it's for a singular field of type
+ // google.protobuf.Value.
// Note: different from Java API, which just ignores it.
// TODO: Bring it more in line? Discuss...
- field.Accessor.Clear(message);
- return;
+ if (field.IsMap || field.IsRepeated || !IsGoogleProtobufValueField(field))
+ {
+ field.Accessor.Clear(message);
+ return;
+ }
}
tokenizer.PushBack(token);
@@ -238,6 +259,10 @@ namespace Google.Protobuf
return;
}
tokenizer.PushBack(token);
+ if (token.Type == JsonToken.TokenType.Null)
+ {
+ throw new InvalidProtocolBufferException("Repeated field elements cannot be null");
+ }
list.Add(ParseSingleValue(field, tokenizer));
}
}
@@ -269,19 +294,30 @@ namespace Google.Protobuf
}
object key = ParseMapKey(keyField, token.StringValue);
object value = ParseSingleValue(valueField, tokenizer);
- // TODO: Null handling
+ if (value == null)
+ {
+ throw new InvalidProtocolBufferException("Map values must not be null");
+ }
dictionary[key] = value;
}
}
+ private static bool IsGoogleProtobufValueField(FieldDescriptor field)
+ {
+ return field.FieldType == FieldType.Message &&
+ field.MessageType.FullName == Value.Descriptor.FullName;
+ }
+
private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tokenizer)
{
var token = tokenizer.Next();
if (token.Type == JsonToken.TokenType.Null)
{
- if (field.FieldType == FieldType.Message && field.MessageType.FullName == Value.Descriptor.FullName)
+ // TODO: In order to support dynamic messages, we should really build this up
+ // dynamically.
+ if (IsGoogleProtobufValueField(field))
{
- return new Value { NullValue = NullValue.NULL_VALUE };
+ return Value.ForNull();
}
return null;
}
@@ -291,8 +327,7 @@ namespace Google.Protobuf
{
// Parse wrapper types as their constituent types.
// TODO: What does this mean for null?
- // TODO: Detect this differently when we have dynamic messages, and put it in one place...
- if (field.MessageType.IsWellKnownType && field.MessageType.File == Int32Value.Descriptor.File)
+ if (field.MessageType.IsWrapperType)
{
field = field.MessageType.Fields[WrappersReflection.WrapperValueFieldNumber];
fieldType = field.FieldType;
@@ -339,6 +374,7 @@ namespace Google.Protobuf
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public T Parse<T>(string json) where T : IMessage, new()
{
+ ProtoPreconditions.CheckNotNull(json, nameof(json));
return Parse<T>(new StringReader(json));
}
@@ -351,11 +387,42 @@ namespace Google.Protobuf
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public T Parse<T>(TextReader jsonReader) where T : IMessage, new()
{
+ ProtoPreconditions.CheckNotNull(jsonReader, nameof(jsonReader));
T message = new T();
Merge(message, jsonReader);
return message;
}
+ /// <summary>
+ /// Parses <paramref name="json"/> into a new message.
+ /// </summary>
+ /// <param name="json">The JSON to parse.</param>
+ /// <param name="descriptor">Descriptor of message type to parse.</param>
+ /// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
+ /// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
+ public IMessage Parse(string json, MessageDescriptor descriptor)
+ {
+ ProtoPreconditions.CheckNotNull(json, nameof(json));
+ ProtoPreconditions.CheckNotNull(descriptor, nameof(descriptor));
+ return Parse(new StringReader(json), descriptor);
+ }
+
+ /// <summary>
+ /// Parses JSON read from <paramref name="jsonReader"/> into a new message.
+ /// </summary>
+ /// <param name="jsonReader">Reader providing the JSON to parse.</param>
+ /// <param name="descriptor">Descriptor of message type to parse.</param>
+ /// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
+ /// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
+ public IMessage Parse(TextReader jsonReader, MessageDescriptor descriptor)
+ {
+ ProtoPreconditions.CheckNotNull(jsonReader, nameof(jsonReader));
+ ProtoPreconditions.CheckNotNull(descriptor, nameof(descriptor));
+ IMessage message = descriptor.Parser.CreateTemplate();
+ Merge(message, jsonReader);
+ return message;
+ }
+
private void MergeStructValue(IMessage message, JsonTokenizer tokenizer)
{
var firstToken = tokenizer.Next();
@@ -411,6 +478,88 @@ namespace Google.Protobuf
MergeMapField(message, field, tokenizer);
}
+ private void MergeAny(IMessage message, JsonTokenizer tokenizer)
+ {
+ // Record the token stream until we see the @type property. At that point, we can take the value, consult
+ // the type registry for the relevant message, and replay the stream, omitting the @type property.
+ var tokens = new List<JsonToken>();
+
+ var token = tokenizer.Next();
+ if (token.Type != JsonToken.TokenType.StartObject)
+ {
+ throw new InvalidProtocolBufferException("Expected object value for Any");
+ }
+ int typeUrlObjectDepth = tokenizer.ObjectDepth;
+
+ // The check for the property depth protects us from nested Any values which occur before the type URL
+ // for *this* Any.
+ while (token.Type != JsonToken.TokenType.Name ||
+ token.StringValue != JsonFormatter.AnyTypeUrlField ||
+ tokenizer.ObjectDepth != typeUrlObjectDepth)
+ {
+ tokens.Add(token);
+ token = tokenizer.Next();
+
+ if (tokenizer.ObjectDepth < typeUrlObjectDepth)
+ {
+ throw new InvalidProtocolBufferException("Any message with no @type");
+ }
+ }
+
+ // Don't add the @type property or its value to the recorded token list
+ token = tokenizer.Next();
+ if (token.Type != JsonToken.TokenType.StringValue)
+ {
+ throw new InvalidProtocolBufferException("Expected string value for Any.@type");
+ }
+ string typeUrl = token.StringValue;
+ string typeName = JsonFormatter.GetTypeName(typeUrl);
+
+ MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName);
+ if (descriptor == null)
+ {
+ throw new InvalidOperationException($"Type registry has no descriptor for type name '{typeName}'");
+ }
+
+ // Now replay the token stream we've already read and anything that remains of the object, just parsing it
+ // as normal. Our original tokenizer should end up at the end of the object.
+ var replay = JsonTokenizer.FromReplayedTokens(tokens, tokenizer);
+ var body = descriptor.Parser.CreateTemplate();
+ if (descriptor.IsWellKnownType)
+ {
+ MergeWellKnownTypeAnyBody(body, replay);
+ }
+ else
+ {
+ Merge(body, replay);
+ }
+ var data = body.ToByteString();
+
+ // Now that we have the message data, we can pack it into an Any (the message received as a parameter).
+ message.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.SetValue(message, typeUrl);
+ message.Descriptor.Fields[Any.ValueFieldNumber].Accessor.SetValue(message, data);
+ }
+
+ // Well-known types end up in a property called "value" in the JSON. As there's no longer a @type property
+ // in the given JSON token stream, we should *only* have tokens of start-object, name("value"), the value
+ // itself, and then end-object.
+ private void MergeWellKnownTypeAnyBody(IMessage body, JsonTokenizer tokenizer)
+ {
+ var token = tokenizer.Next(); // Definitely start-object; checked in previous method
+ token = tokenizer.Next();
+ // TODO: What about an absent Int32Value, for example?
+ if (token.Type != JsonToken.TokenType.Name || token.StringValue != JsonFormatter.AnyWellKnownTypeValueField)
+ {
+ throw new InvalidProtocolBufferException($"Expected '{JsonFormatter.AnyWellKnownTypeValueField}' property for well-known type Any body");
+ }
+ Merge(body, tokenizer);
+ token = tokenizer.Next();
+ if (token.Type != JsonToken.TokenType.EndObject)
+ {
+ throw new InvalidProtocolBufferException($"Expected end-object token after @type/value for well-known type");
+ }
+ }
+
#region Utility methods which don't depend on the state (or settings) of the parser.
private static object ParseMapKey(FieldDescriptor field, string keyText)
{
@@ -431,17 +580,17 @@ namespace Google.Protobuf
case FieldType.Int32:
case FieldType.SInt32:
case FieldType.SFixed32:
- return ParseNumericString(keyText, int.Parse, false);
+ return ParseNumericString(keyText, int.Parse);
case FieldType.UInt32:
case FieldType.Fixed32:
- return ParseNumericString(keyText, uint.Parse, false);
+ return ParseNumericString(keyText, uint.Parse);
case FieldType.Int64:
case FieldType.SInt64:
case FieldType.SFixed64:
- return ParseNumericString(keyText, long.Parse, false);
+ return ParseNumericString(keyText, long.Parse);
case FieldType.UInt64:
case FieldType.Fixed64:
- return ParseNumericString(keyText, ulong.Parse, false);
+ return ParseNumericString(keyText, ulong.Parse);
default:
throw new InvalidProtocolBufferException("Invalid field type for map: " + field.FieldType);
}
@@ -452,7 +601,6 @@ namespace Google.Protobuf
double value = token.NumberValue;
checked
{
- // TODO: Validate that it's actually an integer, possibly in terms of the textual representation?
try
{
switch (field.FieldType)
@@ -460,16 +608,20 @@ namespace Google.Protobuf
case FieldType.Int32:
case FieldType.SInt32:
case FieldType.SFixed32:
+ CheckInteger(value);
return (int) value;
case FieldType.UInt32:
case FieldType.Fixed32:
+ CheckInteger(value);
return (uint) value;
case FieldType.Int64:
case FieldType.SInt64:
case FieldType.SFixed64:
+ CheckInteger(value);
return (long) value;
case FieldType.UInt64:
case FieldType.Fixed64:
+ CheckInteger(value);
return (ulong) value;
case FieldType.Double:
return value;
@@ -488,20 +640,37 @@ namespace Google.Protobuf
{
return float.NegativeInfinity;
}
- throw new InvalidProtocolBufferException("Value out of range: " + value);
+ throw new InvalidProtocolBufferException($"Value out of range: {value}");
}
return (float) value;
+ case FieldType.Enum:
+ CheckInteger(value);
+ // Just return it as an int, and let the CLR convert it.
+ // Note that we deliberately don't check that it's a known value.
+ return (int) value;
default:
- throw new InvalidProtocolBufferException("Unsupported conversion from JSON number for field type " + field.FieldType);
+ throw new InvalidProtocolBufferException($"Unsupported conversion from JSON number for field type {field.FieldType}");
}
}
catch (OverflowException)
{
- throw new InvalidProtocolBufferException("Value out of range: " + value);
+ throw new InvalidProtocolBufferException($"Value out of range: {value}");
}
}
}
+ private static void CheckInteger(double value)
+ {
+ if (double.IsInfinity(value) || double.IsNaN(value))
+ {
+ throw new InvalidProtocolBufferException($"Value not an integer: {value}");
+ }
+ if (value != Math.Floor(value))
+ {
+ throw new InvalidProtocolBufferException($"Value not an integer: {value}");
+ }
+ }
+
private static object ParseSingleStringValue(FieldDescriptor field, string text)
{
switch (field.FieldType)
@@ -509,47 +678,46 @@ namespace Google.Protobuf
case FieldType.String:
return text;
case FieldType.Bytes:
- return ByteString.FromBase64(text);
+ try
+ {
+ return ByteString.FromBase64(text);
+ }
+ catch (FormatException e)
+ {
+ throw InvalidProtocolBufferException.InvalidBase64(e);
+ }
case FieldType.Int32:
case FieldType.SInt32:
case FieldType.SFixed32:
- return ParseNumericString(text, int.Parse, false);
+ return ParseNumericString(text, int.Parse);
case FieldType.UInt32:
case FieldType.Fixed32:
- return ParseNumericString(text, uint.Parse, false);
+ return ParseNumericString(text, uint.Parse);
case FieldType.Int64:
case FieldType.SInt64:
case FieldType.SFixed64:
- return ParseNumericString(text, long.Parse, false);
+ return ParseNumericString(text, long.Parse);
case FieldType.UInt64:
case FieldType.Fixed64:
- return ParseNumericString(text, ulong.Parse, false);
+ return ParseNumericString(text, ulong.Parse);
case FieldType.Double:
- double d = ParseNumericString(text, double.Parse, true);
- // double.Parse can return +/- infinity on Mono for non-infinite values which are out of range for double.
- if (double.IsInfinity(d) && !text.Contains("Infinity"))
- {
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
- }
+ double d = ParseNumericString(text, double.Parse);
+ ValidateInfinityAndNan(text, double.IsPositiveInfinity(d), double.IsNegativeInfinity(d), double.IsNaN(d));
return d;
case FieldType.Float:
- float f = ParseNumericString(text, float.Parse, true);
- // float.Parse can return +/- infinity on Mono for non-infinite values which are out of range for float.
- if (float.IsInfinity(f) && !text.Contains("Infinity"))
- {
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
- }
+ float f = ParseNumericString(text, float.Parse);
+ ValidateInfinityAndNan(text, float.IsPositiveInfinity(f), float.IsNegativeInfinity(f), float.IsNaN(f));
return f;
case FieldType.Enum:
var enumValue = field.EnumType.FindValueByName(text);
if (enumValue == null)
{
- throw new InvalidProtocolBufferException("Invalid enum value: " + text + " for enum type: " + field.EnumType.FullName);
+ throw new InvalidProtocolBufferException($"Invalid enum value: {text} for enum type: {field.EnumType.FullName}");
}
// Just return it as an int, and let the CLR convert it.
return enumValue.Number;
default:
- throw new InvalidProtocolBufferException("Unsupported conversion from JSON string for field type " + field.FieldType);
+ throw new InvalidProtocolBufferException($"Unsupported conversion from JSON string for field type {field.FieldType}");
}
}
@@ -561,43 +729,53 @@ namespace Google.Protobuf
return field.MessageType.Parser.CreateTemplate();
}
- private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser, bool floatingPoint)
+ private static T ParseNumericString<T>(string text, Func<string, NumberStyles, IFormatProvider, T> parser)
{
- // TODO: Prohibit leading zeroes (but allow 0!)
- // TODO: Validate handling of "Infinity" etc. (Should be case sensitive, no leading whitespace etc)
// Can't prohibit this with NumberStyles.
if (text.StartsWith("+"))
{
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
}
if (text.StartsWith("0") && text.Length > 1)
{
if (text[1] >= '0' && text[1] <= '9')
{
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
}
}
else if (text.StartsWith("-0") && text.Length > 2)
{
if (text[2] >= '0' && text[2] <= '9')
{
- throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
}
}
try
{
- var styles = floatingPoint
- ? NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent
- : NumberStyles.AllowLeadingSign;
- return parser(text, styles, CultureInfo.InvariantCulture);
+ return parser(text, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
}
catch (FormatException)
{
- throw new InvalidProtocolBufferException("Invalid numeric value for type: " + text);
+ throw new InvalidProtocolBufferException($"Invalid numeric value for type: {text}");
}
catch (OverflowException)
{
- throw new InvalidProtocolBufferException("Value out of range: " + text);
+ throw new InvalidProtocolBufferException($"Value out of range: {text}");
+ }
+ }
+
+ /// <summary>
+ /// Checks that any infinite/NaN values originated from the correct text.
+ /// This corrects the lenient whitespace handling of double.Parse/float.Parse, as well as the
+ /// way that Mono parses out-of-range values as infinity.
+ /// </summary>
+ private static void ValidateInfinityAndNan(string text, bool isPositiveInfinity, bool isNegativeInfinity, bool isNaN)
+ {
+ if ((isPositiveInfinity && text != "Infinity") ||
+ (isNegativeInfinity && text != "-Infinity") ||
+ (isNaN && text != "NaN"))
+ {
+ throw new InvalidProtocolBufferException($"Invalid numeric value: {text}");
}
}
@@ -610,7 +788,7 @@ namespace Google.Protobuf
var match = TimestampRegex.Match(token.StringValue);
if (!match.Success)
{
- throw new InvalidProtocolBufferException("Invalid Timestamp value: " + token.StringValue);
+ throw new InvalidProtocolBufferException($"Invalid Timestamp value: {token.StringValue}");
}
var dateTime = match.Groups["datetime"].Value;
var subseconds = match.Groups["subseconds"].Value;
@@ -700,28 +878,24 @@ namespace Google.Protobuf
try
{
- long seconds = long.Parse(secondsText, CultureInfo.InvariantCulture);
+ long seconds = long.Parse(secondsText, CultureInfo.InvariantCulture) * multiplier;
int nanos = 0;
if (subseconds != "")
{
// This should always work, as we've got 1-9 digits.
int parsedFraction = int.Parse(subseconds.Substring(1));
- nanos = parsedFraction * SubsecondScalingFactors[subseconds.Length];
+ nanos = parsedFraction * SubsecondScalingFactors[subseconds.Length] * multiplier;
}
- if (seconds >= Duration.MaxSeconds)
+ if (!Duration.IsNormalized(seconds, nanos))
{
- // Allow precisely 315576000000 seconds, but prohibit even 1ns more.
- if (seconds > Duration.MaxSeconds || nanos > 0)
- {
- throw new InvalidProtocolBufferException("Invalid Duration value: " + token.StringValue);
- }
+ throw new InvalidProtocolBufferException($"Invalid Duration value: {token.StringValue}");
}
- message.Descriptor.Fields[Duration.SecondsFieldNumber].Accessor.SetValue(message, seconds * multiplier);
- message.Descriptor.Fields[Duration.NanosFieldNumber].Accessor.SetValue(message, nanos * multiplier);
+ message.Descriptor.Fields[Duration.SecondsFieldNumber].Accessor.SetValue(message, seconds);
+ message.Descriptor.Fields[Duration.NanosFieldNumber].Accessor.SetValue(message, nanos);
}
catch (FormatException)
{
- throw new InvalidProtocolBufferException("Invalid Duration value: " + token.StringValue);
+ throw new InvalidProtocolBufferException($"Invalid Duration value: {token.StringValue}");
}
}
@@ -744,6 +918,8 @@ namespace Google.Protobuf
private static string ToSnakeCase(string text)
{
var builder = new StringBuilder(text.Length * 2);
+ // Note: this is probably unnecessary now, but currently retained to be as close as possible to the
+ // C++, whilst still throwing an exception on underscores.
bool wasNotUnderscore = false; // Initialize to false for case 1 (below)
bool wasNotCap = false;
@@ -777,7 +953,11 @@ namespace Google.Protobuf
else
{
builder.Append(c);
- wasNotUnderscore = c != '_';
+ if (c == '_')
+ {
+ throw new InvalidProtocolBufferException($"Invalid field mask: {text}");
+ }
+ wasNotUnderscore = true;
wasNotCap = true;
}
}
@@ -790,29 +970,48 @@ namespace Google.Protobuf
/// </summary>
public sealed class Settings
{
- private static readonly Settings defaultInstance = new Settings(CodedInputStream.DefaultRecursionLimit);
-
- private readonly int recursionLimit;
-
/// <summary>
- /// Default settings, as used by <see cref="JsonParser.Default"/>
+ /// Default settings, as used by <see cref="JsonParser.Default"/>. This has the same default
+ /// recursion limit as <see cref="CodedInputStream"/>, and an empty type registry.
/// </summary>
- public static Settings Default { get { return defaultInstance; } }
+ public static Settings Default { get; }
+
+ // Workaround for the Mono compiler complaining about XML comments not being on
+ // valid language elements.
+ static Settings()
+ {
+ Default = new Settings(CodedInputStream.DefaultRecursionLimit);
+ }
/// <summary>
/// The maximum depth of messages to parse. Note that this limit only applies to parsing
/// messages, not collections - so a message within a collection within a message only counts as
/// depth 2, not 3.
/// </summary>
- public int RecursionLimit { get { return recursionLimit; } }
+ public int RecursionLimit { get; }
+
+ /// <summary>
+ /// The type registry used to parse <see cref="Any"/> messages.
+ /// </summary>
+ public TypeRegistry TypeRegistry { get; }
/// <summary>
/// Creates a new <see cref="Settings"/> object with the specified recursion limit.
/// </summary>
/// <param name="recursionLimit">The maximum depth of messages to parse</param>
- public Settings(int recursionLimit)
+ public Settings(int recursionLimit) : this(recursionLimit, TypeRegistry.Empty)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Settings"/> object with the specified recursion limit and type registry.
+ /// </summary>
+ /// <param name="recursionLimit">The maximum depth of messages to parse</param>
+ /// <param name="typeRegistry">The type registry used to parse <see cref="Any"/> messages</param>
+ public Settings(int recursionLimit, TypeRegistry typeRegistry)
{
- this.recursionLimit = recursionLimit;
+ RecursionLimit = recursionLimit;
+ TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nameof(typeRegistry));
}
}
}
diff --git a/csharp/src/Google.Protobuf/JsonTokenizer.cs b/csharp/src/Google.Protobuf/JsonTokenizer.cs
index 6589427a..09a6d43b 100644
--- a/csharp/src/Google.Protobuf/JsonTokenizer.cs
+++ b/csharp/src/Google.Protobuf/JsonTokenizer.cs
@@ -47,32 +47,38 @@ namespace Google.Protobuf
/// between values. It validates the token stream as it goes - so callers can assume that the
/// tokens it produces are appropriate. For example, it would never produce "start object, end array."
/// </para>
+ /// <para>Implementation details: the base class handles single token push-back and </para>
/// <para>Not thread-safe.</para>
/// </remarks>
- internal sealed class JsonTokenizer
+ internal abstract class JsonTokenizer
{
- // The set of states in which a value is valid next token.
- private static readonly State ValueStates = State.ArrayStart | State.ArrayAfterComma | State.ObjectAfterColon | State.StartOfDocument;
-
- private readonly Stack<ContainerType> containerStack = new Stack<ContainerType>();
- private readonly PushBackReader reader;
private JsonToken bufferedToken;
- private State state;
- private int objectDepth = 0;
/// <summary>
- /// Returns the depth of the stack, purely in objects (not collections).
- /// Informally, this is the number of remaining unclosed '{' characters we have.
+ /// Creates a tokenizer that reads from the given text reader.
/// </summary>
- internal int ObjectDepth { get { return objectDepth; } }
+ internal static JsonTokenizer FromTextReader(TextReader reader)
+ {
+ return new JsonTextTokenizer(reader);
+ }
- internal JsonTokenizer(TextReader reader)
+ /// <summary>
+ /// Creates a tokenizer that first replays the given list of tokens, then continues reading
+ /// from another tokenizer. Note that if the returned tokenizer is "pushed back", that does not push back
+ /// on the continuation tokenizer, or vice versa. Care should be taken when using this method - it was
+ /// created for the sake of Any parsing.
+ /// </summary>
+ internal static JsonTokenizer FromReplayedTokens(IList<JsonToken> tokens, JsonTokenizer continuation)
{
- this.reader = new PushBackReader(reader);
- state = State.StartOfDocument;
- containerStack.Push(ContainerType.Document);
+ return new JsonReplayTokenizer(tokens, continuation);
}
+ /// <summary>
+ /// Returns the depth of the stack, purely in objects (not collections).
+ /// Informally, this is the number of remaining unclosed '{' characters we have.
+ /// </summary>
+ internal int ObjectDepth { get; private set; }
+
// TODO: Why do we allow a different token to be pushed back? It might be better to always remember the previous
// token returned, and allow a parameterless Rewind() method (which could only be called once, just like the current PushBack).
internal void PushBack(JsonToken token)
@@ -84,11 +90,11 @@ namespace Google.Protobuf
bufferedToken = token;
if (token.Type == JsonToken.TokenType.StartObject)
{
- objectDepth--;
+ ObjectDepth--;
}
else if (token.Type == JsonToken.TokenType.EndObject)
{
- objectDepth++;
+ ObjectDepth++;
}
}
@@ -96,574 +102,636 @@ namespace Google.Protobuf
/// Returns the next JSON token in the stream. An EndDocument token is returned to indicate the end of the stream,
/// after which point <c>Next()</c> should not be called again.
/// </summary>
- /// <remarks>
- /// This method essentially just loops through characters skipping whitespace, validating and
- /// changing state (e.g. from ObjectBeforeColon to ObjectAfterColon)
- /// until it reaches something which will be a genuine token (e.g. a start object, or a value) at which point
- /// it returns the token. Although the method is large, it would be relatively hard to break down further... most
- /// of it is the large switch statement, which sometimes returns and sometimes doesn't.
- /// </remarks>
+ /// <remarks>This implementation provides single-token buffering, and calls <see cref="NextImpl"/> if there is no buffered token.</remarks>
/// <returns>The next token in the stream. This is never null.</returns>
/// <exception cref="InvalidOperationException">This method is called after an EndDocument token has been returned</exception>
/// <exception cref="InvalidJsonException">The input text does not comply with RFC 7159</exception>
internal JsonToken Next()
{
+ JsonToken tokenToReturn;
if (bufferedToken != null)
{
- var ret = bufferedToken;
+ tokenToReturn = bufferedToken;
bufferedToken = null;
- if (ret.Type == JsonToken.TokenType.StartObject)
- {
- objectDepth++;
- }
- else if (ret.Type == JsonToken.TokenType.EndObject)
- {
- objectDepth--;
- }
- return ret;
}
- if (state == State.ReaderExhausted)
+ else
{
- throw new InvalidOperationException("Next() called after end of document");
+ tokenToReturn = NextImpl();
}
- while (true)
+ if (tokenToReturn.Type == JsonToken.TokenType.StartObject)
{
- var next = reader.Read();
- if (next == null)
- {
- ValidateState(State.ExpectedEndOfDocument, "Unexpected end of document in state: ");
- state = State.ReaderExhausted;
- return JsonToken.EndDocument;
- }
- switch (next.Value)
- {
- // Skip whitespace between tokens
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- break;
- case ':':
- ValidateState(State.ObjectBeforeColon, "Invalid state to read a colon: ");
- state = State.ObjectAfterColon;
- break;
- case ',':
- ValidateState(State.ObjectAfterProperty | State.ArrayAfterValue, "Invalid state to read a colon: ");
- state = state == State.ObjectAfterProperty ? State.ObjectAfterComma : State.ArrayAfterComma;
- break;
- case '"':
- string stringValue = ReadString();
- if ((state & (State.ObjectStart | State.ObjectAfterComma)) != 0)
- {
- state = State.ObjectBeforeColon;
- return JsonToken.Name(stringValue);
- }
- else
- {
- ValidateAndModifyStateForValue("Invalid state to read a double quote: ");
- return JsonToken.Value(stringValue);
- }
- case '{':
- ValidateState(ValueStates, "Invalid state to read an open brace: ");
- state = State.ObjectStart;
- containerStack.Push(ContainerType.Object);
- objectDepth++;
- return JsonToken.StartObject;
- case '}':
- ValidateState(State.ObjectAfterProperty | State.ObjectStart, "Invalid state to read a close brace: ");
- PopContainer();
- objectDepth--;
- return JsonToken.EndObject;
- case '[':
- ValidateState(ValueStates, "Invalid state to read an open square bracket: ");
- state = State.ArrayStart;
- containerStack.Push(ContainerType.Array);
- return JsonToken.StartArray;
- case ']':
- ValidateState(State.ArrayAfterValue | State.ArrayStart, "Invalid state to read a close square bracket: ");
- PopContainer();
- return JsonToken.EndArray;
- case 'n': // Start of null
- ConsumeLiteral("null");
- ValidateAndModifyStateForValue("Invalid state to read a null literal: ");
- return JsonToken.Null;
- case 't': // Start of true
- ConsumeLiteral("true");
- ValidateAndModifyStateForValue("Invalid state to read a true literal: ");
- return JsonToken.True;
- case 'f': // Start of false
- ConsumeLiteral("false");
- ValidateAndModifyStateForValue("Invalid state to read a false literal: ");
- return JsonToken.False;
- case '-': // Start of a number
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- double number = ReadNumber(next.Value);
- ValidateAndModifyStateForValue("Invalid state to read a number token: ");
- return JsonToken.Value(number);
- default:
- throw new InvalidJsonException("Invalid first character of token: " + next.Value);
- }
+ ObjectDepth++;
}
- }
-
- private void ValidateState(State validStates, string errorPrefix)
- {
- if ((validStates & state) == 0)
+ else if (tokenToReturn.Type == JsonToken.TokenType.EndObject)
{
- throw reader.CreateException(errorPrefix + state);
+ ObjectDepth--;
}
+ return tokenToReturn;
}
/// <summary>
- /// Reads a string token. It is assumed that the opening " has already been read.
+ /// Returns the next JSON token in the stream, when requested by the base class. (The <see cref="Next"/> method delegates
+ /// to this if it doesn't have a buffered token.)
/// </summary>
- private string ReadString()
- {
- var value = new StringBuilder();
- bool haveHighSurrogate = false;
- while (true)
- {
- char c = reader.ReadOrFail("Unexpected end of text while reading string");
- if (c < ' ')
- {
- throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in string literal: U+{0:x4}", (int) c));
- }
- if (c == '"')
- {
- if (haveHighSurrogate)
- {
- throw reader.CreateException("Invalid use of surrogate pair code units");
- }
- return value.ToString();
- }
- if (c == '\\')
- {
- c = ReadEscapedCharacter();
- }
- // TODO: Consider only allowing surrogate pairs that are either both escaped,
- // or both not escaped. It would be a very odd text stream that contained a "lone" high surrogate
- // followed by an escaped low surrogate or vice versa... and that couldn't even be represented in UTF-8.
- if (haveHighSurrogate != char.IsLowSurrogate(c))
- {
- throw reader.CreateException("Invalid use of surrogate pair code units");
- }
- haveHighSurrogate = char.IsHighSurrogate(c);
- value.Append(c);
- }
- }
+ /// <exception cref="InvalidOperationException">This method is called after an EndDocument token has been returned</exception>
+ /// <exception cref="InvalidJsonException">The input text does not comply with RFC 7159</exception>
+ protected abstract JsonToken NextImpl();
/// <summary>
- /// Reads an escaped character. It is assumed that the leading backslash has already been read.
+ /// Tokenizer which first exhausts a list of tokens, then consults another tokenizer.
/// </summary>
- private char ReadEscapedCharacter()
+ private class JsonReplayTokenizer : JsonTokenizer
{
- char c = reader.ReadOrFail("Unexpected end of text while reading character escape sequence");
- switch (c)
+ private readonly IList<JsonToken> tokens;
+ private readonly JsonTokenizer nextTokenizer;
+ private int nextTokenIndex;
+
+ internal JsonReplayTokenizer(IList<JsonToken> tokens, JsonTokenizer nextTokenizer)
{
- case 'n':
- return '\n';
- case '\\':
- return '\\';
- case 'b':
- return '\b';
- case 'f':
- return '\f';
- case 'r':
- return '\r';
- case 't':
- return '\t';
- case '"':
- return '"';
- case '/':
- return '/';
- case 'u':
- return ReadUnicodeEscape();
- default:
- throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c));
+ this.tokens = tokens;
+ this.nextTokenizer = nextTokenizer;
}
- }
- /// <summary>
- /// Reads an escaped Unicode 4-nybble hex sequence. It is assumed that the leading \u has already been read.
- /// </summary>
- private char ReadUnicodeEscape()
- {
- int result = 0;
- for (int i = 0; i < 4; i++)
+ // FIXME: Object depth not maintained...
+ protected override JsonToken NextImpl()
{
- char c = reader.ReadOrFail("Unexpected end of text while reading Unicode escape sequence");
- int nybble;
- if (c >= '0' && c <= '9')
+ if (nextTokenIndex >= tokens.Count)
{
- nybble = c - '0';
+ return nextTokenizer.Next();
}
- else if (c >= 'a' && c <= 'f')
- {
- nybble = c - 'a' + 10;
- }
- else if (c >= 'A' && c <= 'F')
- {
- nybble = c - 'A' + 10;
- }
- else
- {
- throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c));
- }
- result = (result << 4) + nybble;
+ return tokens[nextTokenIndex++];
}
- return (char) result;
}
/// <summary>
- /// Consumes a text-only literal, throwing an exception if the read text doesn't match it.
- /// It is assumed that the first letter of the literal has already been read.
+ /// Tokenizer which does all the *real* work of parsing JSON.
/// </summary>
- private void ConsumeLiteral(string text)
+ private sealed class JsonTextTokenizer : JsonTokenizer
{
- for (int i = 1; i < text.Length; i++)
+ // The set of states in which a value is valid next token.
+ private static readonly State ValueStates = State.ArrayStart | State.ArrayAfterComma | State.ObjectAfterColon | State.StartOfDocument;
+
+ private readonly Stack<ContainerType> containerStack = new Stack<ContainerType>();
+ private readonly PushBackReader reader;
+ private State state;
+
+ internal JsonTextTokenizer(TextReader reader)
{
- char? next = reader.Read();
- if (next == null)
+ this.reader = new PushBackReader(reader);
+ state = State.StartOfDocument;
+ containerStack.Push(ContainerType.Document);
+ }
+
+ /// <remarks>
+ /// This method essentially just loops through characters skipping whitespace, validating and
+ /// changing state (e.g. from ObjectBeforeColon to ObjectAfterColon)
+ /// until it reaches something which will be a genuine token (e.g. a start object, or a value) at which point
+ /// it returns the token. Although the method is large, it would be relatively hard to break down further... most
+ /// of it is the large switch statement, which sometimes returns and sometimes doesn't.
+ /// </remarks>
+ protected override JsonToken NextImpl()
+ {
+ if (state == State.ReaderExhausted)
{
- throw reader.CreateException("Unexpected end of text while reading literal token " + text);
+ throw new InvalidOperationException("Next() called after end of document");
}
- if (next.Value != text[i])
+ while (true)
{
- throw reader.CreateException("Unexpected character while reading literal token " + text);
+ var next = reader.Read();
+ if (next == null)
+ {
+ ValidateState(State.ExpectedEndOfDocument, "Unexpected end of document in state: ");
+ state = State.ReaderExhausted;
+ return JsonToken.EndDocument;
+ }
+ switch (next.Value)
+ {
+ // Skip whitespace between tokens
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ break;
+ case ':':
+ ValidateState(State.ObjectBeforeColon, "Invalid state to read a colon: ");
+ state = State.ObjectAfterColon;
+ break;
+ case ',':
+ ValidateState(State.ObjectAfterProperty | State.ArrayAfterValue, "Invalid state to read a colon: ");
+ state = state == State.ObjectAfterProperty ? State.ObjectAfterComma : State.ArrayAfterComma;
+ break;
+ case '"':
+ string stringValue = ReadString();
+ if ((state & (State.ObjectStart | State.ObjectAfterComma)) != 0)
+ {
+ state = State.ObjectBeforeColon;
+ return JsonToken.Name(stringValue);
+ }
+ else
+ {
+ ValidateAndModifyStateForValue("Invalid state to read a double quote: ");
+ return JsonToken.Value(stringValue);
+ }
+ case '{':
+ ValidateState(ValueStates, "Invalid state to read an open brace: ");
+ state = State.ObjectStart;
+ containerStack.Push(ContainerType.Object);
+ return JsonToken.StartObject;
+ case '}':
+ ValidateState(State.ObjectAfterProperty | State.ObjectStart, "Invalid state to read a close brace: ");
+ PopContainer();
+ return JsonToken.EndObject;
+ case '[':
+ ValidateState(ValueStates, "Invalid state to read an open square bracket: ");
+ state = State.ArrayStart;
+ containerStack.Push(ContainerType.Array);
+ return JsonToken.StartArray;
+ case ']':
+ ValidateState(State.ArrayAfterValue | State.ArrayStart, "Invalid state to read a close square bracket: ");
+ PopContainer();
+ return JsonToken.EndArray;
+ case 'n': // Start of null
+ ConsumeLiteral("null");
+ ValidateAndModifyStateForValue("Invalid state to read a null literal: ");
+ return JsonToken.Null;
+ case 't': // Start of true
+ ConsumeLiteral("true");
+ ValidateAndModifyStateForValue("Invalid state to read a true literal: ");
+ return JsonToken.True;
+ case 'f': // Start of false
+ ConsumeLiteral("false");
+ ValidateAndModifyStateForValue("Invalid state to read a false literal: ");
+ return JsonToken.False;
+ case '-': // Start of a number
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ double number = ReadNumber(next.Value);
+ ValidateAndModifyStateForValue("Invalid state to read a number token: ");
+ return JsonToken.Value(number);
+ default:
+ throw new InvalidJsonException("Invalid first character of token: " + next.Value);
+ }
}
}
- }
- private double ReadNumber(char initialCharacter)
- {
- StringBuilder builder = new StringBuilder();
- if (initialCharacter == '-')
- {
- builder.Append("-");
- }
- else
- {
- reader.PushBack(initialCharacter);
- }
- // Each method returns the character it read that doesn't belong in that part,
- // so we know what to do next, including pushing the character back at the end.
- // null is returned for "end of text".
- char? next = ReadInt(builder);
- if (next == '.')
+ private void ValidateState(State validStates, string errorPrefix)
{
- next = ReadFrac(builder);
- }
- if (next == 'e' || next == 'E')
- {
- next = ReadExp(builder);
- }
- // If we read a character which wasn't part of the number, push it back so we can read it again
- // to parse the next token.
- if (next != null)
- {
- reader.PushBack(next.Value);
+ if ((validStates & state) == 0)
+ {
+ throw reader.CreateException(errorPrefix + state);
+ }
}
- // TODO: What exception should we throw if the value can't be represented as a double?
- try
- {
- return double.Parse(builder.ToString(),
- NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent,
- CultureInfo.InvariantCulture);
- }
- catch (OverflowException)
+ /// <summary>
+ /// Reads a string token. It is assumed that the opening " has already been read.
+ /// </summary>
+ private string ReadString()
{
- throw reader.CreateException("Numeric value out of range: " + builder);
+ var value = new StringBuilder();
+ bool haveHighSurrogate = false;
+ while (true)
+ {
+ char c = reader.ReadOrFail("Unexpected end of text while reading string");
+ if (c < ' ')
+ {
+ throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in string literal: U+{0:x4}", (int) c));
+ }
+ if (c == '"')
+ {
+ if (haveHighSurrogate)
+ {
+ throw reader.CreateException("Invalid use of surrogate pair code units");
+ }
+ return value.ToString();
+ }
+ if (c == '\\')
+ {
+ c = ReadEscapedCharacter();
+ }
+ // TODO: Consider only allowing surrogate pairs that are either both escaped,
+ // or both not escaped. It would be a very odd text stream that contained a "lone" high surrogate
+ // followed by an escaped low surrogate or vice versa... and that couldn't even be represented in UTF-8.
+ if (haveHighSurrogate != char.IsLowSurrogate(c))
+ {
+ throw reader.CreateException("Invalid use of surrogate pair code units");
+ }
+ haveHighSurrogate = char.IsHighSurrogate(c);
+ value.Append(c);
+ }
}
- }
- private char? ReadInt(StringBuilder builder)
- {
- char first = reader.ReadOrFail("Invalid numeric literal");
- if (first < '0' || first > '9')
- {
- throw reader.CreateException("Invalid numeric literal");
- }
- builder.Append(first);
- int digitCount;
- char? next = ConsumeDigits(builder, out digitCount);
- if (first == '0' && digitCount != 0)
+ /// <summary>
+ /// Reads an escaped character. It is assumed that the leading backslash has already been read.
+ /// </summary>
+ private char ReadEscapedCharacter()
{
- throw reader.CreateException("Invalid numeric literal: leading 0 for non-zero value.");
+ char c = reader.ReadOrFail("Unexpected end of text while reading character escape sequence");
+ switch (c)
+ {
+ case 'n':
+ return '\n';
+ case '\\':
+ return '\\';
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case '"':
+ return '"';
+ case '/':
+ return '/';
+ case 'u':
+ return ReadUnicodeEscape();
+ default:
+ throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c));
+ }
}
- return next;
- }
- private char? ReadFrac(StringBuilder builder)
- {
- builder.Append('.'); // Already consumed this
- int digitCount;
- char? next = ConsumeDigits(builder, out digitCount);
- if (digitCount == 0)
+ /// <summary>
+ /// Reads an escaped Unicode 4-nybble hex sequence. It is assumed that the leading \u has already been read.
+ /// </summary>
+ private char ReadUnicodeEscape()
{
- throw reader.CreateException("Invalid numeric literal: fraction with no trailing digits");
+ int result = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ char c = reader.ReadOrFail("Unexpected end of text while reading Unicode escape sequence");
+ int nybble;
+ if (c >= '0' && c <= '9')
+ {
+ nybble = c - '0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ nybble = c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ nybble = c - 'A' + 10;
+ }
+ else
+ {
+ throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c));
+ }
+ result = (result << 4) + nybble;
+ }
+ return (char) result;
}
- return next;
- }
- private char? ReadExp(StringBuilder builder)
- {
- builder.Append('E'); // Already consumed this (or 'e')
- char? next = reader.Read();
- if (next == null)
+ /// <summary>
+ /// Consumes a text-only literal, throwing an exception if the read text doesn't match it.
+ /// It is assumed that the first letter of the literal has already been read.
+ /// </summary>
+ private void ConsumeLiteral(string text)
{
- throw reader.CreateException("Invalid numeric literal: exponent with no trailing digits");
+ for (int i = 1; i < text.Length; i++)
+ {
+ char? next = reader.Read();
+ if (next == null)
+ {
+ throw reader.CreateException("Unexpected end of text while reading literal token " + text);
+ }
+ if (next.Value != text[i])
+ {
+ throw reader.CreateException("Unexpected character while reading literal token " + text);
+ }
+ }
}
- if (next == '-' || next == '+')
+
+ private double ReadNumber(char initialCharacter)
{
- builder.Append(next.Value);
+ StringBuilder builder = new StringBuilder();
+ if (initialCharacter == '-')
+ {
+ builder.Append("-");
+ }
+ else
+ {
+ reader.PushBack(initialCharacter);
+ }
+ // Each method returns the character it read that doesn't belong in that part,
+ // so we know what to do next, including pushing the character back at the end.
+ // null is returned for "end of text".
+ char? next = ReadInt(builder);
+ if (next == '.')
+ {
+ next = ReadFrac(builder);
+ }
+ if (next == 'e' || next == 'E')
+ {
+ next = ReadExp(builder);
+ }
+ // If we read a character which wasn't part of the number, push it back so we can read it again
+ // to parse the next token.
+ if (next != null)
+ {
+ reader.PushBack(next.Value);
+ }
+
+ // TODO: What exception should we throw if the value can't be represented as a double?
+ try
+ {
+ return double.Parse(builder.ToString(),
+ NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent,
+ CultureInfo.InvariantCulture);
+ }
+ catch (OverflowException)
+ {
+ throw reader.CreateException("Numeric value out of range: " + builder);
+ }
}
- else
+
+ private char? ReadInt(StringBuilder builder)
{
- reader.PushBack(next.Value);
+ char first = reader.ReadOrFail("Invalid numeric literal");
+ if (first < '0' || first > '9')
+ {
+ throw reader.CreateException("Invalid numeric literal");
+ }
+ builder.Append(first);
+ int digitCount;
+ char? next = ConsumeDigits(builder, out digitCount);
+ if (first == '0' && digitCount != 0)
+ {
+ throw reader.CreateException("Invalid numeric literal: leading 0 for non-zero value.");
+ }
+ return next;
}
- int digitCount;
- next = ConsumeDigits(builder, out digitCount);
- if (digitCount == 0)
+
+ private char? ReadFrac(StringBuilder builder)
{
- throw reader.CreateException("Invalid numeric literal: exponent without value");
+ builder.Append('.'); // Already consumed this
+ int digitCount;
+ char? next = ConsumeDigits(builder, out digitCount);
+ if (digitCount == 0)
+ {
+ throw reader.CreateException("Invalid numeric literal: fraction with no trailing digits");
+ }
+ return next;
}
- return next;
- }
- private char? ConsumeDigits(StringBuilder builder, out int count)
- {
- count = 0;
- while (true)
+ private char? ReadExp(StringBuilder builder)
{
+ builder.Append('E'); // Already consumed this (or 'e')
char? next = reader.Read();
- if (next == null || next.Value < '0' || next.Value > '9')
+ if (next == null)
+ {
+ throw reader.CreateException("Invalid numeric literal: exponent with no trailing digits");
+ }
+ if (next == '-' || next == '+')
{
- return next;
+ builder.Append(next.Value);
}
- count++;
- builder.Append(next.Value);
+ else
+ {
+ reader.PushBack(next.Value);
+ }
+ int digitCount;
+ next = ConsumeDigits(builder, out digitCount);
+ if (digitCount == 0)
+ {
+ throw reader.CreateException("Invalid numeric literal: exponent without value");
+ }
+ return next;
}
- }
- /// <summary>
- /// Validates that we're in a valid state to read a value (using the given error prefix if necessary)
- /// and changes the state to the appropriate one, e.g. ObjectAfterColon to ObjectAfterProperty.
- /// </summary>
- private void ValidateAndModifyStateForValue(string errorPrefix)
- {
- ValidateState(ValueStates, errorPrefix);
- switch (state)
+ private char? ConsumeDigits(StringBuilder builder, out int count)
{
- case State.StartOfDocument:
- state = State.ExpectedEndOfDocument;
- return;
- case State.ObjectAfterColon:
- state = State.ObjectAfterProperty;
- return;
- case State.ArrayStart:
- case State.ArrayAfterComma:
- state = State.ArrayAfterValue;
- return;
- default:
- throw new InvalidOperationException("ValidateAndModifyStateForValue does not handle all value states (and should)");
+ count = 0;
+ while (true)
+ {
+ char? next = reader.Read();
+ if (next == null || next.Value < '0' || next.Value > '9')
+ {
+ return next;
+ }
+ count++;
+ builder.Append(next.Value);
+ }
}
- }
- /// <summary>
- /// Pops the top-most container, and sets the state to the appropriate one for the end of a value
- /// in the parent container.
- /// </summary>
- private void PopContainer()
- {
- containerStack.Pop();
- var parent = containerStack.Peek();
- switch (parent)
+ /// <summary>
+ /// Validates that we're in a valid state to read a value (using the given error prefix if necessary)
+ /// and changes the state to the appropriate one, e.g. ObjectAfterColon to ObjectAfterProperty.
+ /// </summary>
+ private void ValidateAndModifyStateForValue(string errorPrefix)
{
- case ContainerType.Object:
- state = State.ObjectAfterProperty;
- break;
- case ContainerType.Array:
- state = State.ArrayAfterValue;
- break;
- case ContainerType.Document:
- state = State.ExpectedEndOfDocument;
- break;
- default:
- throw new InvalidOperationException("Unexpected container type: " + parent);
+ ValidateState(ValueStates, errorPrefix);
+ switch (state)
+ {
+ case State.StartOfDocument:
+ state = State.ExpectedEndOfDocument;
+ return;
+ case State.ObjectAfterColon:
+ state = State.ObjectAfterProperty;
+ return;
+ case State.ArrayStart:
+ case State.ArrayAfterComma:
+ state = State.ArrayAfterValue;
+ return;
+ default:
+ throw new InvalidOperationException("ValidateAndModifyStateForValue does not handle all value states (and should)");
+ }
}
- }
- private enum ContainerType
- {
- Document, Object, Array
- }
-
- /// <summary>
- /// Possible states of the tokenizer.
- /// </summary>
- /// <remarks>
- /// <para>This is a flags enum purely so we can simply and efficiently represent a set of valid states
- /// for checking.</para>
- /// <para>
- /// Each is documented with an example,
- /// where ^ represents the current position within the text stream. The examples all use string values,
- /// but could be any value, including nested objects/arrays.
- /// The complete state of the tokenizer also includes a stack to indicate the contexts (arrays/objects).
- /// Any additional notional state of "AfterValue" indicates that a value has been completed, at which
- /// point there's an immediate transition to ExpectedEndOfDocument, ObjectAfterProperty or ArrayAfterValue.
- /// </para>
- /// <para>
- /// These states were derived manually by reading RFC 7159 carefully.
- /// </para>
- /// </remarks>
- [Flags]
- private enum State
- {
- /// <summary>
- /// ^ { "foo": "bar" }
- /// Before the value in a document. Next states: ObjectStart, ArrayStart, "AfterValue"
- /// </summary>
- StartOfDocument = 1 << 0,
- /// <summary>
- /// { "foo": "bar" } ^
- /// After the value in a document. Next states: ReaderExhausted
- /// </summary>
- ExpectedEndOfDocument = 1 << 1,
- /// <summary>
- /// { "foo": "bar" } ^ (and already read to the end of the reader)
- /// Terminal state.
- /// </summary>
- ReaderExhausted = 1 << 2,
- /// <summary>
- /// { ^ "foo": "bar" }
- /// Before the *first* property in an object.
- /// Next states:
- /// "AfterValue" (empty object)
- /// ObjectBeforeColon (read a name)
- /// </summary>
- ObjectStart = 1 << 3,
- /// <summary>
- /// { "foo" ^ : "bar", "x": "y" }
- /// Next state: ObjectAfterColon
- /// </summary>
- ObjectBeforeColon = 1 << 4,
- /// <summary>
- /// { "foo" : ^ "bar", "x": "y" }
- /// Before any property other than the first in an object.
- /// (Equivalently: after any property in an object)
- /// Next states:
- /// "AfterValue" (value is simple)
- /// ObjectStart (value is object)
- /// ArrayStart (value is array)
- /// </summary>
- ObjectAfterColon = 1 << 5,
- /// <summary>
- /// { "foo" : "bar" ^ , "x" : "y" }
- /// At the end of a property, so expecting either a comma or end-of-object
- /// Next states: ObjectAfterComma or "AfterValue"
- /// </summary>
- ObjectAfterProperty = 1 << 6,
- /// <summary>
- /// { "foo":"bar", ^ "x":"y" }
- /// Read the comma after the previous property, so expecting another property.
- /// This is like ObjectStart, but closing brace isn't valid here
- /// Next state: ObjectBeforeColon.
- /// </summary>
- ObjectAfterComma = 1 << 7,
- /// <summary>
- /// [ ^ "foo", "bar" ]
- /// Before the *first* value in an array.
- /// Next states:
- /// "AfterValue" (read a value)
- /// "AfterValue" (end of array; will pop stack)
- /// </summary>
- ArrayStart = 1 << 8,
- /// <summary>
- /// [ "foo" ^ , "bar" ]
- /// After any value in an array, so expecting either a comma or end-of-array
- /// Next states: ArrayAfterComma or "AfterValue"
- /// </summary>
- ArrayAfterValue = 1 << 9,
/// <summary>
- /// [ "foo", ^ "bar" ]
- /// After a comma in an array, so there *must* be another value (simple or complex).
- /// Next states: "AfterValue" (simple value), StartObject, StartArray
+ /// Pops the top-most container, and sets the state to the appropriate one for the end of a value
+ /// in the parent container.
/// </summary>
- ArrayAfterComma = 1 << 10
- }
-
- /// <summary>
- /// Wrapper around a text reader allowing small amounts of buffering and location handling.
- /// </summary>
- private class PushBackReader
- {
- // TODO: Add locations for errors etc.
-
- private readonly TextReader reader;
+ private void PopContainer()
+ {
+ containerStack.Pop();
+ var parent = containerStack.Peek();
+ switch (parent)
+ {
+ case ContainerType.Object:
+ state = State.ObjectAfterProperty;
+ break;
+ case ContainerType.Array:
+ state = State.ArrayAfterValue;
+ break;
+ case ContainerType.Document:
+ state = State.ExpectedEndOfDocument;
+ break;
+ default:
+ throw new InvalidOperationException("Unexpected container type: " + parent);
+ }
+ }
- internal PushBackReader(TextReader reader)
+ private enum ContainerType
{
- // TODO: Wrap the reader in a BufferedReader?
- this.reader = reader;
+ Document, Object, Array
}
/// <summary>
- /// The buffered next character, if we have one.
+ /// Possible states of the tokenizer.
/// </summary>
- private char? nextChar;
+ /// <remarks>
+ /// <para>This is a flags enum purely so we can simply and efficiently represent a set of valid states
+ /// for checking.</para>
+ /// <para>
+ /// Each is documented with an example,
+ /// where ^ represents the current position within the text stream. The examples all use string values,
+ /// but could be any value, including nested objects/arrays.
+ /// The complete state of the tokenizer also includes a stack to indicate the contexts (arrays/objects).
+ /// Any additional notional state of "AfterValue" indicates that a value has been completed, at which
+ /// point there's an immediate transition to ExpectedEndOfDocument, ObjectAfterProperty or ArrayAfterValue.
+ /// </para>
+ /// <para>
+ /// These states were derived manually by reading RFC 7159 carefully.
+ /// </para>
+ /// </remarks>
+ [Flags]
+ private enum State
+ {
+ /// <summary>
+ /// ^ { "foo": "bar" }
+ /// Before the value in a document. Next states: ObjectStart, ArrayStart, "AfterValue"
+ /// </summary>
+ StartOfDocument = 1 << 0,
+ /// <summary>
+ /// { "foo": "bar" } ^
+ /// After the value in a document. Next states: ReaderExhausted
+ /// </summary>
+ ExpectedEndOfDocument = 1 << 1,
+ /// <summary>
+ /// { "foo": "bar" } ^ (and already read to the end of the reader)
+ /// Terminal state.
+ /// </summary>
+ ReaderExhausted = 1 << 2,
+ /// <summary>
+ /// { ^ "foo": "bar" }
+ /// Before the *first* property in an object.
+ /// Next states:
+ /// "AfterValue" (empty object)
+ /// ObjectBeforeColon (read a name)
+ /// </summary>
+ ObjectStart = 1 << 3,
+ /// <summary>
+ /// { "foo" ^ : "bar", "x": "y" }
+ /// Next state: ObjectAfterColon
+ /// </summary>
+ ObjectBeforeColon = 1 << 4,
+ /// <summary>
+ /// { "foo" : ^ "bar", "x": "y" }
+ /// Before any property other than the first in an object.
+ /// (Equivalently: after any property in an object)
+ /// Next states:
+ /// "AfterValue" (value is simple)
+ /// ObjectStart (value is object)
+ /// ArrayStart (value is array)
+ /// </summary>
+ ObjectAfterColon = 1 << 5,
+ /// <summary>
+ /// { "foo" : "bar" ^ , "x" : "y" }
+ /// At the end of a property, so expecting either a comma or end-of-object
+ /// Next states: ObjectAfterComma or "AfterValue"
+ /// </summary>
+ ObjectAfterProperty = 1 << 6,
+ /// <summary>
+ /// { "foo":"bar", ^ "x":"y" }
+ /// Read the comma after the previous property, so expecting another property.
+ /// This is like ObjectStart, but closing brace isn't valid here
+ /// Next state: ObjectBeforeColon.
+ /// </summary>
+ ObjectAfterComma = 1 << 7,
+ /// <summary>
+ /// [ ^ "foo", "bar" ]
+ /// Before the *first* value in an array.
+ /// Next states:
+ /// "AfterValue" (read a value)
+ /// "AfterValue" (end of array; will pop stack)
+ /// </summary>
+ ArrayStart = 1 << 8,
+ /// <summary>
+ /// [ "foo" ^ , "bar" ]
+ /// After any value in an array, so expecting either a comma or end-of-array
+ /// Next states: ArrayAfterComma or "AfterValue"
+ /// </summary>
+ ArrayAfterValue = 1 << 9,
+ /// <summary>
+ /// [ "foo", ^ "bar" ]
+ /// After a comma in an array, so there *must* be another value (simple or complex).
+ /// Next states: "AfterValue" (simple value), StartObject, StartArray
+ /// </summary>
+ ArrayAfterComma = 1 << 10
+ }
/// <summary>
- /// Returns the next character in the stream, or null if we have reached the end.
+ /// Wrapper around a text reader allowing small amounts of buffering and location handling.
/// </summary>
- /// <returns></returns>
- internal char? Read()
+ private class PushBackReader
{
- if (nextChar != null)
+ // TODO: Add locations for errors etc.
+
+ private readonly TextReader reader;
+
+ internal PushBackReader(TextReader reader)
{
- char? tmp = nextChar;
- nextChar = null;
- return tmp;
+ // TODO: Wrap the reader in a BufferedReader?
+ this.reader = reader;
}
- int next = reader.Read();
- return next == -1 ? null : (char?) next;
- }
- internal char ReadOrFail(string messageOnFailure)
- {
- char? next = Read();
- if (next == null)
+ /// <summary>
+ /// The buffered next character, if we have one.
+ /// </summary>
+ private char? nextChar;
+
+ /// <summary>
+ /// Returns the next character in the stream, or null if we have reached the end.
+ /// </summary>
+ /// <returns></returns>
+ internal char? Read()
{
- throw CreateException(messageOnFailure);
+ if (nextChar != null)
+ {
+ char? tmp = nextChar;
+ nextChar = null;
+ return tmp;
+ }
+ int next = reader.Read();
+ return next == -1 ? null : (char?) next;
}
- return next.Value;
- }
- internal void PushBack(char c)
- {
- if (nextChar != null)
+ internal char ReadOrFail(string messageOnFailure)
{
- throw new InvalidOperationException("Cannot push back when already buffering a character");
+ char? next = Read();
+ if (next == null)
+ {
+ throw CreateException(messageOnFailure);
+ }
+ return next.Value;
}
- nextChar = c;
- }
- /// <summary>
- /// Creates a new exception appropriate for the current state of the reader.
- /// </summary>
- internal InvalidJsonException CreateException(string message)
- {
- // TODO: Keep track of and use the location.
- return new InvalidJsonException(message);
+ internal void PushBack(char c)
+ {
+ if (nextChar != null)
+ {
+ throw new InvalidOperationException("Cannot push back when already buffering a character");
+ }
+ nextChar = c;
+ }
+
+ /// <summary>
+ /// Creates a new exception appropriate for the current state of the reader.
+ /// </summary>
+ internal InvalidJsonException CreateException(string message)
+ {
+ // TODO: Keep track of and use the location.
+ return new InvalidJsonException(message);
+ }
}
}
}
diff --git a/csharp/src/Google.Protobuf/MessageExtensions.cs b/csharp/src/Google.Protobuf/MessageExtensions.cs
index d2d057c0..047156c3 100644
--- a/csharp/src/Google.Protobuf/MessageExtensions.cs
+++ b/csharp/src/Google.Protobuf/MessageExtensions.cs
@@ -46,8 +46,8 @@ namespace Google.Protobuf
/// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeFrom(this IMessage message, byte[] data)
{
- Preconditions.CheckNotNull(message, "message");
- Preconditions.CheckNotNull(data, "data");
+ ProtoPreconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(data, "data");
CodedInputStream input = new CodedInputStream(data);
message.MergeFrom(input);
input.CheckReadEndOfStreamTag();
@@ -60,8 +60,8 @@ namespace Google.Protobuf
/// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeFrom(this IMessage message, ByteString data)
{
- Preconditions.CheckNotNull(message, "message");
- Preconditions.CheckNotNull(data, "data");
+ ProtoPreconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(data, "data");
CodedInputStream input = data.CreateCodedInput();
message.MergeFrom(input);
input.CheckReadEndOfStreamTag();
@@ -74,8 +74,8 @@ namespace Google.Protobuf
/// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeFrom(this IMessage message, Stream input)
{
- Preconditions.CheckNotNull(message, "message");
- Preconditions.CheckNotNull(input, "input");
+ ProtoPreconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(input, "input");
CodedInputStream codedInput = new CodedInputStream(input);
message.MergeFrom(codedInput);
codedInput.CheckReadEndOfStreamTag();
@@ -92,8 +92,8 @@ namespace Google.Protobuf
/// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
public static void MergeDelimitedFrom(this IMessage message, Stream input)
{
- Preconditions.CheckNotNull(message, "message");
- Preconditions.CheckNotNull(input, "input");
+ ProtoPreconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(input, "input");
int size = (int) CodedInputStream.ReadRawVarint32(input);
Stream limitedStream = new LimitedInputStream(input, size);
message.MergeFrom(limitedStream);
@@ -106,7 +106,7 @@ namespace Google.Protobuf
/// <returns>The message data as a byte array.</returns>
public static byte[] ToByteArray(this IMessage message)
{
- Preconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(message, "message");
byte[] result = new byte[message.CalculateSize()];
CodedOutputStream output = new CodedOutputStream(result);
message.WriteTo(output);
@@ -121,8 +121,8 @@ namespace Google.Protobuf
/// <param name="output">The stream to write to.</param>
public static void WriteTo(this IMessage message, Stream output)
{
- Preconditions.CheckNotNull(message, "message");
- Preconditions.CheckNotNull(output, "output");
+ ProtoPreconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(output, "output");
CodedOutputStream codedOutput = new CodedOutputStream(output);
message.WriteTo(codedOutput);
codedOutput.Flush();
@@ -135,8 +135,8 @@ namespace Google.Protobuf
/// <param name="output">The output stream to write to.</param>
public static void WriteDelimitedTo(this IMessage message, Stream output)
{
- Preconditions.CheckNotNull(message, "message");
- Preconditions.CheckNotNull(output, "output");
+ ProtoPreconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(output, "output");
CodedOutputStream codedOutput = new CodedOutputStream(output);
codedOutput.WriteRawVarint32((uint)message.CalculateSize());
message.WriteTo(codedOutput);
@@ -150,7 +150,7 @@ namespace Google.Protobuf
/// <returns>The message data as a byte string.</returns>
public static ByteString ToByteString(this IMessage message)
{
- Preconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(message, "message");
return ByteString.AttachBytes(message.ToByteArray());
}
}
diff --git a/csharp/src/Google.Protobuf/MessageParser.cs b/csharp/src/Google.Protobuf/MessageParser.cs
index 81be3c2a..8889638b 100644
--- a/csharp/src/Google.Protobuf/MessageParser.cs
+++ b/csharp/src/Google.Protobuf/MessageParser.cs
@@ -64,7 +64,7 @@ namespace Google.Protobuf
/// <returns>The newly parsed message.</returns>
public IMessage ParseFrom(byte[] data)
{
- Preconditions.CheckNotNull(data, "data");
+ ProtoPreconditions.CheckNotNull(data, "data");
IMessage message = factory();
message.MergeFrom(data);
return message;
@@ -77,7 +77,7 @@ namespace Google.Protobuf
/// <returns>The parsed message.</returns>
public IMessage ParseFrom(ByteString data)
{
- Preconditions.CheckNotNull(data, "data");
+ ProtoPreconditions.CheckNotNull(data, "data");
IMessage message = factory();
message.MergeFrom(data);
return message;
@@ -191,7 +191,7 @@ namespace Google.Protobuf
/// <returns>The newly parsed message.</returns>
public new T ParseFrom(byte[] data)
{
- Preconditions.CheckNotNull(data, "data");
+ ProtoPreconditions.CheckNotNull(data, "data");
T message = factory();
message.MergeFrom(data);
return message;
@@ -204,7 +204,7 @@ namespace Google.Protobuf
/// <returns>The parsed message.</returns>
public new T ParseFrom(ByteString data)
{
- Preconditions.CheckNotNull(data, "data");
+ ProtoPreconditions.CheckNotNull(data, "data");
T message = factory();
message.MergeFrom(data);
return message;
diff --git a/csharp/src/Google.Protobuf/Preconditions.cs b/csharp/src/Google.Protobuf/ProtoPreconditions.cs
index 2db35ff6..abaeb9b4 100644
--- a/csharp/src/Google.Protobuf/Preconditions.cs
+++ b/csharp/src/Google.Protobuf/ProtoPreconditions.cs
@@ -35,9 +35,14 @@ using System;
namespace Google.Protobuf
{
/// <summary>
- /// Helper methods for throwing exceptions
+ /// Helper methods for throwing exceptions when preconditions are not met.
/// </summary>
- public static class Preconditions
+ /// <remarks>
+ /// This class is used internally and by generated code; it is not particularly
+ /// expected to be used from application code, although nothing prevents it
+ /// from being used that way.
+ /// </remarks>
+ public static class ProtoPreconditions
{
/// <summary>
/// Throws an ArgumentNullException if the given value is null, otherwise
diff --git a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
index 34bc82ec..7de7b5fc 100644
--- a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs
@@ -80,7 +80,7 @@ namespace Google.Protobuf.Reflection {
"EhMKC291dHB1dF90eXBlGAMgASgJEi8KB29wdGlvbnMYBCABKAsyHi5nb29n",
"bGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucxIfChBjbGllbnRfc3RyZWFtaW5n",
"GAUgASgIOgVmYWxzZRIfChBzZXJ2ZXJfc3RyZWFtaW5nGAYgASgIOgVmYWxz",
- "ZSKqBQoLRmlsZU9wdGlvbnMSFAoMamF2YV9wYWNrYWdlGAEgASgJEhwKFGph",
+ "ZSKuBQoLRmlsZU9wdGlvbnMSFAoMamF2YV9wYWNrYWdlGAEgASgJEhwKFGph",
"dmFfb3V0ZXJfY2xhc3NuYW1lGAggASgJEiIKE2phdmFfbXVsdGlwbGVfZmls",
"ZXMYCiABKAg6BWZhbHNlEiwKHWphdmFfZ2VuZXJhdGVfZXF1YWxzX2FuZF9o",
"YXNoGBQgASgIOgVmYWxzZRIlChZqYXZhX3N0cmluZ19jaGVja191dGY4GBsg",
@@ -91,72 +91,77 @@ namespace Google.Protobuf.Reflection {
"cHlfZ2VuZXJpY19zZXJ2aWNlcxgSIAEoCDoFZmFsc2USGQoKZGVwcmVjYXRl",
"ZBgXIAEoCDoFZmFsc2USHwoQY2NfZW5hYmxlX2FyZW5hcxgfIAEoCDoFZmFs",
"c2USGQoRb2JqY19jbGFzc19wcmVmaXgYJCABKAkSGAoQY3NoYXJwX25hbWVz",
- "cGFjZRglIAEoCRInCh9qYXZhbmFub191c2VfZGVwcmVjYXRlZF9wYWNrYWdl",
- "GCYgASgIEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2ds",
- "ZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uIjoKDE9wdGltaXplTW9k",
- "ZRIJCgVTUEVFRBABEg0KCUNPREVfU0laRRACEhAKDExJVEVfUlVOVElNRRAD",
- "KgkI6AcQgICAgAIi5gEKDk1lc3NhZ2VPcHRpb25zEiYKF21lc3NhZ2Vfc2V0",
- "X3dpcmVfZm9ybWF0GAEgASgIOgVmYWxzZRIuCh9ub19zdGFuZGFyZF9kZXNj",
- "cmlwdG9yX2FjY2Vzc29yGAIgASgIOgVmYWxzZRIZCgpkZXByZWNhdGVkGAMg",
- "ASgIOgVmYWxzZRIRCgltYXBfZW50cnkYByABKAgSQwoUdW5pbnRlcnByZXRl",
- "ZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0",
- "ZWRPcHRpb24qCQjoBxCAgICAAiKYAwoMRmllbGRPcHRpb25zEjoKBWN0eXBl",
- "GAEgASgOMiMuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5DVHlwZToG",
- "U1RSSU5HEg4KBnBhY2tlZBgCIAEoCBI/CgZqc3R5cGUYBiABKA4yJC5nb29n",
- "bGUucHJvdG9idWYuRmllbGRPcHRpb25zLkpTVHlwZToJSlNfTk9STUFMEhMK",
- "BGxhenkYBSABKAg6BWZhbHNlEhkKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNl",
- "EhMKBHdlYWsYCiABKAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9u",
- "GOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9u",
- "Ii8KBUNUeXBlEgoKBlNUUklORxAAEggKBENPUkQQARIQCgxTVFJJTkdfUElF",
- "Q0UQAiI1CgZKU1R5cGUSDQoJSlNfTk9STUFMEAASDQoJSlNfU1RSSU5HEAES",
- "DQoJSlNfTlVNQkVSEAIqCQjoBxCAgICAAiKNAQoLRW51bU9wdGlvbnMSEwoL",
- "YWxsb3dfYWxpYXMYAiABKAgSGQoKZGVwcmVjYXRlZBgDIAEoCDoFZmFsc2US",
- "QwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3Rv",
- "YnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiJ9ChBFbnVtVmFs",
- "dWVPcHRpb25zEhkKCmRlcHJlY2F0ZWQYASABKAg6BWZhbHNlEkMKFHVuaW50",
- "ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5Vbmlu",
- "dGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIiewoOU2VydmljZU9wdGlvbnMS",
- "GQoKZGVwcmVjYXRlZBghIAEoCDoFZmFsc2USQwoUdW5pbnRlcnByZXRlZF9v",
- "cHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRP",
- "cHRpb24qCQjoBxCAgICAAiJ6Cg1NZXRob2RPcHRpb25zEhkKCmRlcHJlY2F0",
- "ZWQYISABKAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMo",
- "CzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQ",
- "gICAgAIingIKE1VuaW50ZXJwcmV0ZWRPcHRpb24SOwoEbmFtZRgCIAMoCzIt",
- "Lmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uLk5hbWVQYXJ0",
- "EhgKEGlkZW50aWZpZXJfdmFsdWUYAyABKAkSGgoScG9zaXRpdmVfaW50X3Zh",
- "bHVlGAQgASgEEhoKEm5lZ2F0aXZlX2ludF92YWx1ZRgFIAEoAxIUCgxkb3Vi",
- "bGVfdmFsdWUYBiABKAESFAoMc3RyaW5nX3ZhbHVlGAcgASgMEhcKD2FnZ3Jl",
- "Z2F0ZV92YWx1ZRgIIAEoCRozCghOYW1lUGFydBIRCgluYW1lX3BhcnQYASAC",
- "KAkSFAoMaXNfZXh0ZW5zaW9uGAIgAigIItUBCg5Tb3VyY2VDb2RlSW5mbxI6",
- "Cghsb2NhdGlvbhgBIAMoCzIoLmdvb2dsZS5wcm90b2J1Zi5Tb3VyY2VDb2Rl",
- "SW5mby5Mb2NhdGlvbhqGAQoITG9jYXRpb24SEAoEcGF0aBgBIAMoBUICEAES",
- "EAoEc3BhbhgCIAMoBUICEAESGAoQbGVhZGluZ19jb21tZW50cxgDIAEoCRIZ",
- "ChF0cmFpbGluZ19jb21tZW50cxgEIAEoCRIhChlsZWFkaW5nX2RldGFjaGVk",
- "X2NvbW1lbnRzGAYgAygJQlgKE2NvbS5nb29nbGUucHJvdG9idWZCEERlc2Ny",
- "aXB0b3JQcm90b3NIAVoKZGVzY3JpcHRvcqICA0dQQqoCGkdvb2dsZS5Qcm90",
- "b2J1Zi5SZWZsZWN0aW9u"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ "cGFjZRglIAEoCRIrCh9qYXZhbmFub191c2VfZGVwcmVjYXRlZF9wYWNrYWdl",
+ "GCYgASgIQgIYARJDChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5n",
+ "b29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbiI6CgxPcHRpbWl6",
+ "ZU1vZGUSCQoFU1BFRUQQARINCglDT0RFX1NJWkUQAhIQCgxMSVRFX1JVTlRJ",
+ "TUUQAyoJCOgHEICAgIACIuYBCg5NZXNzYWdlT3B0aW9ucxImChdtZXNzYWdl",
+ "X3NldF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2USLgofbm9fc3RhbmRhcmRf",
+ "ZGVzY3JpcHRvcl9hY2Nlc3NvchgCIAEoCDoFZmFsc2USGQoKZGVwcmVjYXRl",
+ "ZBgDIAEoCDoFZmFsc2USEQoJbWFwX2VudHJ5GAcgASgIEkMKFHVuaW50ZXJw",
+ "cmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVy",
+ "cHJldGVkT3B0aW9uKgkI6AcQgICAgAIimAMKDEZpZWxkT3B0aW9ucxI6CgVj",
+ "dHlwZRgBIAEoDjIjLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuQ1R5",
+ "cGU6BlNUUklORxIOCgZwYWNrZWQYAiABKAgSPwoGanN0eXBlGAYgASgOMiQu",
+ "Z29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5KU1R5cGU6CUpTX05PUk1B",
+ "TBITCgRsYXp5GAUgASgIOgVmYWxzZRIZCgpkZXByZWNhdGVkGAMgASgIOgVm",
+ "YWxzZRITCgR3ZWFrGAogASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29w",
+ "dGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9w",
+ "dGlvbiIvCgVDVHlwZRIKCgZTVFJJTkcQABIICgRDT1JEEAESEAoMU1RSSU5H",
+ "X1BJRUNFEAIiNQoGSlNUeXBlEg0KCUpTX05PUk1BTBAAEg0KCUpTX1NUUklO",
+ "RxABEg0KCUpTX05VTUJFUhACKgkI6AcQgICAgAIijQEKC0VudW1PcHRpb25z",
+ "EhMKC2FsbG93X2FsaWFzGAIgASgIEhkKCmRlcHJlY2F0ZWQYAyABKAg6BWZh",
+ "bHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5w",
+ "cm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIifQoQRW51",
+ "bVZhbHVlT3B0aW9ucxIZCgpkZXByZWNhdGVkGAEgASgIOgVmYWxzZRJDChR1",
+ "bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYu",
+ "VW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACInsKDlNlcnZpY2VPcHRp",
+ "b25zEhkKCmRlcHJlY2F0ZWQYISABKAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0",
+ "ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJl",
+ "dGVkT3B0aW9uKgkI6AcQgICAgAIiegoNTWV0aG9kT3B0aW9ucxIZCgpkZXBy",
+ "ZWNhdGVkGCEgASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29wdGlvbhjn",
+ "ByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbioJ",
+ "COgHEICAgIACIp4CChNVbmludGVycHJldGVkT3B0aW9uEjsKBG5hbWUYAiAD",
+ "KAsyLS5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbi5OYW1l",
+ "UGFydBIYChBpZGVudGlmaWVyX3ZhbHVlGAMgASgJEhoKEnBvc2l0aXZlX2lu",
+ "dF92YWx1ZRgEIAEoBBIaChJuZWdhdGl2ZV9pbnRfdmFsdWUYBSABKAMSFAoM",
+ "ZG91YmxlX3ZhbHVlGAYgASgBEhQKDHN0cmluZ192YWx1ZRgHIAEoDBIXCg9h",
+ "Z2dyZWdhdGVfdmFsdWUYCCABKAkaMwoITmFtZVBhcnQSEQoJbmFtZV9wYXJ0",
+ "GAEgAigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCCLVAQoOU291cmNlQ29kZUlu",
+ "Zm8SOgoIbG9jYXRpb24YASADKAsyKC5nb29nbGUucHJvdG9idWYuU291cmNl",
+ "Q29kZUluZm8uTG9jYXRpb24ahgEKCExvY2F0aW9uEhAKBHBhdGgYASADKAVC",
+ "AhABEhAKBHNwYW4YAiADKAVCAhABEhgKEGxlYWRpbmdfY29tbWVudHMYAyAB",
+ "KAkSGQoRdHJhaWxpbmdfY29tbWVudHMYBCABKAkSIQoZbGVhZGluZ19kZXRh",
+ "Y2hlZF9jb21tZW50cxgGIAMoCSKnAQoRR2VuZXJhdGVkQ29kZUluZm8SQQoK",
+ "YW5ub3RhdGlvbhgBIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5HZW5lcmF0ZWRD",
+ "b2RlSW5mby5Bbm5vdGF0aW9uGk8KCkFubm90YXRpb24SEAoEcGF0aBgBIAMo",
+ "BUICEAESEwoLc291cmNlX2ZpbGUYAiABKAkSDQoFYmVnaW4YAyABKAUSCwoD",
+ "ZW5kGAQgASgFQlgKE2NvbS5nb29nbGUucHJvdG9idWZCEERlc2NyaXB0b3JQ",
+ "cm90b3NIAVoKZGVzY3JpcHRvcqICA0dQQqoCGkdvb2dsZS5Qcm90b2J1Zi5S",
+ "ZWZsZWN0aW9u"));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorSet), global::Google.Protobuf.Reflection.FileDescriptorSet.Parser, new[]{ "File" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorProto), global::Google.Protobuf.Reflection.FileDescriptorProto.Parser, new[]{ "Name", "Package", "Dependency", "PublicDependency", "WeakDependency", "MessageType", "EnumType", "Service", "Extension", "Options", "SourceCodeInfo", "Syntax" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto), global::Google.Protobuf.Reflection.DescriptorProto.Parser, new[]{ "Name", "Field", "Extension", "NestedType", "EnumType", "ExtensionRange", "OneofDecl", "Options", "ReservedRange", "ReservedName" }, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange.Parser, new[]{ "Start", "End" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange.Parser, new[]{ "Start", "End" }, null, null, null)}),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto), global::Google.Protobuf.Reflection.FieldDescriptorProto.Parser, new[]{ "Name", "Number", "Label", "Type", "TypeName", "Extendee", "DefaultValue", "OneofIndex", "JsonName", "Options" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type), typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label) }, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.OneofDescriptorProto), global::Google.Protobuf.Reflection.OneofDescriptorProto.Parser, new[]{ "Name" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto), global::Google.Protobuf.Reflection.EnumDescriptorProto.Parser, new[]{ "Name", "Value", "Options" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueDescriptorProto), global::Google.Protobuf.Reflection.EnumValueDescriptorProto.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.ServiceDescriptorProto), global::Google.Protobuf.Reflection.ServiceDescriptorProto.Parser, new[]{ "Name", "Method", "Options" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.MethodDescriptorProto), global::Google.Protobuf.Reflection.MethodDescriptorProto.Parser, new[]{ "Name", "InputType", "OutputType", "Options", "ClientStreaming", "ServerStreaming" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.FileOptions), global::Google.Protobuf.Reflection.FileOptions.Parser, new[]{ "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "JavaGenerateEqualsAndHash", "JavaStringCheckUtf8", "OptimizeFor", "GoPackage", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "Deprecated", "CcEnableArenas", "ObjcClassPrefix", "CsharpNamespace", "JavananoUseDeprecatedPackage", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode) }, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.MessageOptions), global::Google.Protobuf.Reflection.MessageOptions.Parser, new[]{ "MessageSetWireFormat", "NoStandardDescriptorAccessor", "Deprecated", "MapEntry", "UninterpretedOption" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.FieldOptions), global::Google.Protobuf.Reflection.FieldOptions.Parser, new[]{ "Ctype", "Packed", "Jstype", "Lazy", "Deprecated", "Weak", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.CType), typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.JSType) }, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.EnumOptions), global::Google.Protobuf.Reflection.EnumOptions.Parser, new[]{ "AllowAlias", "Deprecated", "UninterpretedOption" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueOptions), global::Google.Protobuf.Reflection.EnumValueOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.ServiceOptions), global::Google.Protobuf.Reflection.ServiceOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.MethodOptions), global::Google.Protobuf.Reflection.MethodOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption), global::Google.Protobuf.Reflection.UninterpretedOption.Parser, new[]{ "Name", "IdentifierValue", "PositiveIntValue", "NegativeIntValue", "DoubleValue", "StringValue", "AggregateValue" }, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart), global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart.Parser, new[]{ "NamePart_", "IsExtension" }, null, null, null)}),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo), global::Google.Protobuf.Reflection.SourceCodeInfo.Parser, new[]{ "Location" }, null, null, new pbr::GeneratedCodeInfo[] { new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location), global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location.Parser, new[]{ "Path", "Span", "LeadingComments", "TrailingComments", "LeadingDetachedComments" }, null, null, null)})
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorSet), global::Google.Protobuf.Reflection.FileDescriptorSet.Parser, new[]{ "File" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileDescriptorProto), global::Google.Protobuf.Reflection.FileDescriptorProto.Parser, new[]{ "Name", "Package", "Dependency", "PublicDependency", "WeakDependency", "MessageType", "EnumType", "Service", "Extension", "Options", "SourceCodeInfo", "Syntax" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto), global::Google.Protobuf.Reflection.DescriptorProto.Parser, new[]{ "Name", "Field", "Extension", "NestedType", "EnumType", "ExtensionRange", "OneofDecl", "Options", "ReservedRange", "ReservedName" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ExtensionRange.Parser, new[]{ "Start", "End" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange), global::Google.Protobuf.Reflection.DescriptorProto.Types.ReservedRange.Parser, new[]{ "Start", "End" }, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto), global::Google.Protobuf.Reflection.FieldDescriptorProto.Parser, new[]{ "Name", "Number", "Label", "Type", "TypeName", "Extendee", "DefaultValue", "OneofIndex", "JsonName", "Options" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type), typeof(global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label) }, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.OneofDescriptorProto), global::Google.Protobuf.Reflection.OneofDescriptorProto.Parser, new[]{ "Name" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumDescriptorProto), global::Google.Protobuf.Reflection.EnumDescriptorProto.Parser, new[]{ "Name", "Value", "Options" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueDescriptorProto), global::Google.Protobuf.Reflection.EnumValueDescriptorProto.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceDescriptorProto), global::Google.Protobuf.Reflection.ServiceDescriptorProto.Parser, new[]{ "Name", "Method", "Options" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodDescriptorProto), global::Google.Protobuf.Reflection.MethodDescriptorProto.Parser, new[]{ "Name", "InputType", "OutputType", "Options", "ClientStreaming", "ServerStreaming" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileOptions), global::Google.Protobuf.Reflection.FileOptions.Parser, new[]{ "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "JavaGenerateEqualsAndHash", "JavaStringCheckUtf8", "OptimizeFor", "GoPackage", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "Deprecated", "CcEnableArenas", "ObjcClassPrefix", "CsharpNamespace", "JavananoUseDeprecatedPackage", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode) }, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MessageOptions), global::Google.Protobuf.Reflection.MessageOptions.Parser, new[]{ "MessageSetWireFormat", "NoStandardDescriptorAccessor", "Deprecated", "MapEntry", "UninterpretedOption" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldOptions), global::Google.Protobuf.Reflection.FieldOptions.Parser, new[]{ "Ctype", "Packed", "Jstype", "Lazy", "Deprecated", "Weak", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.CType), typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.JSType) }, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumOptions), global::Google.Protobuf.Reflection.EnumOptions.Parser, new[]{ "AllowAlias", "Deprecated", "UninterpretedOption" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueOptions), global::Google.Protobuf.Reflection.EnumValueOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceOptions), global::Google.Protobuf.Reflection.ServiceOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodOptions), global::Google.Protobuf.Reflection.MethodOptions.Parser, new[]{ "Deprecated", "UninterpretedOption" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption), global::Google.Protobuf.Reflection.UninterpretedOption.Parser, new[]{ "Name", "IdentifierValue", "PositiveIntValue", "NegativeIntValue", "DoubleValue", "StringValue", "AggregateValue" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart), global::Google.Protobuf.Reflection.UninterpretedOption.Types.NamePart.Parser, new[]{ "NamePart_", "IsExtension" }, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo), global::Google.Protobuf.Reflection.SourceCodeInfo.Parser, new[]{ "Location" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location), global::Google.Protobuf.Reflection.SourceCodeInfo.Types.Location.Parser, new[]{ "Path", "Span", "LeadingComments", "TrailingComments", "LeadingDetachedComments" }, null, null, null)}),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Parser, new[]{ "Annotation" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation), global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation.Parser, new[]{ "Path", "SourceFile", "Begin", "End" }, null, null, null)})
}));
}
#endregion
@@ -225,7 +230,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -312,7 +317,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -325,7 +330,7 @@ namespace Google.Protobuf.Reflection {
public string Package {
get { return package_; }
set {
- package_ = pb::Preconditions.CheckNotNull(value, "value");
+ package_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -441,7 +446,7 @@ namespace Google.Protobuf.Reflection {
public string Syntax {
get { return syntax_; }
set {
- syntax_ = pb::Preconditions.CheckNotNull(value, "value");
+ syntax_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -489,7 +494,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -697,7 +702,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -827,7 +832,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1021,7 +1026,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1162,7 +1167,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1270,7 +1275,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1321,7 +1326,7 @@ namespace Google.Protobuf.Reflection {
public string TypeName {
get { return typeName_; }
set {
- typeName_ = pb::Preconditions.CheckNotNull(value, "value");
+ typeName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1335,7 +1340,7 @@ namespace Google.Protobuf.Reflection {
public string Extendee {
get { return extendee_; }
set {
- extendee_ = pb::Preconditions.CheckNotNull(value, "value");
+ extendee_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1352,7 +1357,7 @@ namespace Google.Protobuf.Reflection {
public string DefaultValue {
get { return defaultValue_; }
set {
- defaultValue_ = pb::Preconditions.CheckNotNull(value, "value");
+ defaultValue_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1382,7 +1387,7 @@ namespace Google.Protobuf.Reflection {
public string JsonName {
get { return jsonName_; }
set {
- jsonName_ = pb::Preconditions.CheckNotNull(value, "value");
+ jsonName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1436,7 +1441,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1715,7 +1720,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1741,7 +1746,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1823,7 +1828,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1872,7 +1877,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1981,7 +1986,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2031,7 +2036,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2147,7 +2152,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2196,7 +2201,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2308,7 +2313,7 @@ namespace Google.Protobuf.Reflection {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2322,7 +2327,7 @@ namespace Google.Protobuf.Reflection {
public string InputType {
get { return inputType_; }
set {
- inputType_ = pb::Preconditions.CheckNotNull(value, "value");
+ inputType_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2332,7 +2337,7 @@ namespace Google.Protobuf.Reflection {
public string OutputType {
get { return outputType_; }
set {
- outputType_ = pb::Preconditions.CheckNotNull(value, "value");
+ outputType_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2404,7 +2409,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -2578,7 +2583,7 @@ namespace Google.Protobuf.Reflection {
public string JavaPackage {
get { return javaPackage_; }
set {
- javaPackage_ = pb::Preconditions.CheckNotNull(value, "value");
+ javaPackage_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2595,7 +2600,7 @@ namespace Google.Protobuf.Reflection {
public string JavaOuterClassname {
get { return javaOuterClassname_; }
set {
- javaOuterClassname_ = pb::Preconditions.CheckNotNull(value, "value");
+ javaOuterClassname_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2682,7 +2687,7 @@ namespace Google.Protobuf.Reflection {
public string GoPackage {
get { return goPackage_; }
set {
- goPackage_ = pb::Preconditions.CheckNotNull(value, "value");
+ goPackage_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2768,7 +2773,7 @@ namespace Google.Protobuf.Reflection {
public string ObjcClassPrefix {
get { return objcClassPrefix_; }
set {
- objcClassPrefix_ = pb::Preconditions.CheckNotNull(value, "value");
+ objcClassPrefix_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2781,7 +2786,7 @@ namespace Google.Protobuf.Reflection {
public string CsharpNamespace {
get { return csharpNamespace_; }
set {
- csharpNamespace_ = pb::Preconditions.CheckNotNull(value, "value");
+ csharpNamespace_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -2792,6 +2797,7 @@ namespace Google.Protobuf.Reflection {
/// Whether the nano proto compiler should generate in the deprecated non-nano
/// suffixed package.
/// </summary>
+ [global::System.ObsoleteAttribute()]
public bool JavananoUseDeprecatedPackage {
get { return javananoUseDeprecatedPackage_; }
set {
@@ -2863,7 +2869,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3302,7 +3308,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3596,7 +3602,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3845,7 +3851,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -3990,7 +3996,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4121,7 +4127,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4252,7 +4258,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4363,7 +4369,7 @@ namespace Google.Protobuf.Reflection {
public string IdentifierValue {
get { return identifierValue_; }
set {
- identifierValue_ = pb::Preconditions.CheckNotNull(value, "value");
+ identifierValue_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -4403,7 +4409,7 @@ namespace Google.Protobuf.Reflection {
public pb::ByteString StringValue {
get { return stringValue_; }
set {
- stringValue_ = pb::Preconditions.CheckNotNull(value, "value");
+ stringValue_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -4413,7 +4419,7 @@ namespace Google.Protobuf.Reflection {
public string AggregateValue {
get { return aggregateValue_; }
set {
- aggregateValue_ = pb::Preconditions.CheckNotNull(value, "value");
+ aggregateValue_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -4451,7 +4457,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4615,7 +4621,7 @@ namespace Google.Protobuf.Reflection {
public string NamePart_ {
get { return namePart_; }
set {
- namePart_ = pb::Preconditions.CheckNotNull(value, "value");
+ namePart_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -4653,7 +4659,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4823,7 +4829,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -4998,7 +5004,7 @@ namespace Google.Protobuf.Reflection {
public string LeadingComments {
get { return leadingComments_; }
set {
- leadingComments_ = pb::Preconditions.CheckNotNull(value, "value");
+ leadingComments_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -5008,7 +5014,7 @@ namespace Google.Protobuf.Reflection {
public string TrailingComments {
get { return trailingComments_; }
set {
- trailingComments_ = pb::Preconditions.CheckNotNull(value, "value");
+ trailingComments_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -5051,7 +5057,7 @@ namespace Google.Protobuf.Reflection {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -5137,6 +5143,310 @@ namespace Google.Protobuf.Reflection {
}
+ /// <summary>
+ /// Describes the relationship between generated code and its original source
+ /// file. A GeneratedCodeInfo message is associated with only one generated
+ /// source file, but may contain references to different source .proto files.
+ /// </summary>
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ internal sealed partial class GeneratedCodeInfo : pb::IMessage<GeneratedCodeInfo> {
+ private static readonly pb::MessageParser<GeneratedCodeInfo> _parser = new pb::MessageParser<GeneratedCodeInfo>(() => new GeneratedCodeInfo());
+ public static pb::MessageParser<GeneratedCodeInfo> Parser { get { return _parser; } }
+
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::Google.Protobuf.Reflection.DescriptorReflection.Descriptor.MessageTypes[18]; }
+ }
+
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ public GeneratedCodeInfo() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ public GeneratedCodeInfo(GeneratedCodeInfo other) : this() {
+ annotation_ = other.annotation_.Clone();
+ }
+
+ public GeneratedCodeInfo Clone() {
+ return new GeneratedCodeInfo(this);
+ }
+
+ /// <summary>Field number for the "annotation" field.</summary>
+ public const int AnnotationFieldNumber = 1;
+ private static readonly pb::FieldCodec<global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation> _repeated_annotation_codec
+ = pb::FieldCodec.ForMessage(10, global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation.Parser);
+ private readonly pbc::RepeatedField<global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation> annotation_ = new pbc::RepeatedField<global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation>();
+ /// <summary>
+ /// An Annotation connects some span of text in generated code to an element
+ /// of its generating .proto file.
+ /// </summary>
+ public pbc::RepeatedField<global::Google.Protobuf.Reflection.GeneratedCodeInfo.Types.Annotation> Annotation {
+ get { return annotation_; }
+ }
+
+ public override bool Equals(object other) {
+ return Equals(other as GeneratedCodeInfo);
+ }
+
+ public bool Equals(GeneratedCodeInfo other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if(!annotation_.Equals(other.annotation_)) return false;
+ return true;
+ }
+
+ public override int GetHashCode() {
+ int hash = 1;
+ hash ^= annotation_.GetHashCode();
+ return hash;
+ }
+
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ public void WriteTo(pb::CodedOutputStream output) {
+ annotation_.WriteTo(output, _repeated_annotation_codec);
+ }
+
+ public int CalculateSize() {
+ int size = 0;
+ size += annotation_.CalculateSize(_repeated_annotation_codec);
+ return size;
+ }
+
+ public void MergeFrom(GeneratedCodeInfo other) {
+ if (other == null) {
+ return;
+ }
+ annotation_.Add(other.annotation_);
+ }
+
+ public void MergeFrom(pb::CodedInputStream input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ input.SkipLastField();
+ break;
+ case 10: {
+ annotation_.AddEntriesFrom(input, _repeated_annotation_codec);
+ break;
+ }
+ }
+ }
+ }
+
+ #region Nested types
+ /// <summary>Container for nested types declared in the GeneratedCodeInfo message type.</summary>
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public static partial class Types {
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ internal sealed partial class Annotation : pb::IMessage<Annotation> {
+ private static readonly pb::MessageParser<Annotation> _parser = new pb::MessageParser<Annotation>(() => new Annotation());
+ public static pb::MessageParser<Annotation> Parser { get { return _parser; } }
+
+ public static pbr::MessageDescriptor Descriptor {
+ get { return global::Google.Protobuf.Reflection.GeneratedCodeInfo.Descriptor.NestedTypes[0]; }
+ }
+
+ pbr::MessageDescriptor pb::IMessage.Descriptor {
+ get { return Descriptor; }
+ }
+
+ public Annotation() {
+ OnConstruction();
+ }
+
+ partial void OnConstruction();
+
+ public Annotation(Annotation other) : this() {
+ path_ = other.path_.Clone();
+ sourceFile_ = other.sourceFile_;
+ begin_ = other.begin_;
+ end_ = other.end_;
+ }
+
+ public Annotation Clone() {
+ return new Annotation(this);
+ }
+
+ /// <summary>Field number for the "path" field.</summary>
+ public const int PathFieldNumber = 1;
+ private static readonly pb::FieldCodec<int> _repeated_path_codec
+ = pb::FieldCodec.ForInt32(10);
+ private readonly pbc::RepeatedField<int> path_ = new pbc::RepeatedField<int>();
+ /// <summary>
+ /// Identifies the element in the original source .proto file. This field
+ /// is formatted the same as SourceCodeInfo.Location.path.
+ /// </summary>
+ public pbc::RepeatedField<int> Path {
+ get { return path_; }
+ }
+
+ /// <summary>Field number for the "source_file" field.</summary>
+ public const int SourceFileFieldNumber = 2;
+ private string sourceFile_ = "";
+ /// <summary>
+ /// Identifies the filesystem path to the original source .proto.
+ /// </summary>
+ public string SourceFile {
+ get { return sourceFile_; }
+ set {
+ sourceFile_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// <summary>Field number for the "begin" field.</summary>
+ public const int BeginFieldNumber = 3;
+ private int begin_;
+ /// <summary>
+ /// Identifies the starting offset in bytes in the generated code
+ /// that relates to the identified object.
+ /// </summary>
+ public int Begin {
+ get { return begin_; }
+ set {
+ begin_ = value;
+ }
+ }
+
+ /// <summary>Field number for the "end" field.</summary>
+ public const int EndFieldNumber = 4;
+ private int end_;
+ /// <summary>
+ /// Identifies the ending offset in bytes in the generated code that
+ /// relates to the identified offset. The end offset should be one past
+ /// the last relevant byte (so the length of the text = end - begin).
+ /// </summary>
+ public int End {
+ get { return end_; }
+ set {
+ end_ = value;
+ }
+ }
+
+ public override bool Equals(object other) {
+ return Equals(other as Annotation);
+ }
+
+ public bool Equals(Annotation other) {
+ if (ReferenceEquals(other, null)) {
+ return false;
+ }
+ if (ReferenceEquals(other, this)) {
+ return true;
+ }
+ if(!path_.Equals(other.path_)) return false;
+ if (SourceFile != other.SourceFile) return false;
+ if (Begin != other.Begin) return false;
+ if (End != other.End) return false;
+ return true;
+ }
+
+ public override int GetHashCode() {
+ int hash = 1;
+ hash ^= path_.GetHashCode();
+ if (SourceFile.Length != 0) hash ^= SourceFile.GetHashCode();
+ if (Begin != 0) hash ^= Begin.GetHashCode();
+ if (End != 0) hash ^= End.GetHashCode();
+ return hash;
+ }
+
+ public override string ToString() {
+ return pb::JsonFormatter.ToDiagnosticString(this);
+ }
+
+ public void WriteTo(pb::CodedOutputStream output) {
+ path_.WriteTo(output, _repeated_path_codec);
+ if (SourceFile.Length != 0) {
+ output.WriteRawTag(18);
+ output.WriteString(SourceFile);
+ }
+ if (Begin != 0) {
+ output.WriteRawTag(24);
+ output.WriteInt32(Begin);
+ }
+ if (End != 0) {
+ output.WriteRawTag(32);
+ output.WriteInt32(End);
+ }
+ }
+
+ public int CalculateSize() {
+ int size = 0;
+ size += path_.CalculateSize(_repeated_path_codec);
+ if (SourceFile.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(SourceFile);
+ }
+ if (Begin != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeInt32Size(Begin);
+ }
+ if (End != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeInt32Size(End);
+ }
+ return size;
+ }
+
+ public void MergeFrom(Annotation other) {
+ if (other == null) {
+ return;
+ }
+ path_.Add(other.path_);
+ if (other.SourceFile.Length != 0) {
+ SourceFile = other.SourceFile;
+ }
+ if (other.Begin != 0) {
+ Begin = other.Begin;
+ }
+ if (other.End != 0) {
+ End = other.End;
+ }
+ }
+
+ public void MergeFrom(pb::CodedInputStream input) {
+ uint tag;
+ while ((tag = input.ReadTag()) != 0) {
+ switch(tag) {
+ default:
+ input.SkipLastField();
+ break;
+ case 10:
+ case 8: {
+ path_.AddEntriesFrom(input, _repeated_path_codec);
+ break;
+ }
+ case 18: {
+ SourceFile = input.ReadString();
+ break;
+ }
+ case 24: {
+ Begin = input.ReadInt32();
+ break;
+ }
+ case 32: {
+ End = input.ReadInt32();
+ break;
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+ #endregion
+
+ }
+
#endregion
}
diff --git a/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
index 285f26f3..c732c93a 100644
--- a/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
@@ -43,13 +43,13 @@ namespace Google.Protobuf.Reflection
private readonly EnumDescriptorProto proto;
private readonly MessageDescriptor containingType;
private readonly IList<EnumValueDescriptor> values;
- private readonly Type generatedType;
+ private readonly Type clrType;
- internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type generatedType)
+ internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type clrType)
: base(file, file.ComputeFullName(parent, proto.Name), index)
{
this.proto = proto;
- this.generatedType = generatedType;
+ this.clrType = clrType;
containingType = parent;
if (proto.Value.Count == 0)
@@ -73,9 +73,9 @@ namespace Google.Protobuf.Reflection
public override string Name { get { return proto.Name; } }
/// <summary>
- /// The generated type for this enum, or <c>null</c> if the descriptor does not represent a generated type.
+ /// The CLR type for this enum. For generated code, this will be a CLR enum type.
/// </summary>
- public Type GeneratedType { get { return generatedType; } }
+ public Type ClrType { get { return clrType; } }
/// <value>
/// If this is a nested type, get the outer descriptor, otherwise null.
diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
index 3d6cc59f..c6caaec6 100644
--- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
@@ -62,8 +62,7 @@ namespace Google.Protobuf.Reflection
if (FieldNumber <= 0)
{
- throw new DescriptorValidationException(this,
- "Field numbers must be positive integers.");
+ throw new DescriptorValidationException(this, "Field numbers must be positive integers.");
}
containingType = parent;
// OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction.
@@ -72,7 +71,7 @@ namespace Google.Protobuf.Reflection
if (proto.OneofIndex < 0 || proto.OneofIndex >= parent.Proto.OneofDecl.Count)
{
throw new DescriptorValidationException(this,
- "FieldDescriptorProto.oneof_index is out of range for type " + parent.Name);
+ $"FieldDescriptorProto.oneof_index is out of range for type {parent.Name}");
}
containingOneof = parent.Oneofs[proto.OneofIndex];
}
@@ -94,13 +93,22 @@ namespace Google.Protobuf.Reflection
internal FieldDescriptorProto Proto { get { return proto; } }
/// <summary>
- /// Returns the accessor for this field, or <c>null</c> if this descriptor does
- /// not support reflective access.
+ /// Returns the accessor for this field.
/// </summary>
/// <remarks>
+ /// <para>
/// While a <see cref="FieldDescriptor"/> describes the field, it does not provide
/// any way of obtaining or changing the value of the field within a specific message;
/// that is the responsibility of the accessor.
+ /// </para>
+ /// <para>
+ /// The value returned by this property will be non-null for all regular fields. However,
+ /// if a message containing a map field is introspected, the list of nested messages will include
+ /// an auto-generated nested key/value pair message for the field. This is not represented in any
+ /// generated type, and the value of the map field itself is represented by a dictionary in the
+ /// reflection API. There are never instances of those "hidden" messages, so no accessor is provided
+ /// and this property will return null.
+ /// </para>
/// </remarks>
public IFieldAccessor Accessor { get { return accessor; } }
@@ -281,7 +289,7 @@ namespace Google.Protobuf.Reflection
}
else
{
- throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not a type.");
+ throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not a type.");
}
}
@@ -289,8 +297,7 @@ namespace Google.Protobuf.Reflection
{
if (!(typeDescriptor is MessageDescriptor))
{
- throw new DescriptorValidationException(this,
- "\"" + Proto.TypeName + "\" is not a message type.");
+ throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not a message type.");
}
messageType = (MessageDescriptor) typeDescriptor;
@@ -303,7 +310,7 @@ namespace Google.Protobuf.Reflection
{
if (!(typeDescriptor is EnumDescriptor))
{
- throw new DescriptorValidationException(this, "\"" + Proto.TypeName + "\" is not an enum type.");
+ throw new DescriptorValidationException(this, $"\"{Proto.TypeName}\" is not an enum type.");
}
enumType = (EnumDescriptor) typeDescriptor;
}
@@ -333,14 +340,16 @@ namespace Google.Protobuf.Reflection
private IFieldAccessor CreateAccessor(string propertyName)
{
- if (containingType.GeneratedType == null || propertyName == null)
+ // If we're given no property name, that's because we really don't want an accessor.
+ // (At the moment, that means it's a map entry message...)
+ if (propertyName == null)
{
return null;
}
- var property = containingType.GeneratedType.GetProperty(propertyName);
+ var property = containingType.ClrType.GetProperty(propertyName);
if (property == null)
{
- throw new DescriptorValidationException(this, "Property " + propertyName + " not found in " + containingType.GeneratedType);
+ throw new DescriptorValidationException(this, $"Property {propertyName} not found in {containingType.ClrType}");
}
return IsMap ? new MapFieldAccessor(property, this)
: IsRepeated ? new RepeatedFieldAccessor(property, this)
diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
index 549dfe28..ab7cd922 100644
--- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs
@@ -43,35 +43,26 @@ namespace Google.Protobuf.Reflection
/// </summary>
public sealed class FileDescriptor : IDescriptor
{
- private readonly ByteString descriptorData;
- private readonly FileDescriptorProto proto;
- private readonly IList<MessageDescriptor> messageTypes;
- private readonly IList<EnumDescriptor> enumTypes;
- private readonly IList<ServiceDescriptor> services;
- private readonly IList<FileDescriptor> dependencies;
- private readonly IList<FileDescriptor> publicDependencies;
- private readonly DescriptorPool pool;
-
- private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedCodeInfo generatedCodeInfo)
+ private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
{
- this.descriptorData = descriptorData;
- this.pool = pool;
- this.proto = proto;
- this.dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
+ SerializedData = descriptorData;
+ DescriptorPool = pool;
+ Proto = proto;
+ Dependencies = new ReadOnlyCollection<FileDescriptor>((FileDescriptor[]) dependencies.Clone());
- publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
+ PublicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies);
pool.AddPackage(Package, this);
- messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
+ MessageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType,
(message, index) =>
- new MessageDescriptor(message, this, null, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedTypes[index]));
+ new MessageDescriptor(message, this, null, index, generatedCodeInfo.NestedTypes[index]));
- enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
+ EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType,
(enumType, index) =>
- new EnumDescriptor(enumType, this, null, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedEnums[index]));
+ new EnumDescriptor(enumType, this, null, index, generatedCodeInfo.NestedEnums[index]));
- services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
+ Services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service,
(service, index) =>
new ServiceDescriptor(service, this, index));
}
@@ -132,99 +123,63 @@ namespace Google.Protobuf.Reflection
/// <value>
/// The descriptor in its protocol message representation.
/// </value>
- internal FileDescriptorProto Proto
- {
- get { return proto; }
- }
+ internal FileDescriptorProto Proto { get; }
/// <value>
/// The file name.
/// </value>
- public string Name
- {
- get { return proto.Name; }
- }
+ public string Name => Proto.Name;
/// <summary>
/// The package as declared in the .proto file. This may or may not
/// be equivalent to the .NET namespace of the generated classes.
/// </summary>
- public string Package
- {
- get { return proto.Package; }
- }
+ public string Package => Proto.Package;
/// <value>
/// Unmodifiable list of top-level message types declared in this file.
/// </value>
- public IList<MessageDescriptor> MessageTypes
- {
- get { return messageTypes; }
- }
+ public IList<MessageDescriptor> MessageTypes { get; }
/// <value>
/// Unmodifiable list of top-level enum types declared in this file.
/// </value>
- public IList<EnumDescriptor> EnumTypes
- {
- get { return enumTypes; }
- }
+ public IList<EnumDescriptor> EnumTypes { get; }
/// <value>
/// Unmodifiable list of top-level services declared in this file.
/// </value>
- public IList<ServiceDescriptor> Services
- {
- get { return services; }
- }
+ public IList<ServiceDescriptor> Services { get; }
/// <value>
/// Unmodifiable list of this file's dependencies (imports).
/// </value>
- public IList<FileDescriptor> Dependencies
- {
- get { return dependencies; }
- }
+ public IList<FileDescriptor> Dependencies { get; }
/// <value>
/// Unmodifiable list of this file's public dependencies (public imports).
/// </value>
- public IList<FileDescriptor> PublicDependencies
- {
- get { return publicDependencies; }
- }
+ public IList<FileDescriptor> PublicDependencies { get; }
/// <value>
/// The original serialized binary form of this descriptor.
/// </value>
- public ByteString SerializedData
- {
- get { return descriptorData; }
- }
+ public ByteString SerializedData { get; }
/// <value>
/// Implementation of IDescriptor.FullName - just returns the same as Name.
/// </value>
- string IDescriptor.FullName
- {
- get { return Name; }
- }
+ string IDescriptor.FullName => Name;
/// <value>
/// Implementation of IDescriptor.File - just returns this descriptor.
/// </value>
- FileDescriptor IDescriptor.File
- {
- get { return this; }
- }
+ FileDescriptor IDescriptor.File => this;
/// <value>
/// Pool containing symbol descriptors.
/// </value>
- internal DescriptorPool DescriptorPool
- {
- get { return pool; }
- }
+ internal DescriptorPool DescriptorPool { get; }
/// <summary>
/// Finds a type (message, enum, service or extension) in the file by name. Does not find nested types.
@@ -245,7 +200,7 @@ namespace Google.Protobuf.Reflection
{
name = Package + "." + name;
}
- T result = pool.FindSymbol<T>(name);
+ T result = DescriptorPool.FindSymbol<T>(name);
if (result != null && result.File == this)
{
return result;
@@ -264,11 +219,11 @@ namespace Google.Protobuf.Reflection
/// file's dependencies, in the exact order listed in the .proto file. May be null,
/// in which case it is treated as an empty array.</param>
/// <param name="allowUnknownDependencies">Whether unknown dependencies are ignored (true) or cause an exception to be thrown (false).</param>
- /// <param name="generatedCodeInfo">Reflection information, if any. May be null, specifically for non-generated code.</param>
+ /// <param name="generatedCodeInfo">Details about generated code, for the purposes of reflection.</param>
/// <exception cref="DescriptorValidationException">If <paramref name="proto"/> is not
/// a valid descriptor. This can occur for a number of reasons, such as a field
/// having an undefined type or because two messages were defined with the same name.</exception>
- private static FileDescriptor BuildFrom(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedCodeInfo generatedCodeInfo)
+ private static FileDescriptor BuildFrom(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, GeneratedClrTypeInfo generatedCodeInfo)
{
// Building descriptors involves two steps: translating and linking.
// In the translation step (implemented by FileDescriptor's
@@ -291,15 +246,17 @@ namespace Google.Protobuf.Reflection
// need.
if (dependencies.Length != proto.Dependency.Count)
{
- throw new DescriptorValidationException(result,
- "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
- "those listed in the FileDescriptorProto.");
+ throw new DescriptorValidationException(
+ result,
+ "Dependencies passed to FileDescriptor.BuildFrom() don't match " +
+ "those listed in the FileDescriptorProto.");
}
for (int i = 0; i < proto.Dependency.Count; i++)
{
if (dependencies[i].Name != proto.Dependency[i])
{
- throw new DescriptorValidationException(result,
+ throw new DescriptorValidationException(
+ result,
"Dependencies passed to FileDescriptor.BuildFrom() don't match " +
"those listed in the FileDescriptorProto. Expected: " +
proto.Dependency[i] + " but was: " + dependencies[i].Name);
@@ -312,28 +269,29 @@ namespace Google.Protobuf.Reflection
private void CrossLink()
{
- foreach (MessageDescriptor message in messageTypes)
+ foreach (MessageDescriptor message in MessageTypes)
{
message.CrossLink();
}
- foreach (ServiceDescriptor service in services)
+ foreach (ServiceDescriptor service in Services)
{
service.CrossLink();
}
}
/// <summary>
- /// Creates an instance for generated code.
+ /// Creates a descriptor for generated code.
/// </summary>
/// <remarks>
- /// The <paramref name="generatedCodeInfo"/> parameter should be null for descriptors which don't correspond to
- /// generated types. Otherwise, it should be a <see cref="GeneratedCodeInfo"/> with nested types and nested
- /// enums corresponding to the types and enums contained within the file descriptor.
+ /// This method is only designed to be used by the results of generating code with protoc,
+ /// which creates the appropriate dependencies etc. It has to be public because the generated
+ /// code is "external", but should not be called directly by end users.
/// </remarks>
- public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData,
- FileDescriptor[] dependencies,
- GeneratedCodeInfo generatedCodeInfo)
+ public static FileDescriptor FromGeneratedCode(
+ byte[] descriptorData,
+ FileDescriptor[] dependencies,
+ GeneratedClrTypeInfo generatedCodeInfo)
{
FileDescriptorProto proto;
try
@@ -345,8 +303,6 @@ namespace Google.Protobuf.Reflection
throw new ArgumentException("Failed to parse protocol buffer descriptor for generated code.", e);
}
-
-
try
{
// When building descriptors for generated code, we allow unknown
@@ -355,7 +311,7 @@ namespace Google.Protobuf.Reflection
}
catch (DescriptorValidationException e)
{
- throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e);
+ throw new ArgumentException($"Invalid embedded descriptor for \"{proto.Name}\".", e);
}
}
@@ -367,7 +323,7 @@ namespace Google.Protobuf.Reflection
/// </returns>
public override string ToString()
{
- return "FileDescriptor for " + proto.Name;
+ return $"FileDescriptor for {Name}";
}
/// <summary>
diff --git a/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs b/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs
new file mode 100644
index 00000000..fe5db656
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Reflection/GeneratedClrTypeInfo.cs
@@ -0,0 +1,103 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+using System;
+
+namespace Google.Protobuf.Reflection
+{
+ /// <summary>
+ /// Extra information provided by generated code when initializing a message or file descriptor.
+ /// These are constructed as required, and are not long-lived. Hand-written code should
+ /// never need to use this type.
+ /// </summary>
+ public sealed class GeneratedClrTypeInfo
+ {
+ private static readonly string[] EmptyNames = new string[0];
+ private static readonly GeneratedClrTypeInfo[] EmptyCodeInfo = new GeneratedClrTypeInfo[0];
+
+ /// <summary>
+ /// Irrelevant for file descriptors; the CLR type for the message for message descriptors.
+ /// </summary>
+ public Type ClrType { get; private set; }
+
+ /// <summary>
+ /// Irrelevant for file descriptors; the parser for message descriptors.
+ /// </summary>
+ public MessageParser Parser { get; }
+
+ /// <summary>
+ /// Irrelevant for file descriptors; the CLR property names (in message descriptor field order)
+ /// for fields in the message for message descriptors.
+ /// </summary>
+ public string[] PropertyNames { get; }
+
+ /// <summary>
+ /// Irrelevant for file descriptors; the CLR property "base" names (in message descriptor oneof order)
+ /// for oneofs in the message for message descriptors. It is expected that for a oneof name of "Foo",
+ /// there will be a "FooCase" property and a "ClearFoo" method.
+ /// </summary>
+ public string[] OneofNames { get; }
+
+ /// <summary>
+ /// The reflection information for types within this file/message descriptor. Elements may be null
+ /// if there is no corresponding generated type, e.g. for map entry types.
+ /// </summary>
+ public GeneratedClrTypeInfo[] NestedTypes { get; }
+
+ /// <summary>
+ /// The CLR types for enums within this file/message descriptor.
+ /// </summary>
+ public Type[] NestedEnums { get; }
+
+ /// <summary>
+ /// Creates a GeneratedClrTypeInfo for a message descriptor, with nested types, nested enums, the CLR type, property names and oneof names.
+ /// Each array parameter may be null, to indicate a lack of values.
+ /// The parameter order is designed to make it feasible to format the generated code readably.
+ /// </summary>
+ public GeneratedClrTypeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
+ {
+ NestedTypes = nestedTypes ?? EmptyCodeInfo;
+ NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
+ ClrType = clrType;
+ Parser = parser;
+ PropertyNames = propertyNames ?? EmptyNames;
+ OneofNames = oneofNames ?? EmptyNames;
+ }
+
+ /// <summary>
+ /// Creates a GeneratedClrTypeInfo for a file descriptor, with only types and enums.
+ /// </summary>
+ public GeneratedClrTypeInfo(Type[] nestedEnums, GeneratedClrTypeInfo[] nestedTypes)
+ : this(null, null, null, null, nestedEnums, nestedTypes)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs b/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs
deleted file mode 100644
index ff4ad0aa..00000000
--- a/csharp/src/Google.Protobuf/Reflection/GeneratedCodeInfo.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-
-namespace Google.Protobuf.Reflection
-{
- /// <summary>
- /// Extra information provided by generated code when initializing a message or file descriptor.
- /// These are constructed as required, and are not long-lived. Hand-written code should
- /// never need to use this type.
- /// </summary>
- public sealed class GeneratedCodeInfo
- {
- private static readonly string[] EmptyNames = new string[0];
- private static readonly GeneratedCodeInfo[] EmptyCodeInfo = new GeneratedCodeInfo[0];
-
- /// <summary>
- /// Irrelevant for file descriptors; the CLR type for the message for message descriptors.
- /// </summary>
- public Type ClrType { get; private set; }
-
- /// <summary>
- /// Irrelevant for file descriptors; the parser for message descriptors.
- /// </summary>
- public MessageParser Parser { get; private set; }
-
- /// <summary>
- /// Irrelevant for file descriptors; the CLR property names (in message descriptor field order)
- /// for fields in the message for message descriptors.
- /// </summary>
- public string[] PropertyNames { get; private set; }
-
- /// <summary>
- /// Irrelevant for file descriptors; the CLR property "base" names (in message descriptor oneof order)
- /// for oneofs in the message for message descriptors. It is expected that for a oneof name of "Foo",
- /// there will be a "FooCase" property and a "ClearFoo" method.
- /// </summary>
- public string[] OneofNames { get; private set; }
-
- /// <summary>
- /// The reflection information for types within this file/message descriptor. Elements may be null
- /// if there is no corresponding generated type, e.g. for map entry types.
- /// </summary>
- public GeneratedCodeInfo[] NestedTypes { get; private set; }
-
- /// <summary>
- /// The CLR types for enums within this file/message descriptor.
- /// </summary>
- public Type[] NestedEnums { get; private set; }
-
- /// <summary>
- /// Creates a GeneratedCodeInfo for a message descriptor, with nested types, nested enums, the CLR type, property names and oneof names.
- /// Each array parameter may be null, to indicate a lack of values.
- /// The parameter order is designed to make it feasible to format the generated code readably.
- /// </summary>
- public GeneratedCodeInfo(Type clrType, MessageParser parser, string[] propertyNames, string[] oneofNames, Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
- {
- NestedTypes = nestedTypes ?? EmptyCodeInfo;
- NestedEnums = nestedEnums ?? ReflectionUtil.EmptyTypes;
- ClrType = clrType;
- Parser = parser;
- PropertyNames = propertyNames ?? EmptyNames;
- OneofNames = oneofNames ?? EmptyNames;
- }
-
- /// <summary>
- /// Creates a GeneratedCodeInfo for a file descriptor, with only types and enums.
- /// </summary>
- public GeneratedCodeInfo(Type[] nestedEnums, GeneratedCodeInfo[] nestedTypes)
- : this(null, null, null, null, nestedEnums, nestedTypes)
- {
- }
- }
-} \ No newline at end of file
diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
index 65040e42..f5798d1e 100644
--- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
@@ -56,153 +56,156 @@ namespace Google.Protobuf.Reflection
"google/protobuf/type.proto",
};
- private readonly DescriptorProto proto;
- private readonly MessageDescriptor containingType;
- private readonly IList<MessageDescriptor> nestedTypes;
- private readonly IList<EnumDescriptor> enumTypes;
private readonly IList<FieldDescriptor> fieldsInDeclarationOrder;
private readonly IList<FieldDescriptor> fieldsInNumberOrder;
private readonly IDictionary<string, FieldDescriptor> jsonFieldMap;
- private readonly FieldCollection fields;
- private readonly IList<OneofDescriptor> oneofs;
- // CLR representation of the type described by this descriptor, if any.
- private readonly Type generatedType;
- private readonly MessageParser parser;
- internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedCodeInfo generatedCodeInfo)
+ internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedClrTypeInfo generatedCodeInfo)
: base(file, file.ComputeFullName(parent, proto.Name), typeIndex)
{
- this.proto = proto;
- parser = generatedCodeInfo == null ? null : generatedCodeInfo.Parser;
- generatedType = generatedCodeInfo == null ? null : generatedCodeInfo.ClrType;
-
- containingType = parent;
-
- oneofs = DescriptorUtil.ConvertAndMakeReadOnly(
+ Proto = proto;
+ Parser = generatedCodeInfo?.Parser;
+ ClrType = generatedCodeInfo?.ClrType;
+ ContainingType = parent;
+
+ // Note use of generatedCodeInfo. rather than generatedCodeInfo?. here... we don't expect
+ // to see any nested oneofs, types or enums in "not actually generated" code... we do
+ // expect fields though (for map entry messages).
+ Oneofs = DescriptorUtil.ConvertAndMakeReadOnly(
proto.OneofDecl,
(oneof, index) =>
- new OneofDescriptor(oneof, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.OneofNames[index]));
+ new OneofDescriptor(oneof, file, this, index, generatedCodeInfo.OneofNames[index]));
- nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(
+ NestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(
proto.NestedType,
(type, index) =>
- new MessageDescriptor(type, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedTypes[index]));
+ new MessageDescriptor(type, file, this, index, generatedCodeInfo.NestedTypes[index]));
- enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(
+ EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly(
proto.EnumType,
(type, index) =>
- new EnumDescriptor(type, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedEnums[index]));
+ new EnumDescriptor(type, file, this, index, generatedCodeInfo.NestedEnums[index]));
fieldsInDeclarationOrder = DescriptorUtil.ConvertAndMakeReadOnly(
proto.Field,
(field, index) =>
- new FieldDescriptor(field, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.PropertyNames[index]));
+ new FieldDescriptor(field, file, this, index, generatedCodeInfo?.PropertyNames[index]));
fieldsInNumberOrder = new ReadOnlyCollection<FieldDescriptor>(fieldsInDeclarationOrder.OrderBy(field => field.FieldNumber).ToArray());
// TODO: Use field => field.Proto.JsonName when we're confident it's appropriate. (And then use it in the formatter, too.)
- jsonFieldMap = new ReadOnlyDictionary<string, FieldDescriptor>(fieldsInNumberOrder.ToDictionary(field => JsonFormatter.ToCamelCase(field.Name)));
+ jsonFieldMap = CreateJsonFieldMap(fieldsInNumberOrder);
file.DescriptorPool.AddSymbol(this);
- fields = new FieldCollection(this);
+ Fields = new FieldCollection(this);
}
-
- /// <summary>
- /// Returns the total number of nested types and enums, recursively.
- /// </summary>
- private int CountTotalGeneratedTypes()
+
+ private static ReadOnlyDictionary<string, FieldDescriptor> CreateJsonFieldMap(IList<FieldDescriptor> fields)
{
- return nestedTypes.Sum(nested => nested.CountTotalGeneratedTypes()) + enumTypes.Count;
+ var map = new Dictionary<string, FieldDescriptor>();
+ foreach (var field in fields)
+ {
+ map[JsonFormatter.ToCamelCase(field.Name)] = field;
+ map[field.Name] = field;
+ }
+ return new ReadOnlyDictionary<string, FieldDescriptor>(map);
}
/// <summary>
/// The brief name of the descriptor's target.
/// </summary>
- public override string Name { get { return proto.Name; } }
+ public override string Name => Proto.Name;
- internal DescriptorProto Proto { get { return proto; } }
+ internal DescriptorProto Proto { get; }
/// <summary>
- /// The generated type for this message, or <c>null</c> if the descriptor does not represent a generated type.
+ /// The CLR type used to represent message instances from this descriptor.
/// </summary>
- public Type GeneratedType { get { return generatedType; } }
+ /// <remarks>
+ /// <para>
+ /// The value returned by this property will be non-null for all regular fields. However,
+ /// if a message containing a map field is introspected, the list of nested messages will include
+ /// an auto-generated nested key/value pair message for the field. This is not represented in any
+ /// generated type, so this property will return null in such cases.
+ /// </para>
+ /// <para>
+ /// For wrapper types (<see cref="Google.Protobuf.WellKnownTypes.StringValue"/> and the like), the type returned here
+ /// will be the generated message type, not the native type used by reflection for fields of those types. Code
+ /// using reflection should call <see cref="IsWrapperType"/> to determine whether a message descriptor represents
+ /// a wrapper type, and handle the result appropriately.
+ /// </para>
+ /// </remarks>
+ public Type ClrType { get; }
/// <summary>
/// A parser for this message type.
/// </summary>
/// <remarks>
+ /// <para>
/// As <see cref="MessageDescriptor"/> is not generic, this cannot be statically
- /// typed to the relevant type, but if <see cref="GeneratedType"/> returns a non-null value, the parser returned
+ /// typed to the relevant type, but it should produce objects of a type compatible with <see cref="ClrType"/>.
+ /// </para>
+ /// <para>
+ /// The value returned by this property will be non-null for all regular fields. However,
+ /// if a message containing a map field is introspected, the list of nested messages will include
+ /// an auto-generated nested key/value pair message for the field. No message parser object is created for
+ /// such messages, so this property will return null in such cases.
+ /// </para>
+ /// <para>
+ /// For wrapper types (<see cref="Google.Protobuf.WellKnownTypes.StringValue"/> and the like), the parser returned here
+ /// will be the generated message type, not the native type used by reflection for fields of those types. Code
+ /// using reflection should call <see cref="IsWrapperType"/> to determine whether a message descriptor represents
+ /// a wrapper type, and handle the result appropriately.
+ /// </para>
/// </remarks>
- public MessageParser Parser { get { return parser; } }
+ public MessageParser Parser { get; }
/// <summary>
/// Returns whether this message is one of the "well known types" which may have runtime/protoc support.
/// </summary>
- internal bool IsWellKnownType
- {
- get
- {
- return File.Package == "google.protobuf" && WellKnownTypeNames.Contains(File.Name);
- }
- }
+ internal bool IsWellKnownType => File.Package == "google.protobuf" && WellKnownTypeNames.Contains(File.Name);
+
+ /// <summary>
+ /// Returns whether this message is one of the "wrapper types" used for fields which represent primitive values
+ /// with the addition of presence.
+ /// </summary>
+ internal bool IsWrapperType => File.Package == "google.protobuf" && File.Name == "google/protobuf/wrappers.proto";
/// <value>
/// If this is a nested type, get the outer descriptor, otherwise null.
/// </value>
- public MessageDescriptor ContainingType
- {
- get { return containingType; }
- }
+ public MessageDescriptor ContainingType { get; }
/// <value>
/// A collection of fields, which can be retrieved by name or field number.
/// </value>
- public FieldCollection Fields
- {
- get { return fields; }
- }
+ public FieldCollection Fields { get; }
/// <value>
/// An unmodifiable list of this message type's nested types.
/// </value>
- public IList<MessageDescriptor> NestedTypes
- {
- get { return nestedTypes; }
- }
+ public IList<MessageDescriptor> NestedTypes { get; }
/// <value>
/// An unmodifiable list of this message type's enum types.
/// </value>
- public IList<EnumDescriptor> EnumTypes
- {
- get { return enumTypes; }
- }
+ public IList<EnumDescriptor> EnumTypes { get; }
/// <value>
/// An unmodifiable list of the "oneof" field collections in this message type.
/// </value>
- public IList<OneofDescriptor> Oneofs
- {
- get { return oneofs; }
- }
+ public IList<OneofDescriptor> Oneofs { get; }
/// <summary>
/// Finds a field by field name.
/// </summary>
/// <param name="name">The unqualified name of the field (e.g. "foo").</param>
/// <returns>The field's descriptor, or null if not found.</returns>
- public FieldDescriptor FindFieldByName(String name)
- {
- return File.DescriptorPool.FindSymbol<FieldDescriptor>(FullName + "." + name);
- }
+ public FieldDescriptor FindFieldByName(String name) => File.DescriptorPool.FindSymbol<FieldDescriptor>(FullName + "." + name);
/// <summary>
/// Finds a field by field number.
/// </summary>
/// <param name="number">The field number within this message type.</param>
/// <returns>The field's descriptor, or null if not found.</returns>
- public FieldDescriptor FindFieldByNumber(int number)
- {
- return File.DescriptorPool.FindFieldByNumber(this, number);
- }
+ public FieldDescriptor FindFieldByNumber(int number) => File.DescriptorPool.FindFieldByNumber(this, number);
/// <summary>
/// Finds a nested descriptor by name. The is valid for fields, nested
@@ -210,18 +213,15 @@ namespace Google.Protobuf.Reflection
/// </summary>
/// <param name="name">The unqualified name of the descriptor, e.g. "Foo"</param>
/// <returns>The descriptor, or null if not found.</returns>
- public T FindDescriptor<T>(string name)
- where T : class, IDescriptor
- {
- return File.DescriptorPool.FindSymbol<T>(FullName + "." + name);
- }
+ public T FindDescriptor<T>(string name) where T : class, IDescriptor =>
+ File.DescriptorPool.FindSymbol<T>(FullName + "." + name);
/// <summary>
/// Looks up and cross-links all fields and nested types.
/// </summary>
internal void CrossLink()
{
- foreach (MessageDescriptor message in nestedTypes)
+ foreach (MessageDescriptor message in NestedTypes)
{
message.CrossLink();
}
@@ -231,7 +231,7 @@ namespace Google.Protobuf.Reflection
field.CrossLink();
}
- foreach (OneofDescriptor oneof in oneofs)
+ foreach (OneofDescriptor oneof in Oneofs)
{
oneof.CrossLink();
}
@@ -253,10 +253,7 @@ namespace Google.Protobuf.Reflection
/// Returns the fields in the message as an immutable list, in the order in which they
/// are declared in the source .proto file.
/// </value>
- public IList<FieldDescriptor> InDeclarationOrder()
- {
- return messageDescriptor.fieldsInDeclarationOrder;
- }
+ public IList<FieldDescriptor> InDeclarationOrder() => messageDescriptor.fieldsInDeclarationOrder;
/// <value>
/// Returns the fields in the message as an immutable list, in ascending field number
@@ -264,22 +261,17 @@ namespace Google.Protobuf.Reflection
/// index in the list to the field number; to retrieve a field by field number, it is better
/// to use the <see cref="FieldCollection"/> indexer.
/// </value>
- public IList<FieldDescriptor> InFieldNumberOrder()
- {
- return messageDescriptor.fieldsInNumberOrder;
- }
+ public IList<FieldDescriptor> InFieldNumberOrder() => messageDescriptor.fieldsInNumberOrder;
// TODO: consider making this public in the future. (Being conservative for now...)
/// <value>
- /// Returns a read-only dictionary mapping the field names in this message as they're used
+ /// Returns a read-only dictionary mapping the field names in this message as they're available
/// in the JSON representation to the field descriptors. For example, a field <c>foo_bar</c>
- /// in the message would result in an entry with a key <c>fooBar</c>.
+ /// in the message would result two entries, one with a key <c>fooBar</c> and one with a key
+ /// <c>foo_bar</c>, both referring to the same field.
/// </value>
- internal IDictionary<string, FieldDescriptor> ByJsonName()
- {
- return messageDescriptor.jsonFieldMap;
- }
+ internal IDictionary<string, FieldDescriptor> ByJsonName() => messageDescriptor.jsonFieldMap;
/// <summary>
/// Retrieves the descriptor for the field with the given number.
diff --git a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
index d51ee526..22020acf 100644
--- a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
@@ -86,8 +86,7 @@ namespace Google.Protobuf.Reflection
/// in a particular message.
/// </summary>
/// <value>
- /// The accessor used for reflective access, or <c>null</c> if reflection is not
- /// supported by this descriptor.
+ /// The accessor used for reflective access.
/// </value>
public OneofAccessor Accessor { get { return accessor; } }
@@ -106,19 +105,15 @@ namespace Google.Protobuf.Reflection
private OneofAccessor CreateAccessor(string clrName)
{
- if (containingType.GeneratedType == null || clrName == null)
- {
- return null;
- }
- var caseProperty = containingType.GeneratedType.GetProperty(clrName + "Case");
+ var caseProperty = containingType.ClrType.GetProperty(clrName + "Case");
if (caseProperty == null)
{
- throw new DescriptorValidationException(this, "Property " + clrName + "Case not found in " + containingType.GeneratedType);
+ throw new DescriptorValidationException(this, $"Property {clrName}Case not found in {containingType.ClrType}");
}
- var clearMethod = containingType.GeneratedType.GetMethod("Clear" + clrName);
+ var clearMethod = containingType.ClrType.GetMethod("Clear" + clrName);
if (clearMethod == null)
{
- throw new DescriptorValidationException(this, "Method Clear" + clrName + " not found in " + containingType.GeneratedType);
+ throw new DescriptorValidationException(this, $"Method Clear{clrName} not found in {containingType.ClrType}");
}
return new OneofAccessor(caseProperty, clearMethod, this);
diff --git a/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs b/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs
new file mode 100644
index 00000000..e94e3e6c
--- /dev/null
+++ b/csharp/src/Google.Protobuf/Reflection/TypeRegistry.cs
@@ -0,0 +1,183 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Google.Protobuf.Reflection
+{
+ /// <summary>
+ /// An immutable registry of types which can be looked up by their full name.
+ /// </summary>
+ public sealed class TypeRegistry
+ {
+ /// <summary>
+ /// An empty type registry, containing no types.
+ /// </summary>
+ public static TypeRegistry Empty { get; } = new TypeRegistry(new Dictionary<string, MessageDescriptor>());
+
+ private readonly Dictionary<string, MessageDescriptor> fullNameToMessageMap;
+
+ private TypeRegistry(Dictionary<string, MessageDescriptor> fullNameToMessageMap)
+ {
+ this.fullNameToMessageMap = fullNameToMessageMap;
+ }
+
+ /// <summary>
+ /// Attempts to find a message descriptor by its full name.
+ /// </summary>
+ /// <param name="fullName">The full name of the message, which is the dot-separated
+ /// combination of package, containing messages and message name</param>
+ /// <returns>The message descriptor corresponding to <paramref name="fullName"/> or null
+ /// if there is no such message descriptor.</returns>
+ public MessageDescriptor Find(string fullName)
+ {
+ MessageDescriptor ret;
+ // Ignore the return value as ret will end up with the right value either way.
+ fullNameToMessageMap.TryGetValue(fullName, out ret);
+ return ret;
+ }
+
+ /// <summary>
+ /// Creates a type registry from the specified set of file descriptors.
+ /// </summary>
+ /// <remarks>
+ /// This is a convenience overload for <see cref="FromFiles(IEnumerable{FileDescriptor})"/>
+ /// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>.
+ /// </remarks>
+ /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param>
+ /// <returns>A type registry for the given files.</returns>
+ public static TypeRegistry FromFiles(params FileDescriptor[] fileDescriptors)
+ {
+ return FromFiles((IEnumerable<FileDescriptor>) fileDescriptors);
+ }
+
+ /// <summary>
+ /// Creates a type registry from the specified set of file descriptors.
+ /// </summary>
+ /// <remarks>
+ /// All message types within all the specified files are added to the registry, and
+ /// the dependencies of the specified files are also added, recursively.
+ /// </remarks>
+ /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param>
+ /// <returns>A type registry for the given files.</returns>
+ public static TypeRegistry FromFiles(IEnumerable<FileDescriptor> fileDescriptors)
+ {
+ ProtoPreconditions.CheckNotNull(fileDescriptors, nameof(fileDescriptors));
+ var builder = new Builder();
+ foreach (var file in fileDescriptors)
+ {
+ builder.AddFile(file);
+ }
+ return builder.Build();
+ }
+
+ /// <summary>
+ /// Creates a type registry from the file descriptor parents of the specified set of message descriptors.
+ /// </summary>
+ /// <remarks>
+ /// This is a convenience overload for <see cref="FromMessages(IEnumerable{MessageDescriptor})"/>
+ /// to allow calls such as <c>TypeRegistry.FromFiles(descriptor1, descriptor2)</c>.
+ /// </remarks>
+ /// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry.
+ /// Must not contain null values.</param>
+ /// <returns>A type registry for the given files.</returns>
+ public static TypeRegistry FromMessages(params MessageDescriptor[] messageDescriptors)
+ {
+ return FromMessages((IEnumerable<MessageDescriptor>) messageDescriptors);
+ }
+
+ /// <summary>
+ /// Creates a type registry from the file descriptor parents of the specified set of message descriptors.
+ /// </summary>
+ /// <remarks>
+ /// The specified message descriptors are only used to identify their file descriptors; the returned registry
+ /// contains all the types within the file descriptors which contain the specified message descriptors (and
+ /// the dependencies of those files), not just the specified messages.
+ /// </remarks>
+ /// <param name="messageDescriptors">The set of message descriptors to use to identify file descriptors to include in the registry.
+ /// Must not contain null values.</param>
+ /// <returns>A type registry for the given files.</returns>
+ public static TypeRegistry FromMessages(IEnumerable<MessageDescriptor> messageDescriptors)
+ {
+ ProtoPreconditions.CheckNotNull(messageDescriptors, nameof(messageDescriptors));
+ return FromFiles(messageDescriptors.Select(md => md.File));
+ }
+
+ /// <summary>
+ /// Builder class which isn't exposed, but acts as a convenient alternative to passing round two dictionaries in recursive calls.
+ /// </summary>
+ private class Builder
+ {
+ private readonly Dictionary<string, MessageDescriptor> types;
+ private readonly HashSet<string> fileDescriptorNames;
+
+ internal Builder()
+ {
+ types = new Dictionary<string, MessageDescriptor>();
+ fileDescriptorNames = new HashSet<string>();
+ }
+
+ internal void AddFile(FileDescriptor fileDescriptor)
+ {
+ if (!fileDescriptorNames.Add(fileDescriptor.Name))
+ {
+ return;
+ }
+ foreach (var dependency in fileDescriptor.Dependencies)
+ {
+ AddFile(dependency);
+ }
+ foreach (var message in fileDescriptor.MessageTypes)
+ {
+ AddMessage(message);
+ }
+ }
+
+ private void AddMessage(MessageDescriptor messageDescriptor)
+ {
+ foreach (var nestedType in messageDescriptor.NestedTypes)
+ {
+ AddMessage(nestedType);
+ }
+ // This will overwrite any previous entry. Given that each file should
+ // only be added once, this could be a problem such as package A.B with type C,
+ // and package A with type B.C... it's unclear what we should do in that case.
+ types[messageDescriptor.FullName] = messageDescriptor;
+ }
+
+ internal TypeRegistry Build()
+ {
+ return new TypeRegistry(types);
+ }
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
index c69bec04..48cd99a1 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs
@@ -27,10 +27,10 @@ namespace Google.Protobuf.WellKnownTypes {
"JgoDQW55EhAKCHR5cGVfdXJsGAEgASgJEg0KBXZhbHVlGAIgASgMQksKE2Nv",
"bS5nb29nbGUucHJvdG9idWZCCEFueVByb3RvUAGgAQGiAgNHUEKqAh5Hb29n",
"bGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Any), global::Google.Protobuf.WellKnownTypes.Any.Parser, new[]{ "TypeUrl", "Value" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Any), global::Google.Protobuf.WellKnownTypes.Any.Parser, new[]{ "TypeUrl", "Value" }, null, null, null)
}));
}
#endregion
@@ -61,8 +61,8 @@ namespace Google.Protobuf.WellKnownTypes {
///
/// If the embedded message type is well-known and has a custom JSON
/// representation, that representation will be embedded adding a field
- /// `value` which holds the custom JSON in addition to the the `@type`
- /// field. Example (for message [google.protobuf.Duration][google.protobuf.Duration]):
+ /// `value` which holds the custom JSON in addition to the `@type`
+ /// field. Example (for message [google.protobuf.Duration][]):
///
/// {
/// "@type": "type.googleapis.com/google.protobuf.Duration",
@@ -110,7 +110,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// * If no schema is provided, `https` is assumed.
/// * The last segment of the URL's path must represent the fully
/// qualified name of the type (as in `path/google.protobuf.Duration`).
- /// * An HTTP GET on the URL must yield a [google.protobuf.Type][google.protobuf.Type]
+ /// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
/// value in binary format, or produce an error.
/// * Applications are allowed to cache lookup results based on the
/// URL, or have them precompiled into a binary to avoid any
@@ -124,7 +124,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string TypeUrl {
get { return typeUrl_; }
set {
- typeUrl_ = pb::Preconditions.CheckNotNull(value, "value");
+ typeUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -137,7 +137,7 @@ namespace Google.Protobuf.WellKnownTypes {
public pb::ByteString Value {
get { return value_; }
set {
- value_ = pb::Preconditions.CheckNotNull(value, "value");
+ value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -165,7 +165,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
index 082f7432..9d43856e 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs
@@ -72,7 +72,7 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>An Any message with the content and type URL of <paramref name="message"/>.</returns>
public static Any Pack(IMessage message)
{
- Preconditions.CheckNotNull(message, "message");
+ ProtoPreconditions.CheckNotNull(message, "message");
return new Any { TypeUrl = GetTypeUrl(message.Descriptor), Value = message.ToByteString() };
}
}
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
index 23528994..de7aea3a 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs
@@ -39,12 +39,12 @@ namespace Google.Protobuf.WellKnownTypes {
"eCIjCgVNaXhpbhIMCgRuYW1lGAEgASgJEgwKBHJvb3QYAiABKAlCSwoTY29t",
"Lmdvb2dsZS5wcm90b2J1ZkIIQXBpUHJvdG9QAaABAaICA0dQQqoCHkdvb2ds",
"ZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJvdG8z"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TypeReflection.Descriptor, },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Api), global::Google.Protobuf.WellKnownTypes.Api.Parser, new[]{ "Name", "Methods", "Options", "Version", "SourceContext", "Mixins", "Syntax" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Method), global::Google.Protobuf.WellKnownTypes.Method.Parser, new[]{ "Name", "RequestTypeUrl", "RequestStreaming", "ResponseTypeUrl", "ResponseStreaming", "Options", "Syntax" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Mixin), global::Google.Protobuf.WellKnownTypes.Mixin.Parser, new[]{ "Name", "Root" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Api), global::Google.Protobuf.WellKnownTypes.Api.Parser, new[]{ "Name", "Methods", "Options", "Version", "SourceContext", "Mixins", "Syntax" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Method), global::Google.Protobuf.WellKnownTypes.Method.Parser, new[]{ "Name", "RequestTypeUrl", "RequestStreaming", "ResponseTypeUrl", "ResponseStreaming", "Options", "Syntax" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Mixin), global::Google.Protobuf.WellKnownTypes.Mixin.Parser, new[]{ "Name", "Root" }, null, null, null)
}));
}
#endregion
@@ -97,7 +97,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -153,7 +153,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Version {
get { return version_; }
set {
- version_ = pb::Preconditions.CheckNotNull(value, "value");
+ version_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -230,7 +230,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -388,7 +388,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -401,7 +401,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string RequestTypeUrl {
get { return requestTypeUrl_; }
set {
- requestTypeUrl_ = pb::Preconditions.CheckNotNull(value, "value");
+ requestTypeUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -427,7 +427,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string ResponseTypeUrl {
get { return responseTypeUrl_; }
set {
- responseTypeUrl_ = pb::Preconditions.CheckNotNull(value, "value");
+ responseTypeUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -503,7 +503,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -653,7 +653,6 @@ namespace Google.Protobuf.WellKnownTypes {
///
/// package google.storage.v2;
/// service Storage {
- /// // (-- see AccessControl.GetAcl --)
/// rpc GetAcl(GetAclRequest) returns (Acl);
///
/// // Get a data record.
@@ -741,7 +740,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -755,7 +754,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Root {
get { return root_; }
set {
- root_ = pb::Preconditions.CheckNotNull(value, "value");
+ root_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -783,7 +782,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
index 7b3767a2..73e221d4 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs
@@ -28,10 +28,10 @@ namespace Google.Protobuf.WellKnownTypes {
"ASgFQlAKE2NvbS5nb29nbGUucHJvdG9idWZCDUR1cmF0aW9uUHJvdG9QAaAB",
"AaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJv",
"dG8z"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Duration), global::Google.Protobuf.WellKnownTypes.Duration.Parser, new[]{ "Seconds", "Nanos" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Duration), global::Google.Protobuf.WellKnownTypes.Duration.Parser, new[]{ "Seconds", "Nanos" }, null, null, null)
}));
}
#endregion
@@ -164,7 +164,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
index 324f48fc..f164bfd1 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
@@ -31,12 +31,14 @@
#endregion
using System;
+using System.Globalization;
+using System.Text;
namespace Google.Protobuf.WellKnownTypes
{
// Manually-written partial class for the Duration well-known type,
// providing a conversion to TimeSpan and convenience operators.
- public partial class Duration
+ public partial class Duration : ICustomDiagnosticMessage
{
/// <summary>
/// The number of nanoseconds in a second.
@@ -57,15 +59,37 @@ namespace Google.Protobuf.WellKnownTypes
/// </summary>
public const long MinSeconds = -315576000000L;
+ internal const int MaxNanoseconds = NanosecondsPerSecond - 1;
+ internal const int MinNanoseconds = -NanosecondsPerSecond + 1;
+
+ internal static bool IsNormalized(long seconds, int nanoseconds)
+ {
+ // Simple boundaries
+ if (seconds < MinSeconds || seconds > MaxSeconds ||
+ nanoseconds < MinNanoseconds || nanoseconds > MaxNanoseconds)
+ {
+ return false;
+ }
+ // We only have a problem is one is strictly negative and the other is
+ // strictly positive.
+ return Math.Sign(seconds) * Math.Sign(nanoseconds) != -1;
+ }
+
/// <summary>
/// Converts this <see cref="Duration"/> to a <see cref="TimeSpan"/>.
/// </summary>
/// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks>
/// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns>
+ /// <exception cref="InvalidOperationException">This value isn't a valid normalized duration, as
+ /// described in the documentation.</exception>
public TimeSpan ToTimeSpan()
{
checked
{
+ if (!IsNormalized(Seconds, Nanos))
+ {
+ throw new InvalidOperationException("Duration was not a valid normalized duration");
+ }
long ticks = Seconds * TimeSpan.TicksPerSecond + Nanos / NanosecondsPerTick;
return TimeSpan.FromTicks(ticks);
}
@@ -94,7 +118,7 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>The negated value of this duration.</returns>
public static Duration operator -(Duration value)
{
- Preconditions.CheckNotNull(value, "value");
+ ProtoPreconditions.CheckNotNull(value, "value");
checked
{
return Normalize(-value.Seconds, -value.Nanos);
@@ -109,8 +133,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns></returns>
public static Duration operator +(Duration lhs, Duration rhs)
{
- Preconditions.CheckNotNull(lhs, "lhs");
- Preconditions.CheckNotNull(rhs, "rhs");
+ ProtoPreconditions.CheckNotNull(lhs, "lhs");
+ ProtoPreconditions.CheckNotNull(rhs, "rhs");
checked
{
return Normalize(lhs.Seconds + rhs.Seconds, lhs.Nanos + rhs.Nanos);
@@ -125,8 +149,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>The difference between the two specified durations.</returns>
public static Duration operator -(Duration lhs, Duration rhs)
{
- Preconditions.CheckNotNull(lhs, "lhs");
- Preconditions.CheckNotNull(rhs, "rhs");
+ ProtoPreconditions.CheckNotNull(lhs, "lhs");
+ ProtoPreconditions.CheckNotNull(rhs, "rhs");
checked
{
return Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos);
@@ -157,5 +181,90 @@ namespace Google.Protobuf.WellKnownTypes
}
return new Duration { Seconds = seconds, Nanos = nanoseconds };
}
+
+ /// <summary>
+ /// Converts a duration specified in seconds/nanoseconds to a string.
+ /// </summary>
+ /// <remarks>
+ /// If the value is a normalized duration in the range described in <c>duration.proto</c>,
+ /// <paramref name="diagnosticOnly"/> is ignored. Otherwise, if the parameter is <c>true</c>,
+ /// a JSON object with a warning is returned; if it is <c>false</c>, an <see cref="InvalidOperationException"/> is thrown.
+ /// </remarks>
+ /// <param name="seconds">Seconds portion of the duration.</param>
+ /// <param name="nanoseconds">Nanoseconds portion of the duration.</param>
+ /// <param name="diagnosticOnly">Determines the handling of non-normalized values</param>
+ /// <exception cref="InvalidOperationException">The represented duration is invalid, and <paramref name="diagnosticOnly"/> is <c>false</c>.</exception>
+ internal static string ToJson(long seconds, int nanoseconds, bool diagnosticOnly)
+ {
+ if (IsNormalized(seconds, nanoseconds))
+ {
+ var builder = new StringBuilder();
+ builder.Append('"');
+ // The seconds part will normally provide the minus sign if we need it, but not if it's 0...
+ if (seconds == 0 && nanoseconds < 0)
+ {
+ builder.Append('-');
+ }
+
+ builder.Append(seconds.ToString("d", CultureInfo.InvariantCulture));
+ AppendNanoseconds(builder, Math.Abs(nanoseconds));
+ builder.Append("s\"");
+ return builder.ToString();
+ }
+ if (diagnosticOnly)
+ {
+ // Note: the double braces here are escaping for braces in format strings.
+ return string.Format(CultureInfo.InvariantCulture,
+ "{{ \"@warning\": \"Invalid Duration\", \"seconds\": \"{0}\", \"nanos\": {1} }}",
+ seconds,
+ nanoseconds);
+ }
+ else
+ {
+ throw new InvalidOperationException("Non-normalized duration value");
+ }
+ }
+
+ /// <summary>
+ /// Returns a string representation of this <see cref="Duration"/> for diagnostic purposes.
+ /// </summary>
+ /// <remarks>
+ /// Normally the returned value will be a JSON string value (including leading and trailing quotes) but
+ /// when the value is non-normalized or out of range, a JSON object representation will be returned
+ /// instead, including a warning. This is to avoid exceptions being thrown when trying to
+ /// diagnose problems - the regular JSON formatter will still throw an exception for non-normalized
+ /// values.
+ /// </remarks>
+ /// <returns>A string representation of this value.</returns>
+ public string ToDiagnosticString()
+ {
+ return ToJson(Seconds, Nanos, true);
+ }
+
+ /// <summary>
+ /// Appends a number of nanoseconds to a StringBuilder. Either 0 digits are added (in which
+ /// case no "." is appended), or 3 6 or 9 digits. This is internal for use in Timestamp as well
+ /// as Duration.
+ /// </summary>
+ internal static void AppendNanoseconds(StringBuilder builder, int nanos)
+ {
+ if (nanos != 0)
+ {
+ builder.Append('.');
+ // Output to 3, 6 or 9 digits.
+ if (nanos % 1000000 == 0)
+ {
+ builder.Append((nanos / 1000000).ToString("d3", CultureInfo.InvariantCulture));
+ }
+ else if (nanos % 1000 == 0)
+ {
+ builder.Append((nanos / 1000).ToString("d6", CultureInfo.InvariantCulture));
+ }
+ else
+ {
+ builder.Append(nanos.ToString("d9", CultureInfo.InvariantCulture));
+ }
+ }
+ }
}
}
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
index 3b09f1e8..6cad4124 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs
@@ -24,13 +24,13 @@ namespace Google.Protobuf.WellKnownTypes {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"Chtnb29nbGUvcHJvdG9idWYvZW1wdHkucHJvdG8SD2dvb2dsZS5wcm90b2J1",
- "ZiIHCgVFbXB0eUJNChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3Rv",
- "UAGgAQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNi",
- "BnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ "ZiIHCgVFbXB0eUJQChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3Rv",
+ "UAGgAQH4AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlw",
+ "ZXNiBnByb3RvMw=="));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Empty), global::Google.Protobuf.WellKnownTypes.Empty.Parser, null, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Empty), global::Google.Protobuf.WellKnownTypes.Empty.Parser, null, null, null, null)
}));
}
#endregion
@@ -94,7 +94,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
index 3ec74cbe..2ba2b96f 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs
@@ -27,10 +27,10 @@ namespace Google.Protobuf.WellKnownTypes {
"b3RvYnVmIhoKCUZpZWxkTWFzaxINCgVwYXRocxgBIAMoCUJRChNjb20uZ29v",
"Z2xlLnByb3RvYnVmQg5GaWVsZE1hc2tQcm90b1ABoAEBogIDR1BCqgIeR29v",
"Z2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90bzM="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FieldMask), global::Google.Protobuf.WellKnownTypes.FieldMask.Parser, new[]{ "Paths" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FieldMask), global::Google.Protobuf.WellKnownTypes.FieldMask.Parser, new[]{ "Paths" }, null, null, null)
}));
}
#endregion
@@ -69,7 +69,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// z: 8
///
/// The result will not contain specific values for fields x,y and z
- /// (there value will be set to the default, and omitted in proto text
+ /// (their value will be set to the default, and omitted in proto text
/// output):
///
/// f {
@@ -221,7 +221,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs
new file mode 100644
index 00000000..df1292dc
--- /dev/null
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs
@@ -0,0 +1,122 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Google.Protobuf.WellKnownTypes
+{
+ // Manually-written partial class for the FieldMask well-known type.
+ public partial class FieldMask : ICustomDiagnosticMessage
+ {
+ /// <summary>
+ /// Converts a timestamp specified in seconds/nanoseconds to a string.
+ /// </summary>
+ /// <remarks>
+ /// If the value is a normalized duration in the range described in <c>field_mask.proto</c>,
+ /// <paramref name="diagnosticOnly"/> is ignored. Otherwise, if the parameter is <c>true</c>,
+ /// a JSON object with a warning is returned; if it is <c>false</c>, an <see cref="InvalidOperationException"/> is thrown.
+ /// </remarks>
+ /// <param name="paths">Paths in the field mask</param>
+ /// <param name="diagnosticOnly">Determines the handling of non-normalized values</param>
+ /// <exception cref="InvalidOperationException">The represented duration is invalid, and <paramref name="diagnosticOnly"/> is <c>false</c>.</exception>
+ internal static string ToJson(IList<string> paths, bool diagnosticOnly)
+ {
+ var firstInvalid = paths.FirstOrDefault(p => !ValidatePath(p));
+ if (firstInvalid == null)
+ {
+ var builder = new StringBuilder();
+ JsonFormatter.WriteString(builder, string.Join(",", paths.Select(JsonFormatter.ToCamelCase)));
+ return builder.ToString();
+ }
+ else
+ {
+ if (diagnosticOnly)
+ {
+ var builder = new StringBuilder();
+ builder.Append("{ \"@warning\": \"Invalid FieldMask\", \"paths\": ");
+ JsonFormatter.Default.WriteList(builder, (IList) paths);
+ builder.Append(" }");
+ return builder.ToString();
+ }
+ else
+ {
+ throw new InvalidOperationException($"Invalid field mask to be converted to JSON: {firstInvalid}");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Camel-case converter with added strictness for field mask formatting.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The field mask is invalid for JSON representation</exception>
+ private static bool ValidatePath(string input)
+ {
+ for (int i = 0; i < input.Length; i++)
+ {
+ char c = input[i];
+ if (c >= 'A' && c <= 'Z')
+ {
+ return false;
+ }
+ if (c == '_' && i < input.Length - 1)
+ {
+ char next = input[i + 1];
+ if (next < 'a' || next > 'z')
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns a string representation of this <see cref="FieldMask"/> for diagnostic purposes.
+ /// </summary>
+ /// <remarks>
+ /// Normally the returned value will be a JSON string value (including leading and trailing quotes) but
+ /// when the value is non-normalized or out of range, a JSON object representation will be returned
+ /// instead, including a warning. This is to avoid exceptions being thrown when trying to
+ /// diagnose problems - the regular JSON formatter will still throw an exception for non-normalized
+ /// values.
+ /// </remarks>
+ /// <returns>A string representation of this value.</returns>
+ public string ToDiagnosticString()
+ {
+ return ToJson(Paths, true);
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs b/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
index cf12e6c6..a235ecef 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs
@@ -28,10 +28,10 @@ namespace Google.Protobuf.WellKnownTypes {
"CUJVChNjb20uZ29vZ2xlLnByb3RvYnVmQhJTb3VyY2VDb250ZXh0UHJvdG9Q",
"AaABAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IG",
"cHJvdG8z"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.SourceContext), global::Google.Protobuf.WellKnownTypes.SourceContext.Parser, new[]{ "FileName" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.SourceContext), global::Google.Protobuf.WellKnownTypes.SourceContext.Parser, new[]{ "FileName" }, null, null, null)
}));
}
#endregion
@@ -79,7 +79,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string FileName {
get { return fileName_; }
set {
- fileName_ = pb::Preconditions.CheckNotNull(value, "value");
+ fileName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -105,7 +105,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
index 54d5a806..8e2ce8cf 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
@@ -37,12 +37,12 @@ namespace Google.Protobuf.WellKnownTypes {
"QUxVRRAAQk4KE2NvbS5nb29nbGUucHJvdG9idWZCC1N0cnVjdFByb3RvUAGg",
"AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnBy",
"b3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.NullValue), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Struct), global::Google.Protobuf.WellKnownTypes.Struct.Parser, new[]{ "Fields" }, null, null, new pbr::GeneratedCodeInfo[] { null, }),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Value), global::Google.Protobuf.WellKnownTypes.Value.Parser, new[]{ "NullValue", "NumberValue", "StringValue", "BoolValue", "StructValue", "ListValue" }, new[]{ "Kind" }, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.ListValue), global::Google.Protobuf.WellKnownTypes.ListValue.Parser, new[]{ "Values" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.NullValue), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Struct), global::Google.Protobuf.WellKnownTypes.Struct.Parser, new[]{ "Fields" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Value), global::Google.Protobuf.WellKnownTypes.Value.Parser, new[]{ "NullValue", "NumberValue", "StringValue", "BoolValue", "StructValue", "ListValue" }, new[]{ "Kind" }, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.ListValue), global::Google.Protobuf.WellKnownTypes.ListValue.Parser, new[]{ "Values" }, null, null, null)
}));
}
#endregion
@@ -136,7 +136,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -262,7 +262,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string StringValue {
get { return kindCase_ == KindOneofCase.StringValue ? (string) kind_ : ""; }
set {
- kind_ = pb::Preconditions.CheckNotNull(value, "value");
+ kind_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
kindCase_ = KindOneofCase.StringValue;
}
}
@@ -361,7 +361,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -555,7 +555,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
index a26b2bf8..318b0f61 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs
@@ -25,13 +25,13 @@ namespace Google.Protobuf.WellKnownTypes {
string.Concat(
"Ch9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnByb3RvEg9nb29nbGUucHJv",
"dG9idWYiKwoJVGltZXN0YW1wEg8KB3NlY29uZHMYASABKAMSDQoFbmFub3MY",
- "AiABKAVCUQoTY29tLmdvb2dsZS5wcm90b2J1ZkIOVGltZXN0YW1wUHJvdG9Q",
- "AaABAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IG",
- "cHJvdG8z"));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ "AiABKAVCVAoTY29tLmdvb2dsZS5wcm90b2J1ZkIOVGltZXN0YW1wUHJvdG9Q",
+ "AaABAfgBAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBl",
+ "c2IGcHJvdG8z"));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Timestamp), global::Google.Protobuf.WellKnownTypes.Timestamp.Parser, new[]{ "Seconds", "Nanos" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Timestamp), global::Google.Protobuf.WellKnownTypes.Timestamp.Parser, new[]{ "Seconds", "Nanos" }, null, null, null)
}));
}
#endregion
@@ -175,7 +175,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
index d284acd6..aa403473 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
@@ -31,15 +31,25 @@
#endregion
using System;
+using System.Globalization;
+using System.Text;
namespace Google.Protobuf.WellKnownTypes
{
- public partial class Timestamp
+ public partial class Timestamp : ICustomDiagnosticMessage
{
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- private static readonly long BclSecondsAtUnixEpoch = UnixEpoch.Ticks / TimeSpan.TicksPerSecond;
- internal static readonly long UnixSecondsAtBclMinValue = -BclSecondsAtUnixEpoch;
- internal static readonly long UnixSecondsAtBclMaxValue = (DateTime.MaxValue.Ticks / TimeSpan.TicksPerSecond) - BclSecondsAtUnixEpoch;
+ // Constants determined programmatically, but then hard-coded so they can be constant expressions.
+ private const long BclSecondsAtUnixEpoch = 62135596800;
+ internal const long UnixSecondsAtBclMaxValue = 253402300799;
+ internal const long UnixSecondsAtBclMinValue = -BclSecondsAtUnixEpoch;
+ internal const int MaxNanos = Duration.NanosecondsPerSecond - 1;
+
+ private static bool IsNormalized(long seconds, int nanoseconds) =>
+ nanoseconds >= 0 &&
+ nanoseconds <= MaxNanos &&
+ seconds >= UnixSecondsAtBclMinValue &&
+ seconds <= UnixSecondsAtBclMaxValue;
/// <summary>
/// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>.
@@ -49,8 +59,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>The difference between the two specified timestamps.</returns>
public static Duration operator -(Timestamp lhs, Timestamp rhs)
{
- Preconditions.CheckNotNull(lhs, "lhs");
- Preconditions.CheckNotNull(rhs, "rhs");
+ ProtoPreconditions.CheckNotNull(lhs, "lhs");
+ ProtoPreconditions.CheckNotNull(rhs, "rhs");
checked
{
return Duration.Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos);
@@ -65,8 +75,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>The result of adding the duration to the timestamp.</returns>
public static Timestamp operator +(Timestamp lhs, Duration rhs)
{
- Preconditions.CheckNotNull(lhs, "lhs");
- Preconditions.CheckNotNull(rhs, "rhs");
+ ProtoPreconditions.CheckNotNull(lhs, "lhs");
+ ProtoPreconditions.CheckNotNull(rhs, "rhs");
checked
{
return Normalize(lhs.Seconds + rhs.Seconds, lhs.Nanos + rhs.Nanos);
@@ -81,8 +91,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>The result of subtracting the duration from the timestamp.</returns>
public static Timestamp operator -(Timestamp lhs, Duration rhs)
{
- Preconditions.CheckNotNull(lhs, "lhs");
- Preconditions.CheckNotNull(rhs, "rhs");
+ ProtoPreconditions.CheckNotNull(lhs, "lhs");
+ ProtoPreconditions.CheckNotNull(rhs, "rhs");
checked
{
return Normalize(lhs.Seconds - rhs.Seconds, lhs.Nanos - rhs.Nanos);
@@ -99,8 +109,14 @@ namespace Google.Protobuf.WellKnownTypes
/// <see cref="DateTime"/> value precisely on a second.
/// </remarks>
/// <returns>This timestamp as a <c>DateTime</c>.</returns>
+ /// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
+ /// incorrectly normalized or is outside the valid range.</exception>
public DateTime ToDateTime()
{
+ if (!IsNormalized(Seconds, Nanos))
+ {
+ throw new InvalidOperationException(@"Timestamp contains invalid values: Seconds={Seconds}; Nanos={Nanos}");
+ }
return UnixEpoch.AddSeconds(Seconds).AddTicks(Nanos / Duration.NanosecondsPerTick);
}
@@ -114,6 +130,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <see cref="DateTimeOffset"/> value precisely on a second.
/// </remarks>
/// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns>
+ /// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
+ /// incorrectly normalized or is outside the valid range.</exception>
public DateTimeOffset ToDateTimeOffset()
{
return new DateTimeOffset(ToDateTime(), TimeSpan.Zero);
@@ -165,5 +183,59 @@ namespace Google.Protobuf.WellKnownTypes
}
return new Timestamp { Seconds = seconds, Nanos = nanoseconds };
}
+
+ /// <summary>
+ /// Converts a timestamp specified in seconds/nanoseconds to a string.
+ /// </summary>
+ /// <remarks>
+ /// If the value is a normalized duration in the range described in <c>timestamp.proto</c>,
+ /// <paramref name="diagnosticOnly"/> is ignored. Otherwise, if the parameter is <c>true</c>,
+ /// a JSON object with a warning is returned; if it is <c>false</c>, an <see cref="InvalidOperationException"/> is thrown.
+ /// </remarks>
+ /// <param name="seconds">Seconds portion of the duration.</param>
+ /// <param name="nanoseconds">Nanoseconds portion of the duration.</param>
+ /// <param name="diagnosticOnly">Determines the handling of non-normalized values</param>
+ /// <exception cref="InvalidOperationException">The represented duration is invalid, and <paramref name="diagnosticOnly"/> is <c>false</c>.</exception>
+ internal static string ToJson(long seconds, int nanoseconds, bool diagnosticOnly)
+ {
+ if (IsNormalized(seconds, nanoseconds))
+ {
+ // Use .NET's formatting for the value down to the second, including an opening double quote (as it's a string value)
+ DateTime dateTime = UnixEpoch.AddSeconds(seconds);
+ var builder = new StringBuilder();
+ builder.Append('"');
+ builder.Append(dateTime.ToString("yyyy'-'MM'-'dd'T'HH:mm:ss", CultureInfo.InvariantCulture));
+ Duration.AppendNanoseconds(builder, nanoseconds);
+ builder.Append("Z\"");
+ return builder.ToString();
+ }
+ if (diagnosticOnly)
+ {
+ return string.Format(CultureInfo.InvariantCulture,
+ "{{ \"@warning\": \"Invalid Timestamp\", \"seconds\": \"{0}\", \"nanos\": {1} }}",
+ seconds,
+ nanoseconds);
+ }
+ else
+ {
+ throw new InvalidOperationException("Non-normalized timestamp value");
+ }
+ }
+
+ /// <summary>
+ /// Returns a string representation of this <see cref="Timestamp"/> for diagnostic purposes.
+ /// </summary>
+ /// <remarks>
+ /// Normally the returned value will be a JSON string value (including leading and trailing quotes) but
+ /// when the value is non-normalized or out of range, a JSON object representation will be returned
+ /// instead, including a warning. This is to avoid exceptions being thrown when trying to
+ /// diagnose problems - the regular JSON formatter will still throw an exception for non-normalized
+ /// values.
+ /// </remarks>
+ /// <returns>A string representation of this value.</returns>
+ public string ToDiagnosticString()
+ {
+ return ToJson(Seconds, Nanos, true);
+ }
}
}
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
index edd302e7..8faa1d1c 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs
@@ -30,41 +30,42 @@ namespace Google.Protobuf.WellKnownTypes {
"b2ZzGAMgAygJEigKB29wdGlvbnMYBCADKAsyFy5nb29nbGUucHJvdG9idWYu",
"T3B0aW9uEjYKDnNvdXJjZV9jb250ZXh0GAUgASgLMh4uZ29vZ2xlLnByb3Rv",
"YnVmLlNvdXJjZUNvbnRleHQSJwoGc3ludGF4GAYgASgOMhcuZ29vZ2xlLnBy",
- "b3RvYnVmLlN5bnRheCK+BQoFRmllbGQSKQoEa2luZBgBIAEoDjIbLmdvb2ds",
+ "b3RvYnVmLlN5bnRheCLVBQoFRmllbGQSKQoEa2luZBgBIAEoDjIbLmdvb2ds",
"ZS5wcm90b2J1Zi5GaWVsZC5LaW5kEjcKC2NhcmRpbmFsaXR5GAIgASgOMiIu",
"Z29vZ2xlLnByb3RvYnVmLkZpZWxkLkNhcmRpbmFsaXR5Eg4KBm51bWJlchgD",
"IAEoBRIMCgRuYW1lGAQgASgJEhAKCHR5cGVfdXJsGAYgASgJEhMKC29uZW9m",
"X2luZGV4GAcgASgFEg4KBnBhY2tlZBgIIAEoCBIoCgdvcHRpb25zGAkgAygL",
- "MhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhIRCglqc29uX25hbWUYCiABKAki",
- "yAIKBEtpbmQSEAoMVFlQRV9VTktOT1dOEAASDwoLVFlQRV9ET1VCTEUQARIO",
- "CgpUWVBFX0ZMT0FUEAISDgoKVFlQRV9JTlQ2NBADEg8KC1RZUEVfVUlOVDY0",
- "EAQSDgoKVFlQRV9JTlQzMhAFEhAKDFRZUEVfRklYRUQ2NBAGEhAKDFRZUEVf",
- "RklYRUQzMhAHEg0KCVRZUEVfQk9PTBAIEg8KC1RZUEVfU1RSSU5HEAkSDgoK",
- "VFlQRV9HUk9VUBAKEhAKDFRZUEVfTUVTU0FHRRALEg4KClRZUEVfQllURVMQ",
- "DBIPCgtUWVBFX1VJTlQzMhANEg0KCVRZUEVfRU5VTRAOEhEKDVRZUEVfU0ZJ",
- "WEVEMzIQDxIRCg1UWVBFX1NGSVhFRDY0EBASDwoLVFlQRV9TSU5UMzIQERIP",
- "CgtUWVBFX1NJTlQ2NBASInQKC0NhcmRpbmFsaXR5EhcKE0NBUkRJTkFMSVRZ",
- "X1VOS05PV04QABIYChRDQVJESU5BTElUWV9PUFRJT05BTBABEhgKFENBUkRJ",
- "TkFMSVRZX1JFUVVJUkVEEAISGAoUQ0FSRElOQUxJVFlfUkVQRUFURUQQAyLO",
- "AQoERW51bRIMCgRuYW1lGAEgASgJEi0KCWVudW12YWx1ZRgCIAMoCzIaLmdv",
- "b2dsZS5wcm90b2J1Zi5FbnVtVmFsdWUSKAoHb3B0aW9ucxgDIAMoCzIXLmdv",
- "b2dsZS5wcm90b2J1Zi5PcHRpb24SNgoOc291cmNlX2NvbnRleHQYBCABKAsy",
- "Hi5nb29nbGUucHJvdG9idWYuU291cmNlQ29udGV4dBInCgZzeW50YXgYBSAB",
- "KA4yFy5nb29nbGUucHJvdG9idWYuU3ludGF4IlMKCUVudW1WYWx1ZRIMCgRu",
- "YW1lGAEgASgJEg4KBm51bWJlchgCIAEoBRIoCgdvcHRpb25zGAMgAygLMhcu",
- "Z29vZ2xlLnByb3RvYnVmLk9wdGlvbiI7CgZPcHRpb24SDAoEbmFtZRgBIAEo",
- "CRIjCgV2YWx1ZRgCIAEoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnkqLgoGU3lu",
- "dGF4EhEKDVNZTlRBWF9QUk9UTzIQABIRCg1TWU5UQVhfUFJPVE8zEAFCTAoT",
- "Y29tLmdvb2dsZS5wcm90b2J1ZkIJVHlwZVByb3RvUAGgAQGiAgNHUEKqAh5H",
- "b29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ "MhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhIRCglqc29uX25hbWUYCiABKAkS",
+ "FQoNZGVmYXVsdF92YWx1ZRgLIAEoCSLIAgoES2luZBIQCgxUWVBFX1VOS05P",
+ "V04QABIPCgtUWVBFX0RPVUJMRRABEg4KClRZUEVfRkxPQVQQAhIOCgpUWVBF",
+ "X0lOVDY0EAMSDwoLVFlQRV9VSU5UNjQQBBIOCgpUWVBFX0lOVDMyEAUSEAoM",
+ "VFlQRV9GSVhFRDY0EAYSEAoMVFlQRV9GSVhFRDMyEAcSDQoJVFlQRV9CT09M",
+ "EAgSDwoLVFlQRV9TVFJJTkcQCRIOCgpUWVBFX0dST1VQEAoSEAoMVFlQRV9N",
+ "RVNTQUdFEAsSDgoKVFlQRV9CWVRFUxAMEg8KC1RZUEVfVUlOVDMyEA0SDQoJ",
+ "VFlQRV9FTlVNEA4SEQoNVFlQRV9TRklYRUQzMhAPEhEKDVRZUEVfU0ZJWEVE",
+ "NjQQEBIPCgtUWVBFX1NJTlQzMhAREg8KC1RZUEVfU0lOVDY0EBIidAoLQ2Fy",
+ "ZGluYWxpdHkSFwoTQ0FSRElOQUxJVFlfVU5LTk9XThAAEhgKFENBUkRJTkFM",
+ "SVRZX09QVElPTkFMEAESGAoUQ0FSRElOQUxJVFlfUkVRVUlSRUQQAhIYChRD",
+ "QVJESU5BTElUWV9SRVBFQVRFRBADIs4BCgRFbnVtEgwKBG5hbWUYASABKAkS",
+ "LQoJZW51bXZhbHVlGAIgAygLMhouZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1",
+ "ZRIoCgdvcHRpb25zGAMgAygLMhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhI2",
+ "Cg5zb3VyY2VfY29udGV4dBgEIAEoCzIeLmdvb2dsZS5wcm90b2J1Zi5Tb3Vy",
+ "Y2VDb250ZXh0EicKBnN5bnRheBgFIAEoDjIXLmdvb2dsZS5wcm90b2J1Zi5T",
+ "eW50YXgiUwoJRW51bVZhbHVlEgwKBG5hbWUYASABKAkSDgoGbnVtYmVyGAIg",
+ "ASgFEigKB29wdGlvbnMYAyADKAsyFy5nb29nbGUucHJvdG9idWYuT3B0aW9u",
+ "IjsKBk9wdGlvbhIMCgRuYW1lGAEgASgJEiMKBXZhbHVlGAIgASgLMhQuZ29v",
+ "Z2xlLnByb3RvYnVmLkFueSouCgZTeW50YXgSEQoNU1lOVEFYX1BST1RPMhAA",
+ "EhEKDVNZTlRBWF9QUk9UTzMQAUJMChNjb20uZ29vZ2xlLnByb3RvYnVmQglU",
+ "eXBlUHJvdG9QAaABAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25v",
+ "d25UeXBlc2IGcHJvdG8z"));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, },
- new pbr::GeneratedCodeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.EnumValue), global::Google.Protobuf.WellKnownTypes.EnumValue.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Option), global::Google.Protobuf.WellKnownTypes.Option.Parser, new[]{ "Name", "Value" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName", "DefaultValue" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.EnumValue), global::Google.Protobuf.WellKnownTypes.EnumValue.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Option), global::Google.Protobuf.WellKnownTypes.Option.Parser, new[]{ "Name", "Value" }, null, null, null)
}));
}
#endregion
@@ -72,15 +73,15 @@ namespace Google.Protobuf.WellKnownTypes {
}
#region Enums
/// <summary>
- /// Syntax specifies the syntax in which a service element was defined.
+ /// The syntax in which a protocol buffer element is defined.
/// </summary>
public enum Syntax {
/// <summary>
- /// Syntax "proto2"
+ /// Syntax `proto2`.
/// </summary>
SYNTAX_PROTO2 = 0,
/// <summary>
- /// Syntax "proto3"
+ /// Syntax `proto3`.
/// </summary>
SYNTAX_PROTO3 = 1,
}
@@ -89,7 +90,7 @@ namespace Google.Protobuf.WellKnownTypes {
#region Messages
/// <summary>
- /// A light-weight descriptor for a proto message type.
+ /// A protocol buffer message type.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Type : pb::IMessage<Type> {
@@ -132,7 +133,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -154,7 +155,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForString(26);
private readonly pbc::RepeatedField<string> oneofs_ = new pbc::RepeatedField<string>();
/// <summary>
- /// The list of oneof definitions.
+ /// The list of types appearing in `oneof` definitions in this type.
/// </summary>
public pbc::RepeatedField<string> Oneofs {
get { return oneofs_; }
@@ -166,7 +167,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(34, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
- /// The proto options.
+ /// The protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@@ -230,7 +231,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -330,7 +331,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
/// <summary>
- /// Field represents a single field of a message type.
+ /// A single field of a message type.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Field : pb::IMessage<Field> {
@@ -361,6 +362,7 @@ namespace Google.Protobuf.WellKnownTypes {
packed_ = other.packed_;
options_ = other.options_.Clone();
jsonName_ = other.jsonName_;
+ defaultValue_ = other.defaultValue_;
}
public Field Clone() {
@@ -371,7 +373,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int KindFieldNumber = 1;
private global::Google.Protobuf.WellKnownTypes.Field.Types.Kind kind_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN;
/// <summary>
- /// The field kind.
+ /// The field type.
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Field.Types.Kind Kind {
get { return kind_; }
@@ -384,7 +386,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int CardinalityFieldNumber = 2;
private global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality cardinality_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN;
/// <summary>
- /// The field cardinality, i.e. optional/required/repeated.
+ /// The field cardinality.
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality Cardinality {
get { return cardinality_; }
@@ -397,7 +399,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int NumberFieldNumber = 3;
private int number_;
/// <summary>
- /// The proto field number.
+ /// The field number.
/// </summary>
public int Number {
get { return number_; }
@@ -415,7 +417,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -423,13 +425,13 @@ namespace Google.Protobuf.WellKnownTypes {
public const int TypeUrlFieldNumber = 6;
private string typeUrl_ = "";
/// <summary>
- /// The type URL (without the scheme) when the type is MESSAGE or ENUM,
- /// such as `type.googleapis.com/google.protobuf.Empty`.
+ /// The field type URL, without the scheme, for message or enumeration
+ /// types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
/// </summary>
public string TypeUrl {
get { return typeUrl_; }
set {
- typeUrl_ = pb::Preconditions.CheckNotNull(value, "value");
+ typeUrl_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -437,7 +439,8 @@ namespace Google.Protobuf.WellKnownTypes {
public const int OneofIndexFieldNumber = 7;
private int oneofIndex_;
/// <summary>
- /// Index in Type.oneofs. Starts at 1. Zero means no oneof mapping.
+ /// The index of the field type in `Type.oneofs`, for message or enumeration
+ /// types. The first type has index 1; zero means the type is not in the list.
/// </summary>
public int OneofIndex {
get { return oneofIndex_; }
@@ -465,7 +468,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(74, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
- /// The proto options.
+ /// The protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@@ -475,12 +478,25 @@ namespace Google.Protobuf.WellKnownTypes {
public const int JsonNameFieldNumber = 10;
private string jsonName_ = "";
/// <summary>
- /// The JSON name for this field.
+ /// The field JSON name.
/// </summary>
public string JsonName {
get { return jsonName_; }
set {
- jsonName_ = pb::Preconditions.CheckNotNull(value, "value");
+ jsonName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+ }
+ }
+
+ /// <summary>Field number for the "default_value" field.</summary>
+ public const int DefaultValueFieldNumber = 11;
+ private string defaultValue_ = "";
+ /// <summary>
+ /// The string value of the default value of this field. Proto2 syntax only.
+ /// </summary>
+ public string DefaultValue {
+ get { return defaultValue_; }
+ set {
+ defaultValue_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -504,6 +520,7 @@ namespace Google.Protobuf.WellKnownTypes {
if (Packed != other.Packed) return false;
if(!options_.Equals(other.options_)) return false;
if (JsonName != other.JsonName) return false;
+ if (DefaultValue != other.DefaultValue) return false;
return true;
}
@@ -518,11 +535,12 @@ namespace Google.Protobuf.WellKnownTypes {
if (Packed != false) hash ^= Packed.GetHashCode();
hash ^= options_.GetHashCode();
if (JsonName.Length != 0) hash ^= JsonName.GetHashCode();
+ if (DefaultValue.Length != 0) hash ^= DefaultValue.GetHashCode();
return hash;
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -559,6 +577,10 @@ namespace Google.Protobuf.WellKnownTypes {
output.WriteRawTag(82);
output.WriteString(JsonName);
}
+ if (DefaultValue.Length != 0) {
+ output.WriteRawTag(90);
+ output.WriteString(DefaultValue);
+ }
}
public int CalculateSize() {
@@ -588,6 +610,9 @@ namespace Google.Protobuf.WellKnownTypes {
if (JsonName.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(JsonName);
}
+ if (DefaultValue.Length != 0) {
+ size += 1 + pb::CodedOutputStream.ComputeStringSize(DefaultValue);
+ }
return size;
}
@@ -620,6 +645,9 @@ namespace Google.Protobuf.WellKnownTypes {
if (other.JsonName.Length != 0) {
JsonName = other.JsonName;
}
+ if (other.DefaultValue.Length != 0) {
+ DefaultValue = other.DefaultValue;
+ }
}
public void MergeFrom(pb::CodedInputStream input) {
@@ -665,6 +693,10 @@ namespace Google.Protobuf.WellKnownTypes {
JsonName = input.ReadString();
break;
}
+ case 90: {
+ DefaultValue = input.ReadString();
+ break;
+ }
}
}
}
@@ -674,7 +706,7 @@ namespace Google.Protobuf.WellKnownTypes {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial class Types {
/// <summary>
- /// Kind represents a basic field type.
+ /// Basic field types.
/// </summary>
public enum Kind {
/// <summary>
@@ -718,7 +750,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// </summary>
TYPE_STRING = 9,
/// <summary>
- /// Field type group (deprecated proto2 type)
+ /// Field type group. Proto2 syntax only, and deprecated.
/// </summary>
TYPE_GROUP = 10,
/// <summary>
@@ -756,12 +788,11 @@ namespace Google.Protobuf.WellKnownTypes {
}
/// <summary>
- /// Cardinality represents whether a field is optional, required, or
- /// repeated.
+ /// Whether a field is optional, required, or repeated.
/// </summary>
public enum Cardinality {
/// <summary>
- /// The field cardinality is unknown. Typically an error condition.
+ /// For fields with unknown cardinality.
/// </summary>
CARDINALITY_UNKNOWN = 0,
/// <summary>
@@ -769,7 +800,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// </summary>
CARDINALITY_OPTIONAL = 1,
/// <summary>
- /// For required fields. Not used for proto3.
+ /// For required fields. Proto2 syntax only.
/// </summary>
CARDINALITY_REQUIRED = 2,
/// <summary>
@@ -826,7 +857,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -848,7 +879,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(26, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
- /// Proto options for the enum type.
+ /// Protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@@ -910,7 +941,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1043,7 +1074,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1066,7 +1097,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(26, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
- /// Proto options for the enum value.
+ /// Protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@@ -1098,7 +1129,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -1164,7 +1195,8 @@ namespace Google.Protobuf.WellKnownTypes {
}
/// <summary>
- /// Proto option attached to messages/fields/enums etc.
+ /// A protocol buffer option, which can be attached to a message, field,
+ /// enumeration, etc.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Option : pb::IMessage<Option> {
@@ -1198,12 +1230,12 @@ namespace Google.Protobuf.WellKnownTypes {
public const int NameFieldNumber = 1;
private string name_ = "";
/// <summary>
- /// Proto option name.
+ /// The option's name. For example, `"java_package"`.
/// </summary>
public string Name {
get { return name_; }
set {
- name_ = pb::Preconditions.CheckNotNull(value, "value");
+ name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1211,7 +1243,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int ValueFieldNumber = 2;
private global::Google.Protobuf.WellKnownTypes.Any value_;
/// <summary>
- /// Proto option value.
+ /// The option's value. For example, `"com.google.protobuf"`.
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Any Value {
get { return value_; }
@@ -1244,7 +1276,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/ValuePartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/ValuePartial.cs
index b4b6c05e..d34b560d 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/ValuePartial.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/ValuePartial.cs
@@ -41,7 +41,7 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>A newly-created Value message with the given value.</returns>
public static Value ForString(string value)
{
- Preconditions.CheckNotNull(value, "value");
+ ProtoPreconditions.CheckNotNull(value, "value");
return new Value { StringValue = value };
}
@@ -81,7 +81,7 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>A newly-created Value message an initial list value.</returns>
public static Value ForList(params Value[] values)
{
- Preconditions.CheckNotNull(values, "values");
+ ProtoPreconditions.CheckNotNull(values, "values");
return new Value { ListValue = new ListValue { Values = { values } } };
}
@@ -92,7 +92,7 @@ namespace Google.Protobuf.WellKnownTypes
/// <returns>A newly-created Value message an initial struct value.</returns>
public static Value ForStruct(Struct value)
{
- Preconditions.CheckNotNull(value, "value");
+ ProtoPreconditions.CheckNotNull(value, "value");
return new Value { StructValue = value };
}
}
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
index 9fa6c2ec..d6796d1a 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs
@@ -29,21 +29,22 @@ namespace Google.Protobuf.WellKnownTypes {
"KAMiHAoLVUludDY0VmFsdWUSDQoFdmFsdWUYASABKAQiGwoKSW50MzJWYWx1",
"ZRINCgV2YWx1ZRgBIAEoBSIcCgtVSW50MzJWYWx1ZRINCgV2YWx1ZRgBIAEo",
"DSIaCglCb29sVmFsdWUSDQoFdmFsdWUYASABKAgiHAoLU3RyaW5nVmFsdWUS",
- "DQoFdmFsdWUYASABKAkiGwoKQnl0ZXNWYWx1ZRINCgV2YWx1ZRgBIAEoDEJQ",
- "ChNjb20uZ29vZ2xlLnByb3RvYnVmQg1XcmFwcGVyc1Byb3RvUAGgAQGiAgNH",
- "UEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
- descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ "DQoFdmFsdWUYASABKAkiGwoKQnl0ZXNWYWx1ZRINCgV2YWx1ZRgBIAEoDEJT",
+ "ChNjb20uZ29vZ2xlLnByb3RvYnVmQg1XcmFwcGVyc1Byb3RvUAGgAQH4AQGi",
+ "AgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3Rv",
+ "Mw=="));
+ descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
- new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.DoubleValue), global::Google.Protobuf.WellKnownTypes.DoubleValue.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FloatValue), global::Google.Protobuf.WellKnownTypes.FloatValue.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int64Value), global::Google.Protobuf.WellKnownTypes.Int64Value.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt64Value), global::Google.Protobuf.WellKnownTypes.UInt64Value.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int32Value), global::Google.Protobuf.WellKnownTypes.Int32Value.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt32Value), global::Google.Protobuf.WellKnownTypes.UInt32Value.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BoolValue), global::Google.Protobuf.WellKnownTypes.BoolValue.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.StringValue), global::Google.Protobuf.WellKnownTypes.StringValue.Parser, new[]{ "Value" }, null, null, null),
- new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BytesValue), global::Google.Protobuf.WellKnownTypes.BytesValue.Parser, new[]{ "Value" }, null, null, null)
+ new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.DoubleValue), global::Google.Protobuf.WellKnownTypes.DoubleValue.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.FloatValue), global::Google.Protobuf.WellKnownTypes.FloatValue.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int64Value), global::Google.Protobuf.WellKnownTypes.Int64Value.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt64Value), global::Google.Protobuf.WellKnownTypes.UInt64Value.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Int32Value), global::Google.Protobuf.WellKnownTypes.Int32Value.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.UInt32Value), global::Google.Protobuf.WellKnownTypes.UInt32Value.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BoolValue), global::Google.Protobuf.WellKnownTypes.BoolValue.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.StringValue), global::Google.Protobuf.WellKnownTypes.StringValue.Parser, new[]{ "Value" }, null, null, null),
+ new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.BytesValue), global::Google.Protobuf.WellKnownTypes.BytesValue.Parser, new[]{ "Value" }, null, null, null)
}));
}
#endregion
@@ -117,7 +118,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -228,7 +229,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -339,7 +340,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -450,7 +451,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -561,7 +562,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -672,7 +673,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -783,7 +784,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -868,7 +869,7 @@ namespace Google.Protobuf.WellKnownTypes {
public string Value {
get { return value_; }
set {
- value_ = pb::Preconditions.CheckNotNull(value, "value");
+ value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -894,7 +895,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
@@ -979,7 +980,7 @@ namespace Google.Protobuf.WellKnownTypes {
public pb::ByteString Value {
get { return value_; }
set {
- value_ = pb::Preconditions.CheckNotNull(value, "value");
+ value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
@@ -1005,7 +1006,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
public override string ToString() {
- return pb::JsonFormatter.Default.Format(this);
+ return pb::JsonFormatter.ToDiagnosticString(this);
}
public void WriteTo(pb::CodedOutputStream output) {
diff --git a/csharp/src/Google.Protobuf/packages.config b/csharp/src/Google.Protobuf/packages.config
new file mode 100644
index 00000000..40b8fd92
--- /dev/null
+++ b/csharp/src/Google.Protobuf/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="NuSpec.ReferenceGenerator" version="1.4.1" targetFramework="portable45-net45+win8+wp8+wpa81" developmentDependency="true" />
+</packages> \ No newline at end of file
diff --git a/examples/Makefile b/examples/Makefile
index 8dc90836..51f13426 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -5,6 +5,8 @@
all: cpp java python
cpp: add_person_cpp list_people_cpp
+go: add_person_go list_people_go
+gotest: add_person_gotest list_people_gotest
java: add_person_java list_people_java
python: add_person_python list_people_python
@@ -13,6 +15,8 @@ clean:
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
+ rm -f protoc_middleman_go tutorial/*.pb.go add_person_go list_people_go
+ rmdir tutorial 2>/dev/null || true
rmdir com/example/tutorial 2>/dev/null || true
rmdir com/example 2>/dev/null || true
rmdir com 2>/dev/null || true
@@ -21,6 +25,11 @@ protoc_middleman: addressbook.proto
protoc --cpp_out=. --java_out=. --python_out=. addressbook.proto
@touch protoc_middleman
+protoc_middleman_go: addressbook.proto
+ mkdir tutorial # make directory for go package
+ protoc --go_out=tutorial addressbook.proto
+ @touch protoc_middleman_go
+
add_person_cpp: add_person.cc protoc_middleman
pkg-config --cflags protobuf # fails if protobuf is not installed
c++ add_person.cc addressbook.pb.cc -o add_person_cpp `pkg-config --cflags --libs protobuf`
@@ -29,6 +38,18 @@ list_people_cpp: list_people.cc protoc_middleman
pkg-config --cflags protobuf # fails if protobuf is not installed
c++ list_people.cc addressbook.pb.cc -o list_people_cpp `pkg-config --cflags --libs protobuf`
+add_person_go: add_person.go protoc_middleman_go
+ go build -o add_person_go add_person.go
+
+add_person_gotest: add_person_test.go add_person_go
+ go test add_person.go add_person_test.go
+
+list_people_go: list_people.go protoc_middleman_go
+ go build -o list_people_go list_people.go
+
+list_people_gotest: list_people.go list_people_go
+ go test list_people.go list_people_test.go
+
javac_middleman: AddPerson.java ListPeople.java protoc_middleman
javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
@touch javac_middleman
diff --git a/examples/README.txt b/examples/README.txt
index f5530a5e..e6f30370 100644
--- a/examples/README.txt
+++ b/examples/README.txt
@@ -27,3 +27,28 @@ These examples are part of the Protocol Buffers tutorial, located at:
* Note that on some platforms you may have to edit the Makefile and remove
"-lpthread" from the linker commands (perhaps replacing it with something else).
We didn't do this automatically because we wanted to keep the example simple.
+
+## Go ##
+
+The Go example requires a plugin to the protocol buffer compiler, so it is not
+build with all the other examples. See:
+ https://github.com/golang/protobuf
+for more information about Go protocol buffer support.
+
+First, install the the Protocol Buffers compiler (protoc).
+Then, install the Go Protocol Buffers plugin
+($GOPATH/bin must be in your $PATH for protoc to find it):
+ go get github.com/golang/protobuf/protoc-gen-go
+
+Build the Go samples in this directory with "make go". This creates the
+following executable files in the current directory:
+ add_person_go list_people_go
+To run the example:
+ ./add_person_go addressbook.data
+to add a person to the protocol buffer encoded file addressbook.data. The file
+is created if it does not exist. To view the data, run:
+ ./list_people_go addressbook.data
+
+Observe that the C++, Python, and Java examples in this directory run in a
+similar way and can view/modify files created by the Go example and vice
+versa.
diff --git a/examples/add_person.go b/examples/add_person.go
new file mode 100644
index 00000000..4f2e7f74
--- /dev/null
+++ b/examples/add_person.go
@@ -0,0 +1,133 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+
+ "github.com/golang/protobuf/proto"
+ pb "github.com/google/protobuf/examples/tutorial"
+)
+
+func promptForAddress(r io.Reader) (*pb.Person, error) {
+ // A protocol buffer can be created like any struct.
+ p := &pb.Person{}
+
+ rd := bufio.NewReader(r)
+ fmt.Print("Enter person ID number: ")
+ // An int32 field in the .proto file is represented as an int32 field
+ // in the generated Go struct.
+ if _, err := fmt.Fscanf(rd, "%d\n", &p.Id); err != nil {
+ return p, err
+ }
+
+ fmt.Print("Enter name: ")
+ name, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ // A string field in the .proto file results in a string field in Go.
+ // We trim the whitespace because rd.ReadString includes the trailing
+ // newline character in its output.
+ p.Name = strings.TrimSpace(name)
+
+ fmt.Print("Enter email address (blank for none): ")
+ email, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ p.Email = strings.TrimSpace(email)
+
+ for {
+ fmt.Print("Enter a phone number (or leave blank to finish): ")
+ phone, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ phone = strings.TrimSpace(phone)
+ if phone == "" {
+ break
+ }
+ // The PhoneNumber message type is nested within the Person
+ // message in the .proto file. This results in a Go struct
+ // named using the name of the parent prefixed to the name of
+ // the nested message. Just as with pb.Person, it can be
+ // created like any other struct.
+ pn := &pb.Person_PhoneNumber{
+ Number: phone,
+ }
+
+ fmt.Print("Is this a mobile, home, or work phone? ")
+ ptype, err := rd.ReadString('\n')
+ if err != nil {
+ return p, err
+ }
+ ptype = strings.TrimSpace(ptype)
+
+ // A proto enum results in a Go constant for each enum value.
+ switch ptype {
+ case "mobile":
+ pn.Type = pb.Person_MOBILE
+ case "home":
+ pn.Type = pb.Person_HOME
+ case "work":
+ pn.Type = pb.Person_WORK
+ default:
+ fmt.Printf("Unknown phone type %q. Using default.\n", ptype)
+ }
+
+ // A repeated proto field maps to a slice field in Go. We can
+ // append to it like any other slice.
+ p.Phones = append(p.Phones, pn)
+ }
+
+ return p, nil
+}
+
+// Main reads the entire address book from a file, adds one person based on
+// user input, then writes it back out to the same file.
+func main() {
+ if len(os.Args) != 2 {
+ log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0])
+ }
+ fname := os.Args[1]
+
+ // Read the existing address book.
+ in, err := ioutil.ReadFile(fname)
+ if err != nil {
+ if os.IsNotExist(err) {
+ fmt.Printf("%s: File not found. Creating new file.\n", fname)
+ } else {
+ log.Fatalln("Error reading file:", err)
+ }
+ }
+
+ // [START marshal_proto]
+ book := &pb.AddressBook{}
+ // [START_EXCLUDE]
+ if err := proto.Unmarshal(in, book); err != nil {
+ log.Fatalln("Failed to parse address book:", err)
+ }
+
+ // Add an address.
+ addr, err := promptForAddress(os.Stdin)
+ if err != nil {
+ log.Fatalln("Error with address:", err)
+ }
+ book.People = append(book.People, addr)
+ // [END_EXCLUDE]
+
+ // Write the new address book back to disk.
+ out, err := proto.Marshal(book)
+ if err != nil {
+ log.Fatalln("Failed to encode address book:", err)
+ }
+ if err := ioutil.WriteFile(fname, out, 0644); err != nil {
+ log.Fatalln("Failed to write address book:", err)
+ }
+ // [END marshal_proto]
+}
diff --git a/examples/add_person.py b/examples/add_person.py
index fd81c982..0b698579 100755
--- a/examples/add_person.py
+++ b/examples/add_person.py
@@ -43,9 +43,8 @@ address_book = addressbook_pb2.AddressBook()
# Read the existing address book.
try:
- f = open(sys.argv[1], "rb")
- address_book.ParseFromString(f.read())
- f.close()
+ with open(sys.argv[1], "rb") as f:
+ address_book.ParseFromString(f.read())
except IOError:
print sys.argv[1] + ": File not found. Creating a new file."
@@ -53,6 +52,5 @@ except IOError:
PromptForAddress(address_book.people.add())
# Write the new address book back to disk.
-f = open(sys.argv[1], "wb")
-f.write(address_book.SerializeToString())
-f.close()
+with open(sys.argv[1], "wb") as f:
+ f.write(address_book.SerializeToString())
diff --git a/examples/add_person_test.go b/examples/add_person_test.go
new file mode 100644
index 00000000..0507db6f
--- /dev/null
+++ b/examples/add_person_test.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+ pb "github.com/google/protobuf/examples/tutorial"
+)
+
+func TestPromptForAddressReturnsAddress(t *testing.T) {
+ in := `12345
+Example Name
+name@example.com
+123-456-7890
+home
+222-222-2222
+mobile
+111-111-1111
+work
+777-777-7777
+unknown
+
+`
+ got, err := promptForAddress(strings.NewReader(in))
+ if err != nil {
+ t.Fatalf("promptForAddress(%q) had unexpected error: %s", in, err.Error())
+ }
+ if got.Id != 12345 {
+ t.Errorf("promptForAddress(%q) got %d, want ID %d", in, got.Id, 12345)
+ }
+ if got.Name != "Example Name" {
+ t.Errorf("promptForAddress(%q) => want name %q, got %q", "Example Name", got.Name)
+ }
+ if got.Email != "name@example.com" {
+ t.Errorf("promptForAddress(%q) => want email %q, got %q", "name@example.com", got.Email)
+ }
+
+ want := []*pb.Person_PhoneNumber{
+ {Number: "123-456-7890", Type: pb.Person_HOME},
+ {Number: "222-222-2222", Type: pb.Person_MOBILE},
+ {Number: "111-111-1111", Type: pb.Person_WORK},
+ {Number: "777-777-7777", Type: pb.Person_MOBILE},
+ }
+ if len(got.Phones) != len(want) {
+ t.Errorf("want %d phone numbers, got %d", len(want), len(got.Phones))
+ }
+ phones := len(got.Phones)
+ if phones > len(want) {
+ phones = len(want)
+ }
+ for i := 0; i < phones; i++ {
+ if !proto.Equal(got.Phones[i], want[i]) {
+ t.Errorf("want phone %q, got %q", *want[i], *got.Phones[i])
+ }
+
+ }
+}
diff --git a/examples/addressbook.proto b/examples/addressbook.proto
index bfdceeaf..23cc2f97 100644
--- a/examples/addressbook.proto
+++ b/examples/addressbook.proto
@@ -1,16 +1,29 @@
// See README.txt for information and build instructions.
-
+//
+// Note: START and END tags are used in comments to define sections used in
+// tutorials. They are not part of the syntax for Protocol Buffers.
+//
+// To get an in-depth walkthrough of this file and the related examples, see:
+// https://developers.google.com/protocol-buffers/docs/tutorials
+
+// [START declaration]
syntax = "proto3";
-
package tutorial;
+// [END declaration]
+// [START java_declaration]
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
+// [END java_declaration]
+
+// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
+// [END csharp_declaration]
+// [START messages]
message Person {
string name = 1;
- int32 id = 2; // Unique ID number for this person.
+ int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
@@ -31,3 +44,4 @@ message Person {
message AddressBook {
repeated Person people = 1;
}
+// [END messages]
diff --git a/examples/list_people.go b/examples/list_people.go
new file mode 100644
index 00000000..70bc589e
--- /dev/null
+++ b/examples/list_people.go
@@ -0,0 +1,61 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+
+ "github.com/golang/protobuf/proto"
+ pb "github.com/google/protobuf/examples/tutorial"
+)
+
+func writePerson(w io.Writer, p *pb.Person) {
+ fmt.Fprintln(w, "Person ID:", p.Id)
+ fmt.Fprintln(w, " Name:", p.Name)
+ if p.Email != "" {
+ fmt.Fprintln(w, " E-mail address:", p.Email)
+ }
+
+ for _, pn := range p.Phones {
+ switch pn.Type {
+ case pb.Person_MOBILE:
+ fmt.Fprint(w, " Mobile phone #: ")
+ case pb.Person_HOME:
+ fmt.Fprint(w, " Home phone #: ")
+ case pb.Person_WORK:
+ fmt.Fprint(w, " Work phone #: ")
+ }
+ fmt.Fprintln(w, pn.Number)
+ }
+}
+
+func listPeople(w io.Writer, book *pb.AddressBook) {
+ for _, p := range book.People {
+ writePerson(w, p)
+ }
+}
+
+// Main reads the entire address book from a file and prints all the
+// information inside.
+func main() {
+ if len(os.Args) != 2 {
+ log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0])
+ }
+ fname := os.Args[1]
+
+ // [START unmarshal_proto]
+ // Read the existing address book.
+ in, err := ioutil.ReadFile(fname)
+ if err != nil {
+ log.Fatalln("Error reading file:", err)
+ }
+ book := &pb.AddressBook{}
+ if err := proto.Unmarshal(in, book); err != nil {
+ log.Fatalln("Failed to parse address book:", err)
+ }
+ // [END unmarshal_proto]
+
+ listPeople(os.Stdout, book)
+}
diff --git a/examples/list_people.py b/examples/list_people.py
index 755de901..f131872d 100755
--- a/examples/list_people.py
+++ b/examples/list_people.py
@@ -31,8 +31,7 @@ if len(sys.argv) != 2:
address_book = addressbook_pb2.AddressBook()
# Read the existing address book.
-f = open(sys.argv[1], "rb")
-address_book.ParseFromString(f.read())
-f.close()
+with open(sys.argv[1], "rb") as f:
+ address_book.ParseFromString(f.read())
ListPeople(address_book)
diff --git a/examples/list_people_test.go b/examples/list_people_test.go
new file mode 100644
index 00000000..87d6ad6e
--- /dev/null
+++ b/examples/list_people_test.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+
+ pb "github.com/google/protobuf/examples/tutorial"
+)
+
+func TestWritePersonWritesPerson(t *testing.T) {
+ buf := new(bytes.Buffer)
+ // [START populate_proto]
+ p := pb.Person{
+ Id: 1234,
+ Name: "John Doe",
+ Email: "jdoe@example.com",
+ Phones: []*pb.Person_PhoneNumber{
+ {Number: "555-4321", Type: pb.Person_HOME},
+ },
+ }
+ // [END populate_proto]
+ writePerson(buf, &p)
+ got := buf.String()
+ want := `Person ID: 1234
+ Name: John Doe
+ E-mail address: jdoe@example.com
+ Home phone #: 555-4321
+`
+ if got != want {
+ t.Errorf("writePerson(%s) =>\n\t%q, want %q", p.String(), got, want)
+ }
+}
+
+func TestListPeopleWritesList(t *testing.T) {
+ buf := new(bytes.Buffer)
+ in := pb.AddressBook{[]*pb.Person{
+ {
+ Name: "John Doe",
+ Id: 101,
+ Email: "john@example.com",
+ },
+ {
+ Name: "Jane Doe",
+ Id: 102,
+ },
+ {
+ Name: "Jack Doe",
+ Id: 201,
+ Email: "jack@example.com",
+ Phones: []*pb.Person_PhoneNumber{
+ {Number: "555-555-5555", Type: pb.Person_WORK},
+ },
+ },
+ {
+ Name: "Jack Buck",
+ Id: 301,
+ Email: "buck@example.com",
+ Phones: []*pb.Person_PhoneNumber{
+ {Number: "555-555-0000", Type: pb.Person_HOME},
+ {Number: "555-555-0001", Type: pb.Person_MOBILE},
+ {Number: "555-555-0002", Type: pb.Person_WORK},
+ },
+ },
+ {
+ Name: "Janet Doe",
+ Id: 1001,
+ Email: "janet@example.com",
+ Phones: []*pb.Person_PhoneNumber{
+ {Number: "555-777-0000"},
+ {Number: "555-777-0001", Type: pb.Person_HOME},
+ },
+ },
+ }}
+ listPeople(buf, &in)
+ want := strings.Split(`Person ID: 101
+ Name: John Doe
+ E-mail address: john@example.com
+Person ID: 102
+ Name: Jane Doe
+Person ID: 201
+ Name: Jack Doe
+ E-mail address: jack@example.com
+ Work phone #: 555-555-5555
+Person ID: 301
+ Name: Jack Buck
+ E-mail address: buck@example.com
+ Home phone #: 555-555-0000
+ Mobile phone #: 555-555-0001
+ Work phone #: 555-555-0002
+Person ID: 1001
+ Name: Janet Doe
+ E-mail address: janet@example.com
+ Mobile phone #: 555-777-0000
+ Home phone #: 555-777-0001
+`, "\n")
+ got := strings.Split(buf.String(), "\n")
+ if len(got) != len(want) {
+ t.Errorf(
+ "listPeople(%s) =>\n\t%q has %d lines, want %d",
+ in.String(),
+ buf.String(),
+ len(got),
+ len(want))
+ }
+ lines := len(got)
+ if lines > len(want) {
+ lines = len(want)
+ }
+ for i := 0; i < lines; i++ {
+ if got[i] != want[i] {
+ t.Errorf(
+ "listPeople(%s) =>\n\tline %d %q, want %q",
+ in.String(),
+ i,
+ got[i],
+ want[i])
+ }
+ }
+}
diff --git a/generate_descriptor_proto.sh b/generate_descriptor_proto.sh
index cd906896..44006b2c 100755
--- a/generate_descriptor_proto.sh
+++ b/generate_descriptor_proto.sh
@@ -10,6 +10,8 @@
# to make when building protoc. This is particularly useful for passing
# -j4 to run 4 jobs simultaneously.
+set -e
+
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
@@ -43,51 +45,48 @@ declare -a RUNTIME_PROTO_FILES=(\
CORE_PROTO_IS_CORRECT=0
PROCESS_ROUND=1
+TMP=$(mktemp -d)
echo "Updating descriptor protos..."
while [ $CORE_PROTO_IS_CORRECT -ne 1 ]
do
echo "Round $PROCESS_ROUND"
CORE_PROTO_IS_CORRECT=1
- for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do
- BASE_NAME=${PROTO_FILE%.*}
- cp ${BASE_NAME}.pb.h ${BASE_NAME}.pb.h.tmp
- cp ${BASE_NAME}.pb.cc ${BASE_NAME}.pb.cc.tmp
- done
- cp google/protobuf/compiler/plugin.pb.h google/protobuf/compiler/plugin.pb.h.tmp
- cp google/protobuf/compiler/plugin.pb.cc google/protobuf/compiler/plugin.pb.cc.tmp
make $@ protoc &&
- ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:. ${RUNTIME_PROTO_FILES[@]} && \
- ./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:. google/protobuf/compiler/plugin.proto
+ ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \
+ ./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP google/protobuf/compiler/plugin.proto
for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do
BASE_NAME=${PROTO_FILE%.*}
- diff ${BASE_NAME}.pb.h ${BASE_NAME}.pb.h.tmp > /dev/null
+ diff ${BASE_NAME}.pb.h $TMP/${BASE_NAME}.pb.h > /dev/null
if test $? -ne 0; then
CORE_PROTO_IS_CORRECT=0
fi
- diff ${BASE_NAME}.pb.cc ${BASE_NAME}.pb.cc.tmp > /dev/null
+ diff ${BASE_NAME}.pb.cc $TMP/${BASE_NAME}.pb.cc > /dev/null
if test $? -ne 0; then
CORE_PROTO_IS_CORRECT=0
fi
done
- diff google/protobuf/compiler/plugin.pb.h google/protobuf/compiler/plugin.pb.h.tmp > /dev/null
+ diff google/protobuf/compiler/plugin.pb.h $TMP/google/protobuf/compiler/plugin.pb.h > /dev/null
if test $? -ne 0; then
CORE_PROTO_IS_CORRECT=0
fi
- diff google/protobuf/compiler/plugin.pb.cc google/protobuf/compiler/plugin.pb.cc.tmp > /dev/null
+ diff google/protobuf/compiler/plugin.pb.cc $TMP/google/protobuf/compiler/plugin.pb.cc > /dev/null
if test $? -ne 0; then
CORE_PROTO_IS_CORRECT=0
fi
- for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do
- BASE_NAME=${PROTO_FILE%.*}
- rm ${BASE_NAME}.pb.h.tmp
- rm ${BASE_NAME}.pb.cc.tmp
- done
- rm google/protobuf/compiler/plugin.pb.h.tmp
- rm google/protobuf/compiler/plugin.pb.cc.tmp
+ # Only override the output if the files are different to avoid re-compilation
+ # of the protoc.
+ if [ $CORE_PROTO_IS_CORRECT -ne 1 ]; then
+ for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do
+ BASE_NAME=${PROTO_FILE%.*}
+ mv $TMP/${BASE_NAME}.pb.h ${BASE_NAME}.pb.h
+ mv $TMP/${BASE_NAME}.pb.cc ${BASE_NAME}.pb.cc
+ done
+ mv $TMP/google/protobuf/compiler/plugin.pb.* google/protobuf/compiler/
+ fi
PROCESS_ROUND=$((PROCESS_ROUND + 1))
done
diff --git a/gmock.BUILD b/gmock.BUILD
index 66cddea9..82abf275 100644
--- a/gmock.BUILD
+++ b/gmock.BUILD
@@ -4,6 +4,11 @@ cc_library(
"gmock-1.7.0/gtest/src/gtest-all.cc",
"gmock-1.7.0/src/gmock-all.cc",
],
+ hdrs = glob([
+ "gmock-1.7.0/**/*.h",
+ "gmock-1.7.0/gtest/src/*.cc",
+ "gmock-1.7.0/src/*.cc",
+ ]),
includes = [
"gmock-1.7.0",
"gmock-1.7.0/gtest",
diff --git a/java/core/generate-sources-build.xml b/java/core/generate-sources-build.xml
new file mode 100644
index 00000000..0996e5ff
--- /dev/null
+++ b/java/core/generate-sources-build.xml
@@ -0,0 +1,20 @@
+<project name="generate-sources">
+ <echo message="Running protoc ..."/>
+ <mkdir dir="${generated.sources.dir}"/>
+ <exec executable="${protoc}">
+ <arg value="--java_out=${generated.sources.dir}"/>
+ <arg value="--proto_path=${protobuf.source.dir}"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/any.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/api.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/descriptor.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/duration.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/empty.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/field_mask.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/source_context.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/struct.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/timestamp.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/type.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/wrappers.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/compiler/plugin.proto"/>
+ </exec>
+</project> \ No newline at end of file
diff --git a/java/core/generate-test-sources-build.xml b/java/core/generate-test-sources-build.xml
new file mode 100644
index 00000000..ab415db6
--- /dev/null
+++ b/java/core/generate-test-sources-build.xml
@@ -0,0 +1,43 @@
+<project name="generate-test-sources">
+ <mkdir dir="${generated.testsources.dir}"/>
+ <exec executable="${protoc}">
+ <arg value="--java_out=${generated.testsources.dir}"/>
+ <arg value="--proto_path=${protobuf.source.dir}"/>
+ <arg value="--proto_path=${test.proto.dir}"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_import.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_import_public.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_mset.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_mset_wire_format.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_optimize_for.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_custom_options.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_lite.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_import_lite.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_import_public_lite.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_lite_imports_nonlite.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_enormous_descriptor.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_no_generic_services.proto"/>
+ <arg value="${protobuf.source.dir}/google/protobuf/unittest_well_known_types.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/lazy_fields_lite.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/lite_equals_and_hash.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/multiple_files_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/nested_builders_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/nested_extension.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/nested_extension_lite.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/non_nested_extension.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/non_nested_extension_lite.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/outer_class_name_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/outer_class_name_test2.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/outer_class_name_test3.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/test_bad_identifiers.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/test_check_utf8.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/test_check_utf8_size.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/test_custom_options.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/any_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/field_presence_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/map_for_proto2_lite_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/map_for_proto2_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/map_test.proto"/>
+ <arg value="${test.proto.dir}/com/google/protobuf/map_initialization_order_test.proto"/>
+ </exec>
+</project> \ No newline at end of file
diff --git a/java/core/pom.xml b/java/core/pom.xml
new file mode 100644
index 00000000..74d5ead3
--- /dev/null
+++ b/java/core/pom.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-parent</artifactId>
+ <version>3.0.0-beta-2</version>
+ </parent>
+
+ <artifactId>protobuf-java</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Protocol Buffers [Core]</name>
+ <description>
+ Core Protocol Buffers library. Protocol Buffers are a way of encoding structured data in an
+ efficient yet extensible format.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <!-- Include core protos in the bundle as resources -->
+ <resources>
+ <resource>
+ <directory>${protobuf.source.dir}</directory>
+ <includes>
+ <include>google/protobuf/any.proto</include>
+ <include>google/protobuf/api.proto</include>
+ <include>google/protobuf/descriptor.proto</include>
+ <include>google/protobuf/duration.proto</include>
+ <include>google/protobuf/empty.proto</include>
+ <include>google/protobuf/field_mask.proto</include>
+ <include>google/protobuf/source_context.proto</include>
+ <include>google/protobuf/struct.proto</include>
+ <include>google/protobuf/timestamp.proto</include>
+ <include>google/protobuf/type.proto</include>
+ <include>google/protobuf/wrappers.proto</include>
+ <include>google/protobuf/compiler/plugin.proto</include>
+ </includes>
+ </resource>
+ </resources>
+
+ <plugins>
+ <!-- Use Antrun plugin to generate sources with protoc -->
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <!-- Generate core protos -->
+ <execution>
+ <id>generate-sources</id>
+ <phase>generate-sources</phase>
+ <configuration>
+ <target>
+ <ant antfile="generate-sources-build.xml"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <!-- Generate the test protos -->
+ <execution>
+ <id>generate-test-sources</id>
+ <phase>generate-test-sources</phase>
+ <configuration>
+ <target>
+ <ant antfile="generate-test-sources-build.xml"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- Add the generated sources to the build -->
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <generatedSourcesDirectory>${generated.sources.dir}</generatedSourcesDirectory>
+ <generatedTestSourcesDirectory>${generated.testsources.dir}</generatedTestSourcesDirectory>
+ </configuration>
+ </plugin>
+
+ <!-- OSGI bundle configuration -->
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
+ <Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
+ <Export-Package>com.google.protobuf;version=${project.version}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
index cc89173a..9f418f2b 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -30,6 +30,7 @@
package com.google.protobuf;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Internal.EnumLite;
@@ -161,10 +162,18 @@ public abstract class AbstractMessage extends AbstractMessageLite
Descriptors.Descriptor descriptor = entry.getDescriptorForType();
Descriptors.FieldDescriptor key = descriptor.findFieldByName("key");
Descriptors.FieldDescriptor value = descriptor.findFieldByName("value");
- result.put(entry.getField(key), entry.getField(value));
+ Object fieldValue = entry.getField(value);
+ if (fieldValue instanceof EnumValueDescriptor) {
+ fieldValue = ((EnumValueDescriptor) fieldValue).getNumber();
+ }
+ result.put(entry.getField(key), fieldValue);
while (iterator.hasNext()) {
entry = (Message) iterator.next();
- result.put(entry.getField(key), entry.getField(value));
+ fieldValue = entry.getField(value);
+ if (fieldValue instanceof EnumValueDescriptor) {
+ fieldValue = ((EnumValueDescriptor) fieldValue).getNumber();
+ }
+ result.put(entry.getField(key), fieldValue);
}
return result;
}
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
index 12384983..12384983 100644
--- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
diff --git a/java/src/main/java/com/google/protobuf/AbstractParser.java b/java/core/src/main/java/com/google/protobuf/AbstractParser.java
index 1a4c6311..1a4c6311 100644
--- a/java/src/main/java/com/google/protobuf/AbstractParser.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractParser.java
diff --git a/java/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
index bb6446b2..bb6446b2 100644
--- a/java/src/main/java/com/google/protobuf/AbstractProtobufList.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
diff --git a/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java b/java/core/src/main/java/com/google/protobuf/BlockingRpcChannel.java
index d535efb9..d535efb9 100644
--- a/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java
+++ b/java/core/src/main/java/com/google/protobuf/BlockingRpcChannel.java
diff --git a/java/src/main/java/com/google/protobuf/BlockingService.java b/java/core/src/main/java/com/google/protobuf/BlockingService.java
index d01f0b8f..d01f0b8f 100644
--- a/java/src/main/java/com/google/protobuf/BlockingService.java
+++ b/java/core/src/main/java/com/google/protobuf/BlockingService.java
diff --git a/java/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
index 45492d2f..70e042f5 100644
--- a/java/src/main/java/com/google/protobuf/BooleanArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
@@ -68,10 +68,17 @@ final class BooleanArrayList
private int size;
/**
- * Constructs a new mutable {@code BooleanArrayList}.
+ * Constructs a new mutable {@code BooleanArrayList} with default capacity.
*/
BooleanArrayList() {
- array = new boolean[DEFAULT_CAPACITY];
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Constructs a new mutable {@code BooleanArrayList} with the provided capacity.
+ */
+ BooleanArrayList(int capacity) {
+ array = new boolean[capacity];
size = 0;
}
diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java
index b092bc36..305236f3 100644
--- a/java/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/ByteString.java
@@ -1,38 +1,13 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copyright 2007 Google Inc. All rights reserved.
package com.google.protobuf;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
@@ -41,6 +16,7 @@ import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
@@ -81,7 +57,14 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
/**
* Empty {@code ByteString}.
*/
- public static final ByteString EMPTY = new LiteralByteString(new byte[0]);
+ public static final ByteString EMPTY = new LiteralByteString(Internal.EMPTY_BYTE_ARRAY);
+
+ /**
+ * Cached hash value. Intentionally accessed via a data race, which
+ * is safe because of the Java Memory Model's "no out-of-thin-air values"
+ * guarantees for ints. A value of 0 implies that the hash has not been set.
+ */
+ private int hash = 0;
// This constructor is here to prevent subclassing outside of this package,
ByteString() {}
@@ -105,7 +88,38 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @return the iterator
*/
- public abstract ByteIterator iterator();
+ @Override
+ public final ByteIterator iterator() {
+ return new ByteIterator() {
+ private int position = 0;
+ private final int limit = size();
+
+ @Override
+ public boolean hasNext() {
+ return position < limit;
+ }
+
+ @Override
+ public Byte next() {
+ // Boxing calls Byte.valueOf(byte), which does not instantiate.
+ return nextByte();
+ }
+
+ @Override
+ public byte nextByte() {
+ try {
+ return byteAt(position++);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new NoSuchElementException(e.getMessage());
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
/**
* This interface extends {@code Iterator<Byte>}, so that we can return an
@@ -134,7 +148,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @return true if this is zero bytes long
*/
- public boolean isEmpty() {
+ public final boolean isEmpty() {
return size() == 0;
}
@@ -150,7 +164,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @throws IndexOutOfBoundsException if {@code beginIndex < 0} or
* {@code beginIndex > size()}.
*/
- public ByteString substring(int beginIndex) {
+ public final ByteString substring(int beginIndex) {
return substring(beginIndex, size());
}
@@ -175,7 +189,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* argument is a prefix of the byte sequence represented by
* this string; <code>false</code> otherwise.
*/
- public boolean startsWith(ByteString prefix) {
+ public final boolean startsWith(ByteString prefix) {
return size() >= prefix.size() &&
substring(0, prefix.size()).equals(prefix);
}
@@ -189,7 +203,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* argument is a suffix of the byte sequence represented by
* this string; <code>false</code> otherwise.
*/
- public boolean endsWith(ByteString suffix) {
+ public final boolean endsWith(ByteString suffix) {
return size() >= suffix.size() &&
substring(size() - suffix.size()).equals(suffix);
}
@@ -220,6 +234,24 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
public static ByteString copyFrom(byte[] bytes) {
return copyFrom(bytes, 0, bytes.length);
}
+
+ /**
+ * Wraps the given bytes into a {@code ByteString}. Intended for internal only
+ * usage to force a classload of ByteString before LiteralByteString.
+ */
+ static ByteString wrap(byte[] bytes) {
+ // TODO(dweis): Return EMPTY when bytes are empty to reduce allocations?
+ return new LiteralByteString(bytes);
+ }
+
+ /**
+ * Wraps the given bytes into a {@code ByteString}. Intended for internal only
+ * usage to force a classload of ByteString before BoundedByteString and
+ * LiteralByteString.
+ */
+ static ByteString wrap(byte[] bytes, int offset, int length) {
+ return new BoundedByteString(bytes, offset, length);
+ }
/**
* Copies the next {@code size} bytes from a {@code java.nio.ByteBuffer} into
@@ -309,8 +341,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*/
public static ByteString readFrom(InputStream streamToDrain)
throws IOException {
- return readFrom(
- streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE);
+ return readFrom(streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE);
}
/**
@@ -383,10 +414,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
if (bytesRead == 0) {
return null;
- } else {
- // Always make a copy since InputStream could steal a reference to buf.
- return ByteString.copyFrom(buf, 0, bytesRead);
}
+
+ // Always make a copy since InputStream could steal a reference to buf.
+ return ByteString.copyFrom(buf, 0, bytesRead);
}
// =================================================================
@@ -402,12 +433,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param other string to concatenate
* @return a new {@code ByteString} instance
*/
- public ByteString concat(ByteString other) {
- int thisSize = size();
- int otherSize = other.size();
- if ((long) thisSize + otherSize >= Integer.MAX_VALUE) {
+ public final ByteString concat(ByteString other) {
+ if (Integer.MAX_VALUE - size() < other.size()) {
throw new IllegalArgumentException("ByteString would be too long: " +
- thisSize + "+" + otherSize);
+ size() + "+" + other.size());
}
return RopeByteString.concatenate(this, other);
@@ -426,29 +455,29 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @return new {@code ByteString}
*/
public static ByteString copyFrom(Iterable<ByteString> byteStrings) {
- Collection<ByteString> collection;
+ // Determine the size;
+ final int size;
if (!(byteStrings instanceof Collection)) {
- collection = new ArrayList<ByteString>();
- for (ByteString byteString : byteStrings) {
- collection.add(byteString);
+ int tempSize = 0;
+ for (Iterator<ByteString> iter = byteStrings.iterator(); iter.hasNext();
+ iter.next(), ++tempSize) {
}
+ size = tempSize;
} else {
- collection = (Collection<ByteString>) byteStrings;
+ size = ((Collection<ByteString>) byteStrings).size();
}
- ByteString result;
- if (collection.isEmpty()) {
- result = EMPTY;
- } else {
- result = balancedConcat(collection.iterator(), collection.size());
+
+ if (size == 0) {
+ return EMPTY;
}
- return result;
+
+ return balancedConcat(byteStrings.iterator(), size);
}
// Internal function used by copyFrom(Iterable<ByteString>).
// Create a balanced concatenation of the next "length" elements from the
// iterable.
- private static ByteString balancedConcat(Iterator<ByteString> iterator,
- int length) {
+ private static ByteString balancedConcat(Iterator<ByteString> iterator, int length) {
assert length >= 1;
ByteString result;
if (length == 1) {
@@ -486,25 +515,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @throws IndexOutOfBoundsException if an offset or size is negative or too
* large
*/
- public void copyTo(byte[] target, int sourceOffset, int targetOffset,
+ public final void copyTo(byte[] target, int sourceOffset, int targetOffset,
int numberToCopy) {
- if (sourceOffset < 0) {
- throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset);
- }
- if (targetOffset < 0) {
- throw new IndexOutOfBoundsException("Target offset < 0: " + targetOffset);
- }
- if (numberToCopy < 0) {
- throw new IndexOutOfBoundsException("Length < 0: " + numberToCopy);
- }
- if (sourceOffset + numberToCopy > size()) {
- throw new IndexOutOfBoundsException(
- "Source end offset < 0: " + (sourceOffset + numberToCopy));
- }
- if (targetOffset + numberToCopy > target.length) {
- throw new IndexOutOfBoundsException(
- "Target end offset < 0: " + (targetOffset + numberToCopy));
- }
+ checkRange(sourceOffset, sourceOffset + numberToCopy, size());
+ checkRange(targetOffset, targetOffset + numberToCopy, target.length);
if (numberToCopy > 0) {
copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
}
@@ -534,8 +548,8 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @return copied bytes
*/
- public byte[] toByteArray() {
- int size = size();
+ public final byte[] toByteArray() {
+ final int size = size();
if (size == 0) {
return Internal.EMPTY_BYTE_ARRAY;
}
@@ -548,6 +562,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* Writes the complete contents of this byte string to
* the specified output stream argument.
*
+ * <p>It is assumed that the {@link OutputStream} will not modify the contents passed it
+ * it. It may be possible for a malicious {@link OutputStream} to corrupt
+ * the data underlying the {@link ByteString}.
+ *
* @param out the output stream to which to write the data.
* @throws IOException if an I/O error occurs.
*/
@@ -563,30 +581,20 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @throws IndexOutOfBoundsException if an offset or size is negative or too
* large
*/
- void writeTo(OutputStream out, int sourceOffset, int numberToWrite)
+ final void writeTo(OutputStream out, int sourceOffset, int numberToWrite)
throws IOException {
- if (sourceOffset < 0) {
- throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset);
- }
- if (numberToWrite < 0) {
- throw new IndexOutOfBoundsException("Length < 0: " + numberToWrite);
- }
- if (sourceOffset + numberToWrite > size()) {
- throw new IndexOutOfBoundsException(
- "Source end offset exceeded: " + (sourceOffset + numberToWrite));
- }
+ checkRange(sourceOffset, sourceOffset + numberToWrite, size());
if (numberToWrite > 0) {
writeToInternal(out, sourceOffset, numberToWrite);
}
-
}
/**
* Internal version of {@link #writeTo(OutputStream,int,int)} that assumes
* all error checking has already been done.
*/
- abstract void writeToInternal(OutputStream out, int sourceOffset,
- int numberToWrite) throws IOException;
+ abstract void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite)
+ throws IOException;
/**
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
@@ -618,7 +626,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @return new string
* @throws UnsupportedEncodingException if charset isn't recognized
*/
- public String toString(String charsetName)
+ public final String toString(String charsetName)
throws UnsupportedEncodingException {
try {
return toString(Charset.forName(charsetName));
@@ -636,7 +644,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param charset encode using this charset
* @return new string
*/
- public String toString(Charset charset) {
+ public final String toString(Charset charset) {
return size() == 0 ? "" : toStringInternal(charset);
}
@@ -657,7 +665,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @return new string using UTF-8 encoding
*/
- public String toStringUtf8() {
+ public final String toStringUtf8() {
return toString(Internal.UTF_8);
}
@@ -716,13 +724,51 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
public abstract boolean equals(Object o);
/**
- * Return a non-zero hashCode depending only on the sequence of bytes
- * in this ByteString.
+ * Base class for leaf {@link ByteString}s (i.e. non-ropes).
+ */
+ abstract static class LeafByteString extends ByteString {
+ @Override
+ protected final int getTreeDepth() {
+ return 0;
+ }
+
+ @Override
+ protected final boolean isBalanced() {
+ return true;
+ }
+
+ /**
+ * Check equality of the substring of given length of this object starting at
+ * zero with another {@code ByteString} substring starting at offset.
+ *
+ * @param other what to compare a substring in
+ * @param offset offset into other
+ * @param length number of bytes to compare
+ * @return true for equality of substrings, else false.
+ */
+ abstract boolean equalsRange(ByteString other, int offset, int length);
+ }
+
+ /**
+ * Compute the hashCode using the traditional algorithm from {@link
+ * ByteString}.
*
- * @return hashCode value for this object
+ * @return hashCode value
*/
@Override
- public abstract int hashCode();
+ public final int hashCode() {
+ int h = hash;
+
+ if (h == 0) {
+ int size = size();
+ h = partialHash(size, 0, size);
+ if (h == 0) {
+ h = 1;
+ }
+ hash = h;
+ }
+ return h;
+ }
// =================================================================
// Input stream
@@ -1034,7 +1080,9 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @return value of cached hash code or 0 if not computed yet
*/
- protected abstract int peekCachedHashCode();
+ protected final int peekCachedHashCode() {
+ return hash;
+ }
/**
* Compute the hash across the value bytes starting with the given hash, and
@@ -1049,9 +1097,360 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*/
protected abstract int partialHash(int h, int offset, int length);
+ /**
+ * Checks that the given index falls within the specified array size.
+ *
+ * @param index the index position to be tested
+ * @param size the length of the array
+ * @throws ArrayIndexOutOfBoundsException if the index does not fall within the array.
+ */
+ static void checkIndex(int index, int size) {
+ if ((index | (size - (index + 1))) < 0) {
+ if (index < 0) {
+ throw new ArrayIndexOutOfBoundsException("Index < 0: " + index);
+ }
+ throw new ArrayIndexOutOfBoundsException("Index > length: " + index + ", " + size);
+ }
+ }
+
+ /**
+ * Checks that the given range falls within the bounds of an array
+ *
+ * @param startIndex the start index of the range (inclusive)
+ * @param endIndex the end index of the range (exclusive)
+ * @param size the size of the array.
+ * @return the length of the range.
+ * @throws ArrayIndexOutOfBoundsException some or all of the range falls outside of the array.
+ */
+ static int checkRange(int startIndex, int endIndex, int size) {
+ final int length = endIndex - startIndex;
+ if ((startIndex | endIndex | length | (size - endIndex)) < 0) {
+ if (startIndex < 0) {
+ throw new IndexOutOfBoundsException("Beginning index: " + startIndex + " < 0");
+ }
+ if (endIndex < startIndex) {
+ throw new IndexOutOfBoundsException(
+ "Beginning index larger than ending index: " + startIndex + ", " + endIndex);
+ }
+ // endIndex >= size
+ throw new IndexOutOfBoundsException("End index: " + endIndex + " >= " + size);
+ }
+ return length;
+ }
+
@Override
- public String toString() {
+ public final String toString() {
return String.format("<ByteString@%s size=%d>",
Integer.toHexString(System.identityHashCode(this)), size());
}
+
+ /**
+ * This class implements a {@link com.google.protobuf.ByteString} backed by a
+ * single array of bytes, contiguous in memory. It supports substring by
+ * pointing to only a sub-range of the underlying byte array, meaning that a
+ * substring will reference the full byte-array of the string it's made from,
+ * exactly as with {@link String}.
+ *
+ * @author carlanton@google.com (Carl Haverl)
+ */
+ // Keep this class private to avoid deadlocks in classloading across threads as ByteString's
+ // static initializer loads LiteralByteString and another thread loads LiteralByteString.
+ private static class LiteralByteString extends ByteString.LeafByteString {
+ private static final long serialVersionUID = 1L;
+
+ protected final byte[] bytes;
+
+ /**
+ * Creates a {@code LiteralByteString} backed by the given array, without
+ * copying.
+ *
+ * @param bytes array to wrap
+ */
+ LiteralByteString(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public byte byteAt(int index) {
+ // Unlike most methods in this class, this one is a direct implementation
+ // ignoring the potential offset because we need to do range-checking in the
+ // substring case anyway.
+ return bytes[index];
+ }
+
+ @Override
+ public int size() {
+ return bytes.length;
+ }
+
+ // =================================================================
+ // ByteString -> substring
+
+ @Override
+ public final ByteString substring(int beginIndex, int endIndex) {
+ final int length = checkRange(beginIndex, endIndex, size());
+
+ if (length == 0) {
+ return ByteString.EMPTY;
+ }
+
+ return new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex, length);
+ }
+
+ // =================================================================
+ // ByteString -> byte[]
+
+ @Override
+ protected void copyToInternal(
+ byte[] target, int sourceOffset, int targetOffset, int numberToCopy) {
+ // Optimized form, not for subclasses, since we don't call
+ // getOffsetIntoBytes() or check the 'numberToCopy' parameter.
+ // TODO(nathanmittler): Is not calling getOffsetIntoBytes really saving that much?
+ System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy);
+ }
+
+ @Override
+ public final void copyTo(ByteBuffer target) {
+ target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes
+ }
+
+ @Override
+ public final ByteBuffer asReadOnlyByteBuffer() {
+ return ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size()).asReadOnlyBuffer();
+ }
+
+ @Override
+ public final List<ByteBuffer> asReadOnlyByteBufferList() {
+ return Collections.singletonList(asReadOnlyByteBuffer());
+ }
+
+ @Override
+ public final void writeTo(OutputStream outputStream) throws IOException {
+ outputStream.write(toByteArray());
+ }
+
+ @Override
+ final void writeToInternal(OutputStream outputStream, int sourceOffset, int numberToWrite)
+ throws IOException {
+ outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset, numberToWrite);
+ }
+
+ @Override
+ protected final String toStringInternal(Charset charset) {
+ return new String(bytes, getOffsetIntoBytes(), size(), charset);
+ }
+
+ // =================================================================
+ // UTF-8 decoding
+
+ @Override
+ public final boolean isValidUtf8() {
+ int offset = getOffsetIntoBytes();
+ return Utf8.isValidUtf8(bytes, offset, offset + size());
+ }
+
+ @Override
+ protected final int partialIsValidUtf8(int state, int offset, int length) {
+ int index = getOffsetIntoBytes() + offset;
+ return Utf8.partialIsValidUtf8(state, bytes, index, index + length);
+ }
+
+ // =================================================================
+ // equals() and hashCode()
+
+ @Override
+ public final boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof ByteString)) {
+ return false;
+ }
+
+ if (size() != ((ByteString) other).size()) {
+ return false;
+ }
+ if (size() == 0) {
+ return true;
+ }
+
+ if (other instanceof LiteralByteString) {
+ LiteralByteString otherAsLiteral = (LiteralByteString) other;
+ // If we know the hash codes and they are not equal, we know the byte
+ // strings are not equal.
+ int thisHash = peekCachedHashCode();
+ int thatHash = otherAsLiteral.peekCachedHashCode();
+ if (thisHash != 0 && thatHash != 0 && thisHash != thatHash) {
+ return false;
+ }
+
+ return equalsRange((LiteralByteString) other, 0, size());
+ } else {
+ // RopeByteString and NioByteString.
+ return other.equals(this);
+ }
+ }
+
+ /**
+ * Check equality of the substring of given length of this object starting at
+ * zero with another {@code LiteralByteString} substring starting at offset.
+ *
+ * @param other what to compare a substring in
+ * @param offset offset into other
+ * @param length number of bytes to compare
+ * @return true for equality of substrings, else false.
+ */
+ @Override
+ final boolean equalsRange(ByteString other, int offset, int length) {
+ if (length > other.size()) {
+ throw new IllegalArgumentException("Length too large: " + length + size());
+ }
+ if (offset + length > other.size()) {
+ throw new IllegalArgumentException(
+ "Ran off end of other: " + offset + ", " + length + ", " + other.size());
+ }
+
+ if (other instanceof LiteralByteString) {
+ LiteralByteString lbsOther = (LiteralByteString) other;
+ byte[] thisBytes = bytes;
+ byte[] otherBytes = lbsOther.bytes;
+ int thisLimit = getOffsetIntoBytes() + length;
+ for (
+ int thisIndex = getOffsetIntoBytes(),
+ otherIndex = lbsOther.getOffsetIntoBytes() + offset;
+ (thisIndex < thisLimit); ++thisIndex, ++otherIndex) {
+ if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return other.substring(offset, offset + length).equals(substring(0, length));
+ }
+
+ @Override
+ protected final int partialHash(int h, int offset, int length) {
+ return Internal.partialHash(h, bytes, getOffsetIntoBytes() + offset, length);
+ }
+
+ // =================================================================
+ // Input stream
+
+ @Override
+ public final InputStream newInput() {
+ return new ByteArrayInputStream(bytes, getOffsetIntoBytes(), size()); // No copy
+ }
+
+ @Override
+ public final CodedInputStream newCodedInput() {
+ // We trust CodedInputStream not to modify the bytes, or to give anyone
+ // else access to them.
+ return CodedInputStream.newInstance(
+ bytes, getOffsetIntoBytes(), size(), true /* bufferIsImmutable */);
+ }
+
+ // =================================================================
+ // Internal methods
+
+ /**
+ * Offset into {@code bytes[]} to use, non-zero for substrings.
+ *
+ * @return always 0 for this class
+ */
+ protected int getOffsetIntoBytes() {
+ return 0;
+ }
+ }
+
+ /**
+ * This class is used to represent the substring of a {@link ByteString} over a
+ * single byte array. In terms of the public API of {@link ByteString}, you end
+ * up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link
+ * ByteString#substring(int, int)}.
+ *
+ * <p>This class contains most of the overhead involved in creating a substring
+ * from a {@link LiteralByteString}. The overhead involves some range-checking
+ * and two extra fields.
+ *
+ * @author carlanton@google.com (Carl Haverl)
+ */
+ // Keep this class private to avoid deadlocks in classloading across threads as ByteString's
+ // static initializer loads LiteralByteString and another thread loads BoundedByteString.
+ private static final class BoundedByteString extends LiteralByteString {
+
+ private final int bytesOffset;
+ private final int bytesLength;
+
+ /**
+ * Creates a {@code BoundedByteString} backed by the sub-range of given array,
+ * without copying.
+ *
+ * @param bytes array to wrap
+ * @param offset index to first byte to use in bytes
+ * @param length number of bytes to use from bytes
+ * @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0},
+ * or if {@code offset + length >
+ * bytes.length}.
+ */
+ BoundedByteString(byte[] bytes, int offset, int length) {
+ super(bytes);
+ checkRange(offset, offset + length, bytes.length);
+
+ this.bytesOffset = offset;
+ this.bytesLength = length;
+ }
+
+ /**
+ * Gets the byte at the given index.
+ * Throws {@link ArrayIndexOutOfBoundsException}
+ * for backwards-compatibility reasons although it would more properly be
+ * {@link IndexOutOfBoundsException}.
+ *
+ * @param index index of byte
+ * @return the value
+ * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
+ */
+ @Override
+ public byte byteAt(int index) {
+ // We must check the index ourselves as we cannot rely on Java array index
+ // checking for substrings.
+ checkIndex(index, size());
+ return bytes[bytesOffset + index];
+ }
+
+ @Override
+ public int size() {
+ return bytesLength;
+ }
+
+ @Override
+ protected int getOffsetIntoBytes() {
+ return bytesOffset;
+ }
+
+ // =================================================================
+ // ByteString -> byte[]
+
+ @Override
+ protected void copyToInternal(byte[] target, int sourceOffset, int targetOffset,
+ int numberToCopy) {
+ System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target,
+ targetOffset, numberToCopy);
+ }
+
+ // =================================================================
+ // Serializable
+
+ private static final long serialVersionUID = 1L;
+
+ Object writeReplace() {
+ return ByteString.wrap(toByteArray());
+ }
+
+ private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException {
+ throw new InvalidObjectException(
+ "BoundedByteStream instances are not to be serialized directly");
+ }
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
index d201f7c5..b3118ee0 100644
--- a/java/src/main/java/com/google/protobuf/CodedInputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
@@ -70,7 +70,15 @@ public final class CodedInputStream {
*/
public static CodedInputStream newInstance(final byte[] buf, final int off,
final int len) {
- CodedInputStream result = new CodedInputStream(buf, off, len);
+ return newInstance(buf, off, len, false);
+ }
+
+ /**
+ * Create a new CodedInputStream wrapping the given byte array slice.
+ */
+ public static CodedInputStream newInstance(final byte[] buf, final int off,
+ final int len, boolean bufferIsImmutable) {
+ CodedInputStream result = new CodedInputStream(buf, off, len, bufferIsImmutable);
try {
// Some uses of CodedInputStream can be more efficient if they know
// exactly how many bytes are available. By pushing the end point of the
@@ -113,31 +121,6 @@ public final class CodedInputStream {
}
}
- /**
- * Create a new CodedInputStream wrapping a LiteralByteString.
- */
- static CodedInputStream newInstance(LiteralByteString byteString) {
- CodedInputStream result = new CodedInputStream(byteString);
- try {
- // Some uses of CodedInputStream can be more efficient if they know
- // exactly how many bytes are available. By pushing the end point of the
- // buffer as a limit, we allow them to get this information via
- // getBytesUntilLimit(). Pushing a limit that we know is at the end of
- // the stream can never hurt, since we can never past that point anyway.
- result.pushLimit(byteString.size());
- } catch (InvalidProtocolBufferException ex) {
- // The only reason pushLimit() might throw an exception here is if len
- // is negative. Normally pushLimit()'s parameter comes directly off the
- // wire, so it's important to catch exceptions in case of corrupt or
- // malicious data. However, in this case, we expect that len is not a
- // user-supplied value, so we can assume that it being negative indicates
- // a programming error. Therefore, throwing an unchecked exception is
- // appropriate.
- throw new IllegalArgumentException(ex);
- }
- return result;
- }
-
// -----------------------------------------------------------------
/**
@@ -506,7 +489,7 @@ public final class CodedInputStream {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final ByteString result = bufferIsImmutable && enableAliasing
- ? new BoundedByteString(buffer, bufferPos, size)
+ ? ByteString.wrap(buffer, bufferPos, size)
: ByteString.copyFrom(buffer, bufferPos, size);
bufferPos += size;
return result;
@@ -514,7 +497,7 @@ public final class CodedInputStream {
return ByteString.EMPTY;
} else {
// Slow path: Build a byte array first then copy it.
- return new LiteralByteString(readRawBytesSlowPath(size));
+ return ByteString.wrap(readRawBytesSlowPath(size));
}
}
@@ -886,13 +869,13 @@ public final class CodedInputStream {
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
private static final int BUFFER_SIZE = 4096;
- private CodedInputStream(final byte[] buffer, final int off, final int len) {
+ private CodedInputStream(final byte[] buffer, final int off, final int len, boolean bufferIsImmutable) {
this.buffer = buffer;
bufferSize = off + len;
bufferPos = off;
totalBytesRetired = -off;
input = null;
- bufferIsImmutable = false;
+ this.bufferIsImmutable = bufferIsImmutable;
}
private CodedInputStream(final InputStream input) {
@@ -904,15 +887,6 @@ public final class CodedInputStream {
bufferIsImmutable = false;
}
- private CodedInputStream(final LiteralByteString byteString) {
- buffer = byteString.bytes;
- bufferPos = byteString.getOffsetIntoBytes();
- bufferSize = bufferPos + byteString.size();
- totalBytesRetired = -bufferPos;
- input = null;
- bufferIsImmutable = true;
- }
-
public void enableAliasing(boolean enabled) {
this.enableAliasing = enabled;
}
@@ -1056,20 +1030,6 @@ public final class CodedInputStream {
private RefillCallback refillCallback = null;
/**
- * Ensures that at least {@code n} bytes are available in the buffer, reading
- * more bytes from the input if necessary to make it so. Caller must ensure
- * that the requested space is less than BUFFER_SIZE.
- *
- * @throws InvalidProtocolBufferException The end of the stream or the current
- * limit was reached.
- */
- private void ensureAvailable(int n) throws IOException {
- if (bufferSize - bufferPos < n) {
- refillBuffer(n);
- }
- }
-
- /**
* Reads more bytes from the input, making at least {@code n} bytes available
* in the buffer. Caller must ensure that the requested space is not yet
* available, and that the requested space is less than BUFFER_SIZE.
@@ -1180,86 +1140,97 @@ public final class CodedInputStream {
}
}
- if (totalBytesRetired + bufferPos + size > currentLimit) {
+ // Verify that the message size so far has not exceeded sizeLimit.
+ int currentMessageSize = totalBytesRetired + bufferPos + size;
+ if (currentMessageSize > sizeLimit) {
+ throw InvalidProtocolBufferException.sizeLimitExceeded();
+ }
+
+ // Verify that the message size so far has not exceeded currentLimit.
+ if (currentMessageSize > currentLimit) {
// Read to the end of the stream anyway.
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
- // Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
- 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.
+ // We need the input stream to proceed.
+ if (input == null) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ final int originalBufferPos = bufferPos;
+ final int bufferedBytes = bufferSize - bufferPos;
+
+ // Mark the current buffer consumed.
+ totalBytesRetired += bufferSize;
+ bufferPos = 0;
+ bufferSize = 0;
- // First copy what we have.
+ // Determine the number of bytes we need to read from the input stream.
+ int sizeLeft = size - bufferedBytes;
+ // TODO(nathanmittler): Consider using a value larger than BUFFER_SIZE.
+ if (sizeLeft < BUFFER_SIZE || sizeLeft <= input.available()) {
+ // Either the bytes we need are known to be available, or the required buffer is
+ // within an allowed threshold - go ahead and allocate the buffer now.
final byte[] bytes = new byte[size];
- int pos = bufferSize - bufferPos;
- System.arraycopy(buffer, bufferPos, bytes, 0, pos);
- bufferPos = bufferSize;
- // We want to refill the buffer and then copy from the buffer into our
- // byte array rather than reading directly into our byte array because
- // the input may be unbuffered.
- ensureAvailable(size - pos);
- System.arraycopy(buffer, 0, bytes, pos, size - pos);
- bufferPos = size - pos;
+ // Copy all of the buffered bytes to the result buffer.
+ System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
- 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.
- final int originalBufferPos = bufferPos;
- final 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);
- final List<byte[]> chunks = new ArrayList<byte[]>();
-
- while (sizeLeft > 0) {
- final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
- int pos = 0;
- while (pos < chunk.length) {
- final int n = (input == null) ? -1 :
- input.read(chunk, pos, chunk.length - pos);
- if (n == -1) {
- throw InvalidProtocolBufferException.truncatedMessage();
- }
- totalBytesRetired += n;
- pos += n;
+ // Fill the remaining bytes from the input stream.
+ int pos = bufferedBytes;
+ while (pos < bytes.length) {
+ int n = input.read(bytes, pos, size - pos);
+ if (n == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
}
- sizeLeft -= chunk.length;
- chunks.add(chunk);
+ totalBytesRetired += n;
+ pos += n;
}
- // OK, got everything. Now concatenate it all into one buffer.
- final 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);
+ return bytes;
+ }
- // And now all the chunks.
- for (final byte[] chunk : chunks) {
- System.arraycopy(chunk, 0, bytes, pos, chunk.length);
- pos += chunk.length;
+ // 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.
+ final List<byte[]> chunks = new ArrayList<byte[]>();
+
+ while (sizeLeft > 0) {
+ // TODO(nathanmittler): Consider using a value larger than BUFFER_SIZE.
+ final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
+ int pos = 0;
+ while (pos < chunk.length) {
+ final int n = input.read(chunk, pos, chunk.length - pos);
+ if (n == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ totalBytesRetired += n;
+ pos += n;
}
+ sizeLeft -= chunk.length;
+ chunks.add(chunk);
+ }
- // Done.
- return bytes;
+ // OK, got everything. Now concatenate it all into one buffer.
+ final byte[] bytes = new byte[size];
+
+ // Start by copying the leftover bytes from this.buffer.
+ System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+
+ // And now all the chunks.
+ int pos = bufferedBytes;
+ for (final byte[] chunk : chunks) {
+ System.arraycopy(chunk, 0, bytes, pos, chunk.length);
+ pos += chunk.length;
}
+
+ // Done.
+ return bytes;
}
/**
diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
index 291bd20a..d8ebad21 100644
--- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -53,7 +53,7 @@ import java.util.logging.Logger;
* @author kneton@google.com Kenton Varda
*/
public final class CodedOutputStream {
-
+
private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
// TODO(dweis): Consider migrating to a ByteBuffer.
@@ -243,19 +243,6 @@ public final class CodedOutputStream {
}
- /**
- * Write a group represented by an {@link UnknownFieldSet}.
- *
- * @deprecated UnknownFieldSet now implements MessageLite, so you can just
- * call {@link #writeGroup}.
- */
- @Deprecated
- public void writeUnknownGroup(final int fieldNumber,
- final MessageLite value)
- throws IOException {
- writeGroup(fieldNumber, value);
- }
-
/** Write an embedded message field, including tag, to the stream. */
public void writeMessage(final int fieldNumber, final MessageLite value)
throws IOException {
@@ -428,7 +415,7 @@ public final class CodedOutputStream {
try {
efficientWriteStringNoTag(value);
} catch (UnpairedSurrogateException e) {
- logger.log(Level.WARNING,
+ logger.log(Level.WARNING,
"Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", e);
inefficientWriteStringNoTag(value);
}
@@ -449,10 +436,10 @@ public final class CodedOutputStream {
* Write a {@code string} field to the stream efficiently. If the {@code string} is malformed,
* this method rolls back its changes and throws an {@link UnpairedSurrogateException} with the
* intent that the caller will catch and retry with {@link #inefficientWriteStringNoTag(String)}.
- *
+ *
* @param value the string to write to the stream
- *
- * @throws UnpairedSurrogateException when {@code value} is ill-formed UTF-16.
+ *
+ * @throws UnpairedSurrogateException when {@code value} is ill-formed UTF-16.
*/
private void efficientWriteStringNoTag(final String value) throws IOException {
// UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
@@ -510,18 +497,6 @@ public final class CodedOutputStream {
}
- /**
- * Write a group represented by an {@link UnknownFieldSet}.
- *
- * @deprecated UnknownFieldSet now implements MessageLite, so you can just
- * call {@link #writeGroupNoTag}.
- */
- @Deprecated
- public void writeUnknownGroupNoTag(final MessageLite value)
- throws IOException {
- writeGroupNoTag(value);
- }
-
/** Write an embedded message field to the stream. */
public void writeMessageNoTag(final MessageLite value) throws IOException {
writeRawVarint32(value.getSerializedSize());
@@ -685,20 +660,6 @@ public final class CodedOutputStream {
}
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code group} field represented by an {@code UnknownFieldSet}, including
- * tag.
- *
- * @deprecated UnknownFieldSet now implements MessageLite, so you can just
- * call {@link #computeGroupSize}.
- */
- @Deprecated
- public static int computeUnknownGroupSize(final int fieldNumber,
- final MessageLite value) {
- return computeGroupSize(fieldNumber, value);
- }
-
- /**
* Compute the number of bytes that would be needed to encode an
* embedded message field, including tag.
*/
@@ -927,19 +888,6 @@ public final class CodedOutputStream {
}
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code group} field represented by an {@code UnknownFieldSet}, including
- * tag.
- *
- * @deprecated UnknownFieldSet now implements MessageLite, so you can just
- * call {@link #computeUnknownGroupSizeNoTag}.
- */
- @Deprecated
- public static int computeUnknownGroupSizeNoTag(final MessageLite value) {
- return computeGroupSizeNoTag(value);
- }
-
- /**
* Compute the number of bytes that would be needed to encode an embedded
* message field.
*/
@@ -1295,10 +1243,10 @@ public final class CodedOutputStream {
* negative.
*/
public static int computeRawVarint32Size(final 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;
+ if ((value & (~0 << 7)) == 0) return 1;
+ if ((value & (~0 << 14)) == 0) return 2;
+ if ((value & (~0 << 21)) == 0) return 3;
+ if ((value & (~0 << 28)) == 0) return 4;
return 5;
}
@@ -1316,17 +1264,16 @@ public final class CodedOutputStream {
}
/** Compute the number of bytes that would be needed to encode a varint. */
- public static int computeRawVarint64Size(final 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;
+ public static int computeRawVarint64Size(long value) {
+ // handle two popular special cases up front ...
+ if ((value & (~0L << 7)) == 0L) return 1;
+ if (value < 0L) return 10;
+ // ... leaving us with 8 remaining, which we can divide and conquer
+ int n = 2;
+ if ((value & (~0L << 35)) != 0L) { n += 4; value >>>= 28; }
+ if ((value & (~0L << 21)) != 0L) { n += 2; value >>>= 14; }
+ if ((value & (~0L << 14)) != 0L) { n += 1; }
+ return n;
}
/** Write a little-endian 32-bit integer. */
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java
index 7cfc47f7..5e15cfbe 100644
--- a/java/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -889,6 +889,11 @@ public final class Descriptors {
*/
public String getFullName() { return fullName; }
+ /** Get the JSON name of this field. */
+ public String getJsonName() {
+ return jsonName;
+ }
+
/**
* Get the field's java type. This is just for convenience. Every
* {@code FieldDescriptorProto.Type} maps to exactly one Java type.
@@ -1079,6 +1084,7 @@ public final class Descriptors {
private FieldDescriptorProto proto;
private final String fullName;
+ private final String jsonName;
private final FileDescriptor file;
private final Descriptor extensionScope;
@@ -1157,6 +1163,38 @@ public final class Descriptors {
private final Object defaultDefault;
}
+ // TODO(xiaofeng): Implement it consistently across different languages. See b/24751348.
+ private static String fieldNameToLowerCamelCase(String name) {
+ StringBuilder result = new StringBuilder(name.length());
+ boolean isNextUpperCase = false;
+ for (int i = 0; i < name.length(); i++) {
+ Character ch = name.charAt(i);
+ if (Character.isLowerCase(ch)) {
+ if (isNextUpperCase) {
+ result.append(Character.toUpperCase(ch));
+ } else {
+ result.append(ch);
+ }
+ isNextUpperCase = false;
+ } else if (Character.isUpperCase(ch)) {
+ if (i == 0) {
+ // Force first letter to lower-case.
+ result.append(Character.toLowerCase(ch));
+ } else {
+ // Capital letters after the first are left as-is.
+ result.append(ch);
+ }
+ isNextUpperCase = false;
+ } else if (Character.isDigit(ch)) {
+ result.append(ch);
+ isNextUpperCase = false;
+ } else {
+ isNextUpperCase = true;
+ }
+ }
+ return result.toString();
+ }
+
private FieldDescriptor(final FieldDescriptorProto proto,
final FileDescriptor file,
final Descriptor parent,
@@ -1167,6 +1205,11 @@ public final class Descriptors {
this.proto = proto;
fullName = computeFullName(file, parent, proto.getName());
this.file = file;
+ if (proto.hasJsonName()) {
+ jsonName = proto.getJsonName();
+ } else {
+ jsonName = fieldNameToLowerCamelCase(proto.getName());
+ }
if (proto.hasType()) {
type = Type.valueOf(proto.getType());
diff --git a/java/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
index 90ebe109..bcc9d6ee 100644
--- a/java/src/main/java/com/google/protobuf/DoubleArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
@@ -68,10 +68,17 @@ final class DoubleArrayList
private int size;
/**
- * Constructs a new mutable {@code DoubleArrayList}.
+ * Constructs a new mutable {@code DoubleArrayList} with default capacity.
*/
DoubleArrayList() {
- array = new double[DEFAULT_CAPACITY];
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Constructs a new mutable {@code DoubleArrayList} with the provided capacity.
+ */
+ DoubleArrayList(int capacity) {
+ array = new double[capacity];
size = 0;
}
diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
index 3ea1b688..3ea1b688 100644
--- a/java/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
diff --git a/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java b/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java
new file mode 100644
index 00000000..6f41fb81
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java
@@ -0,0 +1,36 @@
+package com.google.protobuf;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates a public API that can change at any time, and has no guarantee of API stability and
+ * backward-compatibility.
+ *
+ * <p>Usage guidelines:
+ * <ol>
+ * <li>This annotation is used only on public API. Internal interfaces should not use it.</li>
+ * <li>This annotation should only be added to new APIs. Adding it to an existing API is
+ * considered API-breaking.</li>
+ * <li>Removing this annotation from an API gives it stable status.</li>
+ * </ol>
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.TYPE})
+@Documented
+public @interface ExperimentalApi {
+ /**
+ * Context information such as links to discussion thread, tracking issue etc.
+ */
+ String value() default "";
+}
+
diff --git a/java/src/main/java/com/google/protobuf/Extension.java b/java/core/src/main/java/com/google/protobuf/Extension.java
index 68d29f33..68d29f33 100644
--- a/java/src/main/java/com/google/protobuf/Extension.java
+++ b/java/core/src/main/java/com/google/protobuf/Extension.java
diff --git a/java/src/main/java/com/google/protobuf/ExtensionLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionLite.java
index f8f5bd2c..f8f5bd2c 100644
--- a/java/src/main/java/com/google/protobuf/ExtensionLite.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionLite.java
diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
index 0067392f..0067392f 100644
--- a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
index 65cf7385..65cf7385 100644
--- a/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java
index 47924b65..47924b65 100644
--- a/java/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java
diff --git a/java/src/main/java/com/google/protobuf/FloatArrayList.java b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
index 293eaff6..033b5eed 100644
--- a/java/src/main/java/com/google/protobuf/FloatArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
@@ -67,10 +67,17 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
private int size;
/**
- * Constructs a new mutable {@code FloatArrayList}.
+ * Constructs a new mutable {@code FloatArrayList} with default capacity.
*/
FloatArrayList() {
- array = new float[DEFAULT_CAPACITY];
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Constructs a new mutable {@code FloatArrayList} with the provided capacity.
+ */
+ FloatArrayList(int capacity) {
+ array = new float[capacity];
size = 0;
}
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
index d84fa75c..ceb97a4e 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -36,15 +36,13 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
-import com.google.protobuf.GeneratedMessageLite.ExtendableMessage;
-import com.google.protobuf.GeneratedMessageLite.GeneratedExtension;
import java.io.IOException;
+import java.io.InputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -276,6 +274,60 @@ public abstract class GeneratedMessage extends AbstractMessage
return unknownFields.mergeFieldFrom(tag, input);
}
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser, InputStream input)
+ throws IOException {
+ try {
+ return parser.parseFrom(input);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser, InputStream input,
+ ExtensionRegistryLite extensions) throws IOException {
+ try {
+ return parser.parseFrom(input, extensions);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser,
+ CodedInputStream input) throws IOException {
+ try {
+ return parser.parseFrom(input);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser,
+ CodedInputStream input, ExtensionRegistryLite extensions) throws IOException {
+ try {
+ return parser.parseFrom(input, extensions);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseDelimitedWithIOException(Parser<M> parser,
+ InputStream input) throws IOException {
+ try {
+ return parser.parseDelimitedFrom(input);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseDelimitedWithIOException(Parser<M> parser,
+ InputStream input, ExtensionRegistryLite extensions) throws IOException {
+ try {
+ return parser.parseDelimitedFrom(input, extensions);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
@Override
public void writeTo(final CodedOutputStream output) throws IOException {
MessageReflection.writeMessageTo(this, getAllFieldsRaw(), output, false);
@@ -667,7 +719,7 @@ public abstract class GeneratedMessage extends AbstractMessage
"No map fields found in " + getClass().getName());
}
- /** Like {@link internalGetMapField} but return a mutable version. */
+ /** Like {@link #internalGetMapField} but return a mutable version. */
@SuppressWarnings({"unused", "rawtypes"})
protected MapField internalGetMutableMapField(int fieldNumber) {
// Note that we can't use descriptor names here because this method will
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index 4316efee..81e1862c 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -102,6 +102,11 @@ public abstract class GeneratedMessageLite<
* @return {@code true} unless the tag is an end-group tag.
*/
protected boolean parseUnknownField(int tag, CodedInputStream input) throws IOException {
+ // This will avoid the allocation of unknown fields when a group tag is encountered.
+ if (WireFormat.getTagWireType(tag) == WireFormat.WIRETYPE_END_GROUP) {
+ return false;
+ }
+
ensureUnknownFieldsInitialized();
return unknownFields.mergeFieldFrom(tag, input);
}
@@ -1173,6 +1178,10 @@ public abstract class GeneratedMessageLite<
return new IntArrayList();
}
+ protected static IntList newIntListWithCapacity(int capacity) {
+ return new IntArrayList(capacity);
+ }
+
protected static IntList newIntList(List<Integer> toCopy) {
return new IntArrayList(toCopy);
}
@@ -1180,10 +1189,14 @@ public abstract class GeneratedMessageLite<
protected static IntList emptyIntList() {
return IntArrayList.emptyList();
}
-
+
protected static LongList newLongList() {
return new LongArrayList();
}
+
+ protected static LongList newLongListWithCapacity(int capacity) {
+ return new LongArrayList(capacity);
+ }
protected static LongList newLongList(List<Long> toCopy) {
return new LongArrayList(toCopy);
@@ -1197,6 +1210,10 @@ public abstract class GeneratedMessageLite<
return new FloatArrayList();
}
+ protected static FloatList newFloatListWithCapacity(int capacity) {
+ return new FloatArrayList(capacity);
+ }
+
protected static FloatList newFloatList(List<Float> toCopy) {
return new FloatArrayList(toCopy);
}
@@ -1209,6 +1226,10 @@ public abstract class GeneratedMessageLite<
return new DoubleArrayList();
}
+ protected static DoubleList newDoubleListWithCapacity(int capacity) {
+ return new DoubleArrayList(capacity);
+ }
+
protected static DoubleList newDoubleList(List<Double> toCopy) {
return new DoubleArrayList(toCopy);
}
@@ -1221,6 +1242,10 @@ public abstract class GeneratedMessageLite<
return new BooleanArrayList();
}
+ protected static BooleanList newBooleanListWithCapacity(int capacity) {
+ return new BooleanArrayList(capacity);
+ }
+
protected static BooleanList newBooleanList(List<Boolean> toCopy) {
return new BooleanArrayList(toCopy);
}
@@ -1237,6 +1262,10 @@ public abstract class GeneratedMessageLite<
return new ProtobufArrayList<E>(toCopy);
}
+ protected static <E> ProtobufList<E> newProtobufListWithCapacity(int capacity) {
+ return new ProtobufArrayList<E>(capacity);
+ }
+
protected static <E> ProtobufList<E> emptyProtobufList() {
return ProtobufArrayList.emptyList();
}
diff --git a/java/src/main/java/com/google/protobuf/IntArrayList.java b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
index f7609cc9..f4e68ed8 100644
--- a/java/src/main/java/com/google/protobuf/IntArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
@@ -67,10 +67,17 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
private int size;
/**
- * Constructs a new mutable {@code IntArrayList}.
+ * Constructs a new mutable {@code IntArrayList} with default capacity.
*/
IntArrayList() {
- array = new int[DEFAULT_CAPACITY];
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Constructs a new mutable {@code IntArrayList} with the provided capacity.
+ */
+ IntArrayList(int capacity) {
+ array = new int[capacity];
size = 0;
}
diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/core/src/main/java/com/google/protobuf/Internal.java
index 11901998..e19b6dca 100644
--- a/java/src/main/java/com/google/protobuf/Internal.java
+++ b/java/core/src/main/java/com/google/protobuf/Internal.java
@@ -283,10 +283,32 @@ public class Internal {
// ByteString with the same content. This is to ensure that the generated
// hashCode() method will return the same value as the pure reflection
// based hashCode() method.
- return LiteralByteString.hashCode(bytes);
+ return Internal.hashCode(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Helper method for implementing {@link LiteralByteString#hashCode()}.
+ */
+ static int hashCode(byte[] bytes, int offset, int length) {
+ // The hash code for a byte array should be the same as the hash code for a
+ // ByteString with the same content. This is to ensure that the generated
+ // hashCode() method will return the same value as the pure reflection
+ // based hashCode() method.
+ int h = Internal.partialHash(length, bytes, offset, length);
+ return h == 0 ? 1 : h;
}
/**
+ * Helper method for continuously hashing bytes.
+ */
+ static int partialHash(int h, byte[] bytes, int offset, int length) {
+ for (int i = offset; i < offset + length; i++) {
+ h = h * 31 + bytes[i];
+ }
+ return h;
+ }
+
+ /**
* Helper method for implementing {@link Message#equals(Object)} for bytes
* field.
*/
@@ -337,8 +359,7 @@ public class Internal {
public static int hashCodeByteBuffer(ByteBuffer bytes) {
if (bytes.hasArray()) {
// Fast path.
- int h = LiteralByteString.hashCode(bytes.capacity(), bytes.array(),
- bytes.arrayOffset(), bytes.capacity());
+ int h = partialHash(bytes.capacity(), bytes.array(), bytes.arrayOffset(), bytes.capacity());
return h == 0 ? 1 : h;
} else {
// Read the data into a temporary byte array before calculating the
@@ -353,7 +374,7 @@ public class Internal {
final int length = duplicated.remaining() <= bufferSize ?
duplicated.remaining() : bufferSize;
duplicated.get(buffer, 0, length);
- h = LiteralByteString.hashCode(h, buffer, 0, length);
+ h = partialHash(h, buffer, 0, length);
}
return h == 0 ? 1 : h;
}
@@ -385,7 +406,6 @@ public class Internal {
public static final CodedInputStream EMPTY_CODED_INPUT_STREAM =
CodedInputStream.newInstance(EMPTY_BYTE_ARRAY);
-
/**
* Provides an immutable view of {@code List<T>} around a {@code List<F>}.
*
diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
index 0a761052..85ce7b24 100644
--- a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
+++ b/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
@@ -46,6 +46,10 @@ public class InvalidProtocolBufferException extends IOException {
super(description);
}
+ public InvalidProtocolBufferException(IOException e) {
+ super(e.getMessage(), e);
+ }
+
/**
* Attaches an unfinished message to the exception to support best-effort
* parsing in {@code Parser} interface.
@@ -66,6 +70,14 @@ public class InvalidProtocolBufferException extends IOException {
return unfinishedMessage;
}
+ /**
+ * Unwraps the underlying {@link IOException} if this exception was caused by an I/O
+ * problem. Otherwise, returns {@code this}.
+ */
+ public IOException unwrapIOException() {
+ return getCause() instanceof IOException ? (IOException) getCause() : this;
+ }
+
static InvalidProtocolBufferException truncatedMessage() {
return new InvalidProtocolBufferException(
"While parsing a protocol message, the input ended unexpectedly " +
diff --git a/java/src/main/java/com/google/protobuf/LazyField.java b/java/core/src/main/java/com/google/protobuf/LazyField.java
index 3da8b900..5e0a485c 100644
--- a/java/src/main/java/com/google/protobuf/LazyField.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyField.java
@@ -39,14 +39,14 @@ import java.util.Map.Entry;
*
* Most of key methods are implemented in {@link LazyFieldLite} but this class
* can contain default instance of the message to provide {@code hashCode()},
- * {@code euqals()} and {@code toString()}.
+ * {@code equals()} and {@code toString()}.
*
* @author xiangl@google.com (Xiang Li)
*/
public class LazyField extends LazyFieldLite {
/**
- * Carry a message's default instance which is used by {@code hashCode()}, {@code euqals()} and
+ * Carry a message's default instance which is used by {@code hashCode()}, {@code equals()} and
* {@code toString()}.
*/
private final MessageLite defaultInstance;
diff --git a/java/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
index eea1fe3c..eea1fe3c 100644
--- a/java/src/main/java/com/google/protobuf/LazyFieldLite.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
index c3be3cca..c3be3cca 100644
--- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
diff --git a/java/src/main/java/com/google/protobuf/LazyStringList.java b/java/core/src/main/java/com/google/protobuf/LazyStringList.java
index 3eeedca1..3eeedca1 100644
--- a/java/src/main/java/com/google/protobuf/LazyStringList.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyStringList.java
diff --git a/java/src/main/java/com/google/protobuf/LongArrayList.java b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
index 298617ff..ebe62029 100644
--- a/java/src/main/java/com/google/protobuf/LongArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
@@ -67,10 +67,17 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
private int size;
/**
- * Constructs a new mutable {@code LongArrayList}.
+ * Constructs a new mutable {@code LongArrayList} with default capacity.
*/
LongArrayList() {
- array = new long[DEFAULT_CAPACITY];
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Constructs a new mutable {@code LongArrayList} with the provided capacity.
+ */
+ LongArrayList(int capacity) {
+ array = new long[capacity];
size = 0;
}
diff --git a/java/src/main/java/com/google/protobuf/MapEntry.java b/java/core/src/main/java/com/google/protobuf/MapEntry.java
index 31414bb4..31414bb4 100644
--- a/java/src/main/java/com/google/protobuf/MapEntry.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntry.java
diff --git a/java/src/main/java/com/google/protobuf/MapEntryLite.java b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
index bcffa946..bcffa946 100644
--- a/java/src/main/java/com/google/protobuf/MapEntryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/core/src/main/java/com/google/protobuf/MapField.java
index b290993c..b290993c 100644
--- a/java/src/main/java/com/google/protobuf/MapField.java
+++ b/java/core/src/main/java/com/google/protobuf/MapField.java
diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
index c17fa7b1..960b6339 100644
--- a/java/src/main/java/com/google/protobuf/MapFieldLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
@@ -30,6 +30,8 @@
package com.google.protobuf;
+import com.google.protobuf.Internal.EnumLite;
+
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -44,7 +46,7 @@ import java.util.Set;
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*/
-public class MapFieldLite<K, V> implements MutabilityOracle {
+public final class MapFieldLite<K, V> implements MutabilityOracle {
private MutatabilityAwareMap<K, V> mapData;
private boolean isMutable;
@@ -134,10 +136,11 @@ public class MapFieldLite<K, V> implements MutabilityOracle {
private static int calculateHashCodeForObject(Object a) {
if (a instanceof byte[]) {
- return LiteralByteString.hashCode((byte[]) a);
+ return Internal.hashCode((byte[]) a);
}
- if (a instanceof Internal.EnumLite) {
- return Internal.hashEnum((Internal.EnumLite) a);
+ // Enums should be stored as integers internally.
+ if (a instanceof EnumLite) {
+ throw new UnsupportedOperationException();
}
return a.hashCode();
}
diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/core/src/main/java/com/google/protobuf/Message.java
index 9516d71f..9516d71f 100644
--- a/java/src/main/java/com/google/protobuf/Message.java
+++ b/java/core/src/main/java/com/google/protobuf/Message.java
diff --git a/java/src/main/java/com/google/protobuf/MessageLite.java b/java/core/src/main/java/com/google/protobuf/MessageLite.java
index 798b7943..798b7943 100644
--- a/java/src/main/java/com/google/protobuf/MessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageLite.java
diff --git a/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java b/java/core/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java
index 818386ce..818386ce 100644
--- a/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java
diff --git a/python/google/protobuf/util/__init__.py b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
index e69de29b..e69de29b 100644
--- a/python/google/protobuf/util/__init__.py
+++ b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
diff --git a/java/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
index f0fc4859..f0fc4859 100644
--- a/java/src/main/java/com/google/protobuf/MessageOrBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
diff --git a/java/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
index de4bfd3e..de4bfd3e 100644
--- a/java/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
diff --git a/java/src/main/java/com/google/protobuf/MutabilityOracle.java b/java/core/src/main/java/com/google/protobuf/MutabilityOracle.java
index 82b723c9..82b723c9 100644
--- a/java/src/main/java/com/google/protobuf/MutabilityOracle.java
+++ b/java/core/src/main/java/com/google/protobuf/MutabilityOracle.java
diff --git a/java/core/src/main/java/com/google/protobuf/NioByteString.java b/java/core/src/main/java/com/google/protobuf/NioByteString.java
new file mode 100644
index 00000000..f71e41b2
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/NioByteString.java
@@ -0,0 +1,309 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.InvalidMarkException;
+import java.nio.channels.Channels;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link ByteString} that wraps around a {@link ByteBuffer}.
+ */
+final class NioByteString extends ByteString.LeafByteString {
+ private final ByteBuffer buffer;
+
+ NioByteString(ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException("buffer");
+ }
+
+ this.buffer = buffer.slice();
+ }
+
+ // =================================================================
+ // Serializable
+
+ /**
+ * Magic method that lets us override serialization behavior.
+ */
+ private Object writeReplace() {
+ return ByteString.copyFrom(buffer.slice());
+ }
+
+ /**
+ * Magic method that lets us override deserialization behavior.
+ */
+ private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException {
+ throw new InvalidObjectException("NioByteString instances are not to be serialized directly");
+ }
+
+ // =================================================================
+
+ @Override
+ public byte byteAt(int index) {
+ try {
+ return buffer.get(index);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw e;
+ } catch (IndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(e.getMessage());
+ }
+ }
+
+ @Override
+ public int size() {
+ return buffer.remaining();
+ }
+
+ @Override
+ public ByteString substring(int beginIndex, int endIndex) {
+ try {
+ ByteBuffer slice = slice(beginIndex, endIndex);
+ return new NioByteString(slice);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw e;
+ } catch (IndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(e.getMessage());
+ }
+ }
+
+ @Override
+ protected void copyToInternal(
+ byte[] target, int sourceOffset, int targetOffset, int numberToCopy) {
+ ByteBuffer slice = buffer.slice();
+ slice.position(sourceOffset);
+ slice.get(target, targetOffset, numberToCopy);
+ }
+
+ @Override
+ public void copyTo(ByteBuffer target) {
+ target.put(buffer.slice());
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException {
+ writeToInternal(out, buffer.position(), buffer.remaining());
+ }
+
+ @Override
+ boolean equalsRange(ByteString other, int offset, int length) {
+ return substring(0, length).equals(other.substring(offset, offset + length));
+ }
+
+ @Override
+ void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException {
+ if (buffer.hasArray()) {
+ // Optimized write for array-backed buffers.
+ // Note that we're taking the risk that a malicious OutputStream could modify the array.
+ int bufferOffset = buffer.arrayOffset() + buffer.position() + sourceOffset;
+ out.write(buffer.array(), bufferOffset, numberToWrite);
+ return;
+ }
+
+ // Slow path
+ if (out instanceof FileOutputStream || numberToWrite >= 8192) {
+ // Use a channel to write out the ByteBuffer.
+ Channels.newChannel(out).write(slice(sourceOffset, sourceOffset + numberToWrite));
+ } else {
+ // Just copy the data to an array and write it.
+ out.write(toByteArray());
+ }
+ }
+
+ @Override
+ public ByteBuffer asReadOnlyByteBuffer() {
+ return buffer.asReadOnlyBuffer();
+ }
+
+ @Override
+ public List<ByteBuffer> asReadOnlyByteBufferList() {
+ return Collections.singletonList(asReadOnlyByteBuffer());
+ }
+
+ @Override
+ protected String toStringInternal(Charset charset) {
+ byte[] bytes;
+ int offset;
+ if (buffer.hasArray()) {
+ bytes = buffer.array();
+ offset = buffer.arrayOffset() + buffer.position();
+ } else {
+ bytes = toByteArray();
+ offset = 0;
+ }
+ return new String(bytes, offset, size(), charset);
+ }
+
+ @Override
+ public boolean isValidUtf8() {
+ // TODO(nathanmittler): add a ByteBuffer fork for Utf8.isValidUtf8 to avoid the copy
+ byte[] bytes;
+ int startIndex;
+ if (buffer.hasArray()) {
+ bytes = buffer.array();
+ startIndex = buffer.arrayOffset() + buffer.position();
+ } else {
+ bytes = toByteArray();
+ startIndex = 0;
+ }
+ return Utf8.isValidUtf8(bytes, startIndex, startIndex + size());
+ }
+
+ @Override
+ protected int partialIsValidUtf8(int state, int offset, int length) {
+ // TODO(nathanmittler): TODO add a ByteBuffer fork for Utf8.partialIsValidUtf8 to avoid the copy
+ byte[] bytes;
+ int startIndex;
+ if (buffer.hasArray()) {
+ bytes = buffer.array();
+ startIndex = buffer.arrayOffset() + buffer.position();
+ } else {
+ bytes = toByteArray();
+ startIndex = 0;
+ }
+ return Utf8.partialIsValidUtf8(state, bytes, startIndex, startIndex + size());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof ByteString)) {
+ return false;
+ }
+ ByteString otherString = ((ByteString) other);
+ if (size() != otherString.size()) {
+ return false;
+ }
+ if (size() == 0) {
+ return true;
+ }
+ if (other instanceof NioByteString) {
+ return buffer.equals(((NioByteString) other).buffer);
+ }
+ if (other instanceof RopeByteString) {
+ return other.equals(this);
+ }
+ return buffer.equals(otherString.asReadOnlyByteBuffer());
+ }
+
+ @Override
+ protected int partialHash(int h, int offset, int length) {
+ for (int i = offset; i < offset + length; i++) {
+ h = h * 31 + buffer.get(i);
+ }
+ return h;
+ }
+
+ @Override
+ public InputStream newInput() {
+ return new InputStream() {
+ private final ByteBuffer buf = buffer.slice();
+
+ @Override
+ public void mark(int readlimit) {
+ buf.mark();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public void reset() throws IOException {
+ try {
+ buf.reset();
+ } catch (InvalidMarkException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public int available() throws IOException {
+ return buf.remaining();
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!buf.hasRemaining()) {
+ return -1;
+ }
+ return buf.get() & 0xFF;
+ }
+
+ @Override
+ public int read(byte[] bytes, int off, int len) throws IOException {
+ if (!buf.hasRemaining()) {
+ return -1;
+ }
+
+ len = Math.min(len, buf.remaining());
+ buf.get(bytes, off, len);
+ return len;
+ }
+ };
+ }
+
+ @Override
+ public CodedInputStream newCodedInput() {
+ return CodedInputStream.newInstance(buffer);
+ }
+
+ /**
+ * Creates a slice of a range of this buffer.
+ *
+ * @param beginIndex the beginning index of the slice (inclusive).
+ * @param endIndex the end index of the slice (exclusive).
+ * @return the requested slice.
+ */
+ private ByteBuffer slice(int beginIndex, int endIndex) {
+ if (beginIndex < buffer.position() || endIndex > buffer.limit() || beginIndex > endIndex) {
+ throw new IllegalArgumentException(
+ String.format("Invalid indices [%d, %d]", beginIndex, endIndex));
+ }
+
+ ByteBuffer slice = buffer.slice();
+ slice.position(beginIndex - buffer.position());
+ slice.limit(endIndex - buffer.position());
+ return slice;
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/Parser.java b/java/core/src/main/java/com/google/protobuf/Parser.java
index 227c02b7..3fa11c3b 100644
--- a/java/src/main/java/com/google/protobuf/Parser.java
+++ b/java/core/src/main/java/com/google/protobuf/Parser.java
@@ -30,6 +30,7 @@
package com.google.protobuf;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -37,9 +38,20 @@ import java.io.InputStream;
*
* The implementation should be stateless and thread-safe.
*
+ * <p>All methods may throw {@link InvalidProtocolBufferException}. In the event of invalid data,
+ * like an encoding error, the cause of the thrown exception will be {@code null}. However, if an
+ * I/O problem occurs, an exception is thrown with an {@link IOException} cause.
+ *
* @author liujisi@google.com (Pherl Liu)
*/
public interface Parser<MessageType> {
+
+ // NB(jh): Other parts of the protobuf API that parse messages distinguish between an I/O problem
+ // (like failure reading bytes from a socket) and invalid data (encoding error) via the type of
+ // thrown exception. But it would be source-incompatible to make the methods in this interface do
+ // so since they were originally spec'ed to only throw InvalidProtocolBufferException. So callers
+ // must inspect the cause of the exception to distinguish these two cases.
+
/**
* Parses a message of {@code MessageType} from the input.
*
diff --git a/java/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
index 759368c9..d2f82ac5 100644
--- a/java/src/main/java/com/google/protobuf/ProtobufArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
@@ -60,6 +60,10 @@ class ProtobufArrayList<E> extends AbstractProtobufList<E> {
list = new ArrayList<E>(toCopy);
}
+ ProtobufArrayList(int capacity) {
+ list = new ArrayList<E>(capacity);
+ }
+
@Override
public void add(int index, E element) {
ensureIsMutable();
diff --git a/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java b/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java
index 0c8df989..0c8df989 100644
--- a/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java
diff --git a/java/src/main/java/com/google/protobuf/ProtocolStringList.java b/java/core/src/main/java/com/google/protobuf/ProtocolStringList.java
index d553b41e..d553b41e 100644
--- a/java/src/main/java/com/google/protobuf/ProtocolStringList.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtocolStringList.java
diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
index f91cdbce..f91cdbce 100644
--- a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
diff --git a/java/src/main/java/com/google/protobuf/RopeByteString.java b/java/core/src/main/java/com/google/protobuf/RopeByteString.java
index 2c332624..469c90c2 100644
--- a/java/src/main/java/com/google/protobuf/RopeByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/RopeByteString.java
@@ -69,7 +69,7 @@ import java.util.Stack;
*
* @author carlanton@google.com (Carl Haverl)
*/
-class RopeByteString extends ByteString {
+final class RopeByteString extends ByteString {
/**
* BAP95. Let Fn be the nth Fibonacci number. A {@link RopeByteString} of
@@ -151,21 +151,24 @@ class RopeByteString extends ByteString {
* @return concatenation representing the same sequence as the given strings
*/
static ByteString concatenate(ByteString left, ByteString right) {
- ByteString result;
- RopeByteString leftRope =
- (left instanceof RopeByteString) ? (RopeByteString) left : null;
if (right.size() == 0) {
- result = left;
- } else if (left.size() == 0) {
- result = right;
- } else {
- int newLength = left.size() + right.size();
- if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
- // Optimization from BAP95: For short (leaves in paper, but just short
- // here) total length, do a copy of data to a new leaf.
- result = concatenateBytes(left, right);
- } else if (leftRope != null
- && leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
+ return left;
+ }
+
+ if (left.size() == 0) {
+ return right;
+ }
+
+ final int newLength = left.size() + right.size();
+ if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
+ // Optimization from BAP95: For short (leaves in paper, but just short
+ // here) total length, do a copy of data to a new leaf.
+ return concatenateBytes(left, right);
+ }
+
+ if (left instanceof RopeByteString) {
+ final RopeByteString leftRope = (RopeByteString) left;
+ if (leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
// Optimization from BAP95: As an optimization of the case where the
// ByteString is constructed by repeated concatenate, recognize the case
// where a short string is concatenated to a left-hand node whose
@@ -177,9 +180,10 @@ class RopeByteString extends ByteString {
// new parent node so that the depth of the result is the same as the
// given left tree.
ByteString newRight = concatenateBytes(leftRope.right, right);
- result = new RopeByteString(leftRope.left, newRight);
- } else if (leftRope != null
- && leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
+ return new RopeByteString(leftRope.left, newRight);
+ }
+
+ if (leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
&& leftRope.getTreeDepth() > right.getTreeDepth()) {
// Typically for concatenate-built strings the left-side is deeper than
// the right. This is our final attempt to concatenate without
@@ -187,20 +191,18 @@ class RopeByteString extends ByteString {
// is yet another optimization for building the string by repeatedly
// concatenating on the right.
ByteString newRight = new RopeByteString(leftRope.right, right);
- result = new RopeByteString(leftRope.left, newRight);
- } else {
- // Fine, we'll add a node and increase the tree depth--unless we
- // rebalance ;^)
- int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
- if (newLength >= minLengthByDepth[newDepth]) {
- // The tree is shallow enough, so don't rebalance
- result = new RopeByteString(left, right);
- } else {
- result = new Balancer().balance(left, right);
- }
+ return new RopeByteString(leftRope.left, newRight);
}
}
- return result;
+
+ // Fine, we'll add a node and increase the tree depth--unless we rebalance ;^)
+ int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
+ if (newLength >= minLengthByDepth[newDepth]) {
+ // The tree is shallow enough, so don't rebalance
+ return new RopeByteString(left, right);
+ }
+
+ return new Balancer().balance(left, right);
}
/**
@@ -211,14 +213,14 @@ class RopeByteString extends ByteString {
* @param right string on the right
* @return string formed by copying data bytes
*/
- private static LiteralByteString concatenateBytes(ByteString left,
+ private static ByteString concatenateBytes(ByteString left,
ByteString right) {
int leftSize = left.size();
int rightSize = right.size();
byte[] bytes = new byte[leftSize + rightSize];
left.copyTo(bytes, 0, 0, leftSize);
right.copyTo(bytes, 0, leftSize, rightSize);
- return new LiteralByteString(bytes); // Constructor wraps bytes
+ return ByteString.wrap(bytes); // Constructor wraps bytes
}
/**
@@ -248,22 +250,14 @@ class RopeByteString extends ByteString {
*/
@Override
public byte byteAt(int index) {
- if (index < 0) {
- throw new ArrayIndexOutOfBoundsException("Index < 0: " + index);
- }
- if (index > totalLength) {
- throw new ArrayIndexOutOfBoundsException(
- "Index > length: " + index + ", " + totalLength);
- }
+ checkIndex(index, totalLength);
- byte result;
// Find the relevant piece by recursive descent
if (index < leftLength) {
- result = left.byteAt(index);
- } else {
- result = right.byteAt(index - leftLength);
+ return left.byteAt(index);
}
- return result;
+
+ return right.byteAt(index - leftLength);
}
@Override
@@ -309,48 +303,36 @@ class RopeByteString extends ByteString {
*/
@Override
public ByteString substring(int beginIndex, int endIndex) {
- if (beginIndex < 0) {
- throw new IndexOutOfBoundsException(
- "Beginning index: " + beginIndex + " < 0");
+ final int length = checkRange(beginIndex, endIndex, totalLength);
+
+ if (length == 0) {
+ // Empty substring
+ return ByteString.EMPTY;
}
- if (endIndex > totalLength) {
- throw new IndexOutOfBoundsException(
- "End index: " + endIndex + " > " + totalLength);
+
+ if (length == totalLength) {
+ // The whole string
+ return this;
}
- int substringLength = endIndex - beginIndex;
- if (substringLength < 0) {
- throw new IndexOutOfBoundsException(
- "Beginning index larger than ending index: " + beginIndex + ", "
- + endIndex);
+
+ // Proper substring
+ if (endIndex <= leftLength) {
+ // Substring on the left
+ return left.substring(beginIndex, endIndex);
}
- ByteString result;
- if (substringLength == 0) {
- // Empty substring
- result = ByteString.EMPTY;
- } else if (substringLength == totalLength) {
- // The whole string
- result = this;
- } else {
- // Proper substring
- if (endIndex <= leftLength) {
- // Substring on the left
- result = left.substring(beginIndex, endIndex);
- } else if (beginIndex >= leftLength) {
- // Substring on the right
- result = right
- .substring(beginIndex - leftLength, endIndex - leftLength);
- } else {
- // Split substring
- ByteString leftSub = left.substring(beginIndex);
- ByteString rightSub = right.substring(0, endIndex - leftLength);
- // Intentionally not rebalancing, since in many cases these two
- // substrings will already be less deep than the top-level
- // RopeByteString we're taking a substring of.
- result = new RopeByteString(leftSub, rightSub);
- }
+ if (beginIndex >= leftLength) {
+ // Substring on the right
+ return right.substring(beginIndex - leftLength, endIndex - leftLength);
}
- return result;
+
+ // Split substring
+ ByteString leftSub = left.substring(beginIndex);
+ ByteString rightSub = right.substring(0, endIndex - leftLength);
+ // Intentionally not rebalancing, since in many cases these two
+ // substrings will already be less deep than the top-level
+ // RopeByteString we're taking a substring of.
+ return new RopeByteString(leftSub, rightSub);
}
// =================================================================
@@ -391,7 +373,7 @@ class RopeByteString extends ByteString {
List<ByteBuffer> result = new ArrayList<ByteBuffer>();
PieceIterator pieces = new PieceIterator(this);
while (pieces.hasNext()) {
- LiteralByteString byteString = pieces.next();
+ LeafByteString byteString = pieces.next();
result.add(byteString.asReadOnlyByteBuffer());
}
return result;
@@ -471,11 +453,10 @@ class RopeByteString extends ByteString {
// hashCode if it's already computed. It's arguable we should compute the
// hashCode here, and if we're going to be testing a bunch of byteStrings,
// it might even make sense.
- if (hash != 0) {
- int cachedOtherHash = otherByteString.peekCachedHashCode();
- if (cachedOtherHash != 0 && hash != cachedOtherHash) {
- return false;
- }
+ int thisHash = peekCachedHashCode();
+ int thatHash = otherByteString.peekCachedHashCode();
+ if (thisHash != 0 && thatHash != 0 && thisHash != thatHash) {
+ return false;
}
return equalsFragments(otherByteString);
@@ -492,12 +473,12 @@ class RopeByteString extends ByteString {
*/
private boolean equalsFragments(ByteString other) {
int thisOffset = 0;
- Iterator<LiteralByteString> thisIter = new PieceIterator(this);
- LiteralByteString thisString = thisIter.next();
+ Iterator<LeafByteString> thisIter = new PieceIterator(this);
+ LeafByteString thisString = thisIter.next();
int thatOffset = 0;
- Iterator<LiteralByteString> thatIter = new PieceIterator(other);
- LiteralByteString thatString = thatIter.next();
+ Iterator<LeafByteString> thatIter = new PieceIterator(other);
+ LeafByteString thatString = thatIter.next();
int pos = 0;
while (true) {
@@ -536,33 +517,6 @@ class RopeByteString extends ByteString {
}
}
- /**
- * Cached hash value. Intentionally accessed via a data race, which is safe
- * because of the Java Memory Model's "no out-of-thin-air values" guarantees
- * for ints.
- */
- private int hash = 0;
-
- @Override
- public int hashCode() {
- int h = hash;
-
- if (h == 0) {
- h = totalLength;
- h = partialHash(h, 0, totalLength);
- if (h == 0) {
- h = 1;
- }
- hash = h;
- }
- return h;
- }
-
- @Override
- protected int peekCachedHashCode() {
- return hash;
- }
-
@Override
protected int partialHash(int h, int offset, int length) {
int toIndex = offset + length;
@@ -714,34 +668,34 @@ class RopeByteString extends ByteString {
* <p>This iterator is used to implement
* {@link RopeByteString#equalsFragments(ByteString)}.
*/
- private static class PieceIterator implements Iterator<LiteralByteString> {
+ private static class PieceIterator implements Iterator<LeafByteString> {
private final Stack<RopeByteString> breadCrumbs =
new Stack<RopeByteString>();
- private LiteralByteString next;
+ private LeafByteString next;
private PieceIterator(ByteString root) {
next = getLeafByLeft(root);
}
- private LiteralByteString getLeafByLeft(ByteString root) {
+ private LeafByteString getLeafByLeft(ByteString root) {
ByteString pos = root;
while (pos instanceof RopeByteString) {
RopeByteString rbs = (RopeByteString) pos;
breadCrumbs.push(rbs);
pos = rbs.left;
}
- return (LiteralByteString) pos;
+ return (LeafByteString) pos;
}
- private LiteralByteString getNextNonEmptyLeaf() {
+ private LeafByteString getNextNonEmptyLeaf() {
while (true) {
// Almost always, we go through this loop exactly once. However, if
// we discover an empty string in the rope, we toss it and try again.
if (breadCrumbs.isEmpty()) {
return null;
} else {
- LiteralByteString result = getLeafByLeft(breadCrumbs.pop().right);
+ LeafByteString result = getLeafByLeft(breadCrumbs.pop().right);
if (!result.isEmpty()) {
return result;
}
@@ -749,6 +703,7 @@ class RopeByteString extends ByteString {
}
}
+ @Override
public boolean hasNext() {
return next != null;
}
@@ -758,15 +713,17 @@ class RopeByteString extends ByteString {
*
* @return next non-empty LiteralByteString or {@code null}
*/
- public LiteralByteString next() {
+ @Override
+ public LeafByteString next() {
if (next == null) {
throw new NoSuchElementException();
}
- LiteralByteString result = next;
+ LeafByteString result = next;
next = getNextNonEmptyLeaf();
return result;
}
+ @Override
public void remove() {
throw new UnsupportedOperationException();
}
@@ -778,55 +735,14 @@ class RopeByteString extends ByteString {
private static final long serialVersionUID = 1L;
Object writeReplace() {
- return new LiteralByteString(toByteArray());
+ return ByteString.wrap(toByteArray());
}
- private void readObject(ObjectInputStream in) throws IOException {
+ private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException {
throw new InvalidObjectException(
"RopeByteStream instances are not to be serialized directly");
}
- // =================================================================
- // ByteIterator
-
- @Override
- public ByteIterator iterator() {
- return new RopeByteIterator();
- }
-
- private class RopeByteIterator implements ByteString.ByteIterator {
-
- private final PieceIterator pieces;
- private ByteIterator bytes;
- int bytesRemaining;
-
- private RopeByteIterator() {
- pieces = new PieceIterator(RopeByteString.this);
- bytes = pieces.next().iterator();
- bytesRemaining = size();
- }
-
- public boolean hasNext() {
- return (bytesRemaining > 0);
- }
-
- public Byte next() {
- return nextByte(); // Does not instantiate a Byte
- }
-
- public byte nextByte() {
- if (!bytes.hasNext()) {
- bytes = pieces.next().iterator();
- }
- --bytesRemaining;
- return bytes.nextByte();
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
/**
* This class is the {@link RopeByteString} equivalent for
* {@link ByteArrayInputStream}.
@@ -835,7 +751,7 @@ class RopeByteString extends ByteString {
// Iterates through the pieces of the rope
private PieceIterator pieceIterator;
// The current piece
- private LiteralByteString currentPiece;
+ private LeafByteString currentPiece;
// The size of the current piece
private int currentPieceSize;
// The index of the next byte to read in the current piece
@@ -872,7 +788,7 @@ class RopeByteString extends ByteString {
/**
* Internal implementation of read and skip. If b != null, then read the
* next {@code length} bytes into the buffer {@code b} at
- * offset {@code offset}. If b == null, then skip the next {@code length)
+ * offset {@code offset}. If b == null, then skip the next {@code length}
* bytes.
* <p>
* This method assumes that all error checking has already happened.
diff --git a/java/src/main/java/com/google/protobuf/RpcCallback.java b/java/core/src/main/java/com/google/protobuf/RpcCallback.java
index 10752968..10752968 100644
--- a/java/src/main/java/com/google/protobuf/RpcCallback.java
+++ b/java/core/src/main/java/com/google/protobuf/RpcCallback.java
diff --git a/java/src/main/java/com/google/protobuf/RpcChannel.java b/java/core/src/main/java/com/google/protobuf/RpcChannel.java
index f272f4ad..f272f4ad 100644
--- a/java/src/main/java/com/google/protobuf/RpcChannel.java
+++ b/java/core/src/main/java/com/google/protobuf/RpcChannel.java
diff --git a/java/src/main/java/com/google/protobuf/RpcController.java b/java/core/src/main/java/com/google/protobuf/RpcController.java
index a92dd7be..a92dd7be 100644
--- a/java/src/main/java/com/google/protobuf/RpcController.java
+++ b/java/core/src/main/java/com/google/protobuf/RpcController.java
diff --git a/java/src/main/java/com/google/protobuf/RpcUtil.java b/java/core/src/main/java/com/google/protobuf/RpcUtil.java
index 694b8d13..694b8d13 100644
--- a/java/src/main/java/com/google/protobuf/RpcUtil.java
+++ b/java/core/src/main/java/com/google/protobuf/RpcUtil.java
diff --git a/java/src/main/java/com/google/protobuf/Service.java b/java/core/src/main/java/com/google/protobuf/Service.java
index ba7b033e..ba7b033e 100644
--- a/java/src/main/java/com/google/protobuf/Service.java
+++ b/java/core/src/main/java/com/google/protobuf/Service.java
diff --git a/java/src/main/java/com/google/protobuf/ServiceException.java b/java/core/src/main/java/com/google/protobuf/ServiceException.java
index 00d57075..00d57075 100644
--- a/java/src/main/java/com/google/protobuf/ServiceException.java
+++ b/java/core/src/main/java/com/google/protobuf/ServiceException.java
diff --git a/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java
index aba65e32..aba65e32 100644
--- a/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java
diff --git a/java/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java
index 0674d2e2..0674d2e2 100644
--- a/java/src/main/java/com/google/protobuf/SmallSortedMap.java
+++ b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java
diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java
index 44d036c1..c99b5285 100644
--- a/java/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -1074,6 +1074,18 @@ public final class TextFormat {
private ParseException floatParseException(final NumberFormatException e) {
return parseException("Couldn't parse number: " + e.getMessage());
}
+
+ /**
+ * Returns a {@link UnknownFieldParseException} with the line and column
+ * numbers of the previous token in the description, and the unknown field
+ * name, suitable for throwing.
+ */
+ public UnknownFieldParseException unknownFieldParseExceptionPreviousToken(
+ final String unknownField, final String description) {
+ // Note: People generally prefer one-based line and column numbers.
+ return new UnknownFieldParseException(
+ previousLine + 1, previousColumn + 1, unknownField, description);
+ }
}
/** Thrown when parsing an invalid text format message. */
@@ -1121,6 +1133,45 @@ public final class TextFormat {
return column;
}
}
+
+ /**
+ * Thrown when encountering an unknown field while parsing
+ * a text format message.
+ */
+ public static class UnknownFieldParseException extends ParseException {
+ private final String unknownField;
+
+ /**
+ * Create a new instance, with -1 as the line and column numbers, and an
+ * empty unknown field name.
+ */
+ public UnknownFieldParseException(final String message) {
+ this(-1, -1, "", message);
+ }
+
+ /**
+ * Create a new instance
+ *
+ * @param line the line number where the parse error occurred,
+ * using 1-offset.
+ * @param column the column number where the parser error occurred,
+ * using 1-offset.
+ * @param unknownField the name of the unknown field found while parsing.
+ */
+ public UnknownFieldParseException(final int line, final int column,
+ final String unknownField, final String message) {
+ super(line, column, message);
+ this.unknownField = unknownField;
+ }
+
+ /**
+ * Return the name of the unknown field encountered while parsing the
+ * protocol buffer string.
+ */
+ public String getUnknownField() {
+ return unknownField;
+ }
+ }
private static final Parser PARSER = Parser.newBuilder().build();
@@ -1388,7 +1439,8 @@ public final class TextFormat {
if (field == null) {
if (!allowUnknownFields) {
- throw tokenizer.parseExceptionPreviousToken(
+ throw tokenizer.unknownFieldParseExceptionPreviousToken(
+ name,
"Message type \"" + type.getFullName()
+ "\" has no field named \"" + name + "\".");
} else {
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java b/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java
diff --git a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java b/java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java
index 5714c063..5714c063 100644
--- a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java
+++ b/java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
index 7cd2250e..7cd2250e 100644
--- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
index 435ad4d4..435ad4d4 100644
--- a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
diff --git a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
index 5257c5a2..5257c5a2 100644
--- a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
+++ b/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
new file mode 100644
index 00000000..8806395c
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
@@ -0,0 +1,63 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Provides a number of unsafe byte operations to be used by advanced applications with high
+ * performance requirements. These methods are referred to as "unsafe" due to the fact that they
+ * potentially expose the backing buffer of a {@link ByteString} to the application.
+ *
+ * <p><strong>DISCLAIMER:</strong> The methods in this class should only be called if it is
+ * guaranteed that the the buffer backing the {@link ByteString} will never change! Mutation of a
+ * {@link ByteString} can lead to unexpected and undesirable consequences in your application,
+ * and will likely be difficult to debug. Proceed with caution!
+ */
+@ExperimentalApi
+public final class UnsafeByteOperations {
+ private UnsafeByteOperations() {}
+
+ /**
+ * An unsafe operation that returns a {@link ByteString} that is backed by the provided buffer.
+ *
+ * @param buffer the Java NIO buffer to be wrapped.
+ * @return a {@link ByteString} backed by the provided buffer.
+ */
+ public static ByteString unsafeWrap(ByteBuffer buffer) {
+ if (buffer.hasArray()) {
+ final int offset = buffer.arrayOffset();
+ return ByteString.wrap(buffer.array(), offset + buffer.position(), buffer.remaining());
+ } else {
+ return new NioByteString(buffer);
+ }
+ }
+}
diff --git a/java/src/main/java/com/google/protobuf/Utf8.java b/java/core/src/main/java/com/google/protobuf/Utf8.java
index 48c7e9e6..48c7e9e6 100644
--- a/java/src/main/java/com/google/protobuf/Utf8.java
+++ b/java/core/src/main/java/com/google/protobuf/Utf8.java
diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/core/src/main/java/com/google/protobuf/WireFormat.java
index 8dbe1ae3..8dbe1ae3 100644
--- a/java/src/main/java/com/google/protobuf/WireFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/WireFormat.java
diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java
index d964ef59..d964ef59 100644
--- a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java
diff --git a/java/src/test/java/com/google/protobuf/AnyTest.java b/java/core/src/test/java/com/google/protobuf/AnyTest.java
index e169f69d..e169f69d 100644
--- a/java/src/test/java/com/google/protobuf/AnyTest.java
+++ b/java/core/src/test/java/com/google/protobuf/AnyTest.java
diff --git a/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java b/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
index df89c263..b8ad1fe4 100644
--- a/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
@@ -310,10 +310,6 @@ public class BooleanArrayListTest extends TestCase {
}
private void assertImmutable(BooleanArrayList list) {
- if (list.contains(1)) {
- throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
- }
-
try {
list.add(false);
fail();
@@ -413,7 +409,7 @@ public class BooleanArrayListTest extends TestCase {
}
try {
- list.removeAll(Collections.singleton(1));
+ list.removeAll(Collections.singleton(Boolean.TRUE));
fail();
} catch (UnsupportedOperationException e) {
// expected
@@ -434,7 +430,7 @@ public class BooleanArrayListTest extends TestCase {
}
try {
- list.retainAll(Collections.singleton(1));
+ list.retainAll(Collections.singleton(Boolean.TRUE));
fail();
} catch (UnsupportedOperationException e) {
// expected
diff --git a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java
index 447e6ef3..7a8a0a5e 100644
--- a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java
@@ -62,7 +62,7 @@ public class BoundedByteStringTest extends LiteralByteStringTest {
@Override
public void testToString() throws UnsupportedEncodingException {
String testString = "I love unicode \u1234\u5678 characters";
- LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
+ ByteString unicode = ByteString.wrap(testString.getBytes(Internal.UTF_8));
ByteString chopped = unicode.substring(2, unicode.size() - 6);
assertEquals(classUnderTest + ".substring() must have the expected type",
classUnderTest, getActualClassName(chopped));
@@ -73,9 +73,9 @@ public class BoundedByteStringTest extends LiteralByteStringTest {
}
@Override
- public void testCharsetToString() throws UnsupportedEncodingException {
+ public void testCharsetToString() {
String testString = "I love unicode \u1234\u5678 characters";
- LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
+ ByteString unicode = ByteString.wrap(testString.getBytes(Internal.UTF_8));
ByteString chopped = unicode.substring(2, unicode.size() - 6);
assertEquals(classUnderTest + ".substring() must have the expected type",
classUnderTest, getActualClassName(chopped));
diff --git a/java/src/test/java/com/google/protobuf/ByteStringTest.java b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
index 8af5dcde..5267c160 100644
--- a/java/src/test/java/com/google/protobuf/ByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
@@ -39,7 +39,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -129,7 +128,7 @@ public class ByteStringTest extends TestCase {
isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
}
- public void testCopyFrom_StringEncoding() throws UnsupportedEncodingException {
+ public void testCopyFrom_StringEncoding() {
String testString = "I love unicode \u1234\u5678 characters";
ByteString byteString = ByteString.copyFrom(testString, UTF_16);
byte[] testBytes = testString.getBytes(UTF_16);
@@ -137,7 +136,7 @@ public class ByteStringTest extends TestCase {
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
}
- public void testCopyFrom_Utf8() throws UnsupportedEncodingException {
+ public void testCopyFrom_Utf8() {
String testString = "I love unicode \u1234\u5678 characters";
ByteString byteString = ByteString.copyFromUtf8(testString);
byte[] testBytes = testString.getBytes(Internal.UTF_8);
@@ -154,6 +153,7 @@ public class ByteStringTest extends TestCase {
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
// Call copyFrom on an iteration that's not a collection
ByteString byteStringAlt = ByteString.copyFrom(new Iterable<ByteString>() {
+ @Override
public Iterator<ByteString> iterator() {
return pieces.iterator();
}
@@ -399,7 +399,7 @@ public class ByteStringTest extends TestCase {
}
}
- public void testToStringUtf8() throws UnsupportedEncodingException {
+ public void testToStringUtf8() {
String testString = "I love unicode \u1234\u5678 characters";
byte[] testBytes = testString.getBytes(Internal.UTF_8);
ByteString byteString = ByteString.copyFrom(testBytes);
@@ -419,7 +419,7 @@ public class ByteStringTest extends TestCase {
// Test newOutput() using a variety of buffer sizes and a variety of (fixed)
// write sizes
- public void testNewOutput_ArrayWrite() throws IOException {
+ public void testNewOutput_ArrayWrite() {
byte[] bytes = getTestBytes();
int length = bytes.length;
int[] bufferSizes = {128, 256, length / 2, length - 1, length, length + 1,
@@ -442,7 +442,7 @@ public class ByteStringTest extends TestCase {
// Test newOutput() using a variety of buffer sizes, but writing all the
// characters using write(byte);
- public void testNewOutput_WriteChar() throws IOException {
+ public void testNewOutput_WriteChar() {
byte[] bytes = getTestBytes();
int length = bytes.length;
int[] bufferSizes = {0, 1, 128, 256, length / 2,
@@ -461,7 +461,7 @@ public class ByteStringTest extends TestCase {
// Test newOutput() in which we write the bytes using a variety of methods
// and sizes, and in which we repeatedly call toByteString() in the middle.
- public void testNewOutput_Mixed() throws IOException {
+ public void testNewOutput_Mixed() {
Random rng = new Random(1);
byte[] bytes = getTestBytes();
int length = bytes.length;
@@ -494,7 +494,7 @@ public class ByteStringTest extends TestCase {
}
}
- public void testNewOutputEmpty() throws IOException {
+ public void testNewOutputEmpty() {
// Make sure newOutput() correctly builds empty byte strings
ByteString byteString = ByteString.newOutput().toByteString();
assertEquals(ByteString.EMPTY, byteString);
@@ -655,11 +655,11 @@ public class ByteStringTest extends TestCase {
// trees of empty leaves, to make a string that will fail this test.
for (int i = 1; i < duo.size(); ++i) {
assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
- duo.substring(i - 1, i) instanceof LiteralByteString);
+ duo.substring(i - 1, i) instanceof ByteString.LeafByteString);
}
for (int i = 1; i < quintet.size(); ++i) {
assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
- quintet.substring(i - 1, i) instanceof LiteralByteString);
+ quintet.substring(i - 1, i) instanceof ByteString.LeafByteString);
}
}
@@ -724,7 +724,7 @@ public class ByteStringTest extends TestCase {
}
data1[1] = (byte) 11;
// Test LiteralByteString.writeTo(OutputStream,int,int)
- LiteralByteString left = new LiteralByteString(data1);
+ ByteString left = ByteString.wrap(data1);
byte[] result = substringUsingWriteTo(left, 1, 1);
assertEquals(1, result.length);
assertEquals((byte) 11, result[0]);
@@ -733,7 +733,7 @@ public class ByteStringTest extends TestCase {
for (int i = 0; i < data1.length; i++) {
data2[i] = (byte) 2;
}
- LiteralByteString right = new LiteralByteString(data2);
+ ByteString right = ByteString.wrap(data2);
// Concatenate two ByteStrings to create a RopeByteString.
ByteString root = left.concat(right);
// Make sure we are actually testing a RopeByteString with a simple tree
diff --git a/java/src/test/java/com/google/protobuf/CheckUtf8Test.java b/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java
index 3d6381c9..3d6381c9 100644
--- a/java/src/test/java/com/google/protobuf/CheckUtf8Test.java
+++ b/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java
diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
index 18d8142c..18d8142c 100644
--- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+++ b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
diff --git a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
index 360e759e..6018ea55 100644
--- a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
+++ b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
@@ -37,6 +37,7 @@ import protobuf_unittest.UnittestProto.TestSparseEnum;
import junit.framework.TestCase;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -80,8 +81,8 @@ public class CodedOutputStreamTest extends TestCase {
* 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) {
+ // Only test 32-bit write if the value fits into an int.
+ if (value == (int) value) {
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
output.writeRawVarint32((int) value);
@@ -107,8 +108,8 @@ public class CodedOutputStreamTest extends TestCase {
// 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) {
+ // Only test 32-bit write if the value fits into an int.
+ if (value == (int) value) {
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
CodedOutputStream output =
CodedOutputStream.newInstance(rawOutput, blockSize);
@@ -128,6 +129,42 @@ public class CodedOutputStreamTest extends TestCase {
}
}
+ private void assertVarintRoundTrip(long value) throws Exception {
+ {
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ output.writeRawVarint64(value);
+ output.flush();
+ byte[] bytes = rawOutput.toByteArray();
+ assertEquals(bytes.length, CodedOutputStream.computeRawVarint64Size(value));
+ CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes));
+ assertEquals(value, input.readRawVarint64());
+ }
+
+ if (value == (int) value) {
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ output.writeRawVarint32((int) value);
+ output.flush();
+ byte[] bytes = rawOutput.toByteArray();
+ assertEquals(bytes.length, CodedOutputStream.computeRawVarint32Size((int) value));
+ CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes));
+ assertEquals(value, input.readRawVarint32());
+ }
+ }
+
+ /** Checks that invariants are maintained for varint round trip input and output. */
+ public void testVarintRoundTrips() throws Exception {
+ assertVarintRoundTrip(0L);
+ for (int bits = 0; bits < 64; bits++) {
+ long value = 1L << bits;
+ assertVarintRoundTrip(value);
+ assertVarintRoundTrip(value + 1);
+ assertVarintRoundTrip(value - 1);
+ assertVarintRoundTrip(-value);
+ }
+ }
+
/** Tests writeRawVarint32() and writeRawVarint64(). */
public void testWriteVarint() throws Exception {
assertWriteVarint(bytes(0x00), 0);
diff --git a/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java b/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
index e7905f79..e7905f79 100644
--- a/java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
index 30da2487..82ff34af 100644
--- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
@@ -274,6 +274,15 @@ public class DescriptorsTest extends TestCase {
assertFalse(repeatedField.isRequired());
assertTrue(repeatedField.isRepeated());
}
+
+ public void testFieldDescriptorJsonName() throws Exception {
+ FieldDescriptor requiredField = TestRequired.getDescriptor().findFieldByName("a");
+ FieldDescriptor optionalField = TestAllTypes.getDescriptor().findFieldByName("optional_int32");
+ FieldDescriptor repeatedField = TestAllTypes.getDescriptor().findFieldByName("repeated_int32");
+ assertEquals("a", requiredField.getJsonName());
+ assertEquals("optionalInt32", optionalField.getJsonName());
+ assertEquals("repeatedInt32", repeatedField.getJsonName());
+ }
public void testFieldDescriptorDefault() throws Exception {
Descriptor d = TestAllTypes.getDescriptor();
diff --git a/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java b/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java
index e7a73d70..d3deaa07 100644
--- a/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java
@@ -310,7 +310,7 @@ public class DoubleArrayListTest extends TestCase {
}
private void assertImmutable(DoubleArrayList list) {
- if (list.contains(1)) {
+ if (list.contains(1D)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
@@ -413,7 +413,7 @@ public class DoubleArrayListTest extends TestCase {
}
try {
- list.removeAll(Collections.singleton(1));
+ list.removeAll(Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
@@ -434,7 +434,7 @@ public class DoubleArrayListTest extends TestCase {
}
try {
- list.retainAll(Collections.singleton(1));
+ list.retainAll(Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
diff --git a/java/src/test/java/com/google/protobuf/DynamicMessageTest.java b/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java
index 55144e7c..55144e7c 100644
--- a/java/src/test/java/com/google/protobuf/DynamicMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java
diff --git a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
index eaeec0b8..eaeec0b8 100644
--- a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java
+++ b/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
diff --git a/java/src/test/java/com/google/protobuf/FloatArrayListTest.java b/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
index 8f3e93dc..a5e65424 100644
--- a/java/src/test/java/com/google/protobuf/FloatArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
@@ -310,7 +310,7 @@ public class FloatArrayListTest extends TestCase {
}
private void assertImmutable(FloatArrayList list) {
- if (list.contains(1)) {
+ if (list.contains(1F)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
@@ -413,7 +413,7 @@ public class FloatArrayListTest extends TestCase {
}
try {
- list.removeAll(Collections.singleton(1));
+ list.removeAll(Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
@@ -434,7 +434,7 @@ public class FloatArrayListTest extends TestCase {
}
try {
- list.retainAll(Collections.singleton(1));
+ list.retainAll(Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
diff --git a/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java b/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
index a92ba374..a92ba374 100644
--- a/java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
+++ b/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 70812b95..70812b95 100644
--- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
diff --git a/java/src/test/java/com/google/protobuf/IntArrayListTest.java b/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
index 3733eb30..3733eb30 100644
--- a/java/src/test/java/com/google/protobuf/IntArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
diff --git a/java/src/test/java/com/google/protobuf/IsValidUtf8Test.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java
index 8751baae..8751baae 100644
--- a/java/src/test/java/com/google/protobuf/IsValidUtf8Test.java
+++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java
diff --git a/java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
index 321669f3..321669f3 100644
--- a/java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
+++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
diff --git a/java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
index 211b5697..211b5697 100644
--- a/java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
diff --git a/java/src/test/java/com/google/protobuf/LazyFieldTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java
index 2b900065..2b900065 100644
--- a/java/src/test/java/com/google/protobuf/LazyFieldTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java
diff --git a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
index afe0fffd..afe0fffd 100644
--- a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
diff --git a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
index 0f42ac50..0f42ac50 100644
--- a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
diff --git a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java b/java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
index 0ef414aa..0ef414aa 100644
--- a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
diff --git a/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java b/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
index 035917c8..035917c8 100644
--- a/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/core/src/test/java/com/google/protobuf/LiteTest.java
index b1f298ff..b1f298ff 100644
--- a/java/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteTest.java
diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java
index 7dfda2ae..68b55ceb 100644
--- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java
@@ -75,9 +75,7 @@ public class LiteralByteStringTest extends TestCase {
}
protected String getActualClassName(Object object) {
- String actualClassName = object.getClass().getName();
- actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1);
- return actualClassName;
+ return object.getClass().getSimpleName();
}
public void testByteAt() {
@@ -350,14 +348,14 @@ public class LiteralByteStringTest extends TestCase {
public void testToString() throws UnsupportedEncodingException {
String testString = "I love unicode \u1234\u5678 characters";
- LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
+ ByteString unicode = ByteString.wrap(testString.getBytes(Internal.UTF_8));
String roundTripString = unicode.toString(UTF_8);
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
}
- public void testCharsetToString() throws UnsupportedEncodingException {
+ public void testCharsetToString() {
String testString = "I love unicode \u1234\u5678 characters";
- LiteralByteString unicode = new LiteralByteString(testString.getBytes(Internal.UTF_8));
+ ByteString unicode = ByteString.wrap(testString.getBytes(Internal.UTF_8));
String roundTripString = unicode.toString(Internal.UTF_8);
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
}
@@ -365,7 +363,7 @@ public class LiteralByteStringTest extends TestCase {
public void testToString_returnsCanonicalEmptyString() {
assertSame(classUnderTest + " must be the same string references",
ByteString.EMPTY.toString(Internal.UTF_8),
- new LiteralByteString(new byte[]{}).toString(Internal.UTF_8));
+ ByteString.wrap(new byte[]{}).toString(Internal.UTF_8));
}
public void testToString_raisesException() {
@@ -377,7 +375,7 @@ public class LiteralByteStringTest extends TestCase {
}
try {
- new LiteralByteString(referenceBytes).toString("invalid");
+ ByteString.wrap(referenceBytes).toString("invalid");
fail("Should have thrown an exception.");
} catch (UnsupportedEncodingException expected) {
// This is success
@@ -390,15 +388,15 @@ public class LiteralByteStringTest extends TestCase {
assertFalse(classUnderTest + " must not equal the empty string",
stringUnderTest.equals(ByteString.EMPTY));
assertEquals(classUnderTest + " empty strings must be equal",
- new LiteralByteString(new byte[]{}), stringUnderTest.substring(55, 55));
+ ByteString.wrap(new byte[]{}), stringUnderTest.substring(55, 55));
assertEquals(classUnderTest + " must equal another string with the same value",
- stringUnderTest, new LiteralByteString(referenceBytes));
+ stringUnderTest, ByteString.wrap(referenceBytes));
byte[] mungedBytes = new byte[referenceBytes.length];
System.arraycopy(referenceBytes, 0, mungedBytes, 0, referenceBytes.length);
mungedBytes[mungedBytes.length - 5] = (byte) (mungedBytes[mungedBytes.length - 5] ^ 0xFF);
assertFalse(classUnderTest + " must not equal every string with the same length",
- stringUnderTest.equals(new LiteralByteString(mungedBytes)));
+ stringUnderTest.equals(ByteString.wrap(mungedBytes)));
}
public void testHashCode() {
diff --git a/java/src/test/java/com/google/protobuf/LongArrayListTest.java b/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
index 3a52ec7f..1bd094f7 100644
--- a/java/src/test/java/com/google/protobuf/LongArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
@@ -310,7 +310,7 @@ public class LongArrayListTest extends TestCase {
}
private void assertImmutable(LongArrayList list) {
- if (list.contains(1)) {
+ if (list.contains(1L)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
@@ -413,7 +413,7 @@ public class LongArrayListTest extends TestCase {
}
try {
- list.removeAll(Collections.singleton(1));
+ list.removeAll(Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
@@ -434,7 +434,7 @@ public class LongArrayListTest extends TestCase {
}
try {
- list.retainAll(Collections.singleton(1));
+ list.retainAll(Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
index 3d8c9bc4..3d8c9bc4 100644
--- a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
index 1fa3cbdb..1fa3cbdb 100644
--- a/java/src/test/java/com/google/protobuf/MapForProto2Test.java
+++ b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/core/src/test/java/com/google/protobuf/MapTest.java
index 0e5c1284..0e5c1284 100644
--- a/java/src/test/java/com/google/protobuf/MapTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MapTest.java
diff --git a/java/src/test/java/com/google/protobuf/MessageTest.java b/java/core/src/test/java/com/google/protobuf/MessageTest.java
index abcd3a1d..abcd3a1d 100644
--- a/java/src/test/java/com/google/protobuf/MessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MessageTest.java
diff --git a/java/src/test/java/com/google/protobuf/NestedBuildersTest.java b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
index 23653126..23653126 100644
--- a/java/src/test/java/com/google/protobuf/NestedBuildersTest.java
+++ b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
diff --git a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java
new file mode 100644
index 00000000..e40a3662
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java
@@ -0,0 +1,545 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static com.google.protobuf.Internal.UTF_8;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Tests for {@link NioByteString}.
+ */
+public class NioByteStringTest extends TestCase {
+ private static final ByteString EMPTY = new NioByteString(ByteBuffer.wrap(new byte[0]));
+ private static final String CLASSNAME = NioByteString.class.getSimpleName();
+ private static final byte[] BYTES = ByteStringTest.getTestBytes(1234, 11337766L);
+ private static final int EXPECTED_HASH = ByteString.wrap(BYTES).hashCode();
+ private static final ByteBuffer BUFFER = ByteBuffer.wrap(BYTES.clone());
+ private static final ByteString TEST_STRING = new NioByteString(BUFFER);
+
+ public void testExpectedType() {
+ String actualClassName = getActualClassName(TEST_STRING);
+ assertEquals(CLASSNAME + " should match type exactly", CLASSNAME, actualClassName);
+ }
+
+ protected String getActualClassName(Object object) {
+ String actualClassName = object.getClass().getName();
+ actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1);
+ return actualClassName;
+ }
+
+ public void testByteAt() {
+ boolean stillEqual = true;
+ for (int i = 0; stillEqual && i < BYTES.length; ++i) {
+ stillEqual = (BYTES[i] == TEST_STRING.byteAt(i));
+ }
+ assertTrue(CLASSNAME + " must capture the right bytes", stillEqual);
+ }
+
+ public void testByteIterator() {
+ boolean stillEqual = true;
+ ByteString.ByteIterator iter = TEST_STRING.iterator();
+ for (int i = 0; stillEqual && i < BYTES.length; ++i) {
+ stillEqual = (iter.hasNext() && BYTES[i] == iter.nextByte());
+ }
+ assertTrue(CLASSNAME + " must capture the right bytes", stillEqual);
+ assertFalse(CLASSNAME + " must have exhausted the itertor", iter.hasNext());
+
+ try {
+ iter.nextByte();
+ fail("Should have thrown an exception.");
+ } catch (NoSuchElementException e) {
+ // This is success
+ }
+ }
+
+ public void testByteIterable() {
+ boolean stillEqual = true;
+ int j = 0;
+ for (byte quantum : TEST_STRING) {
+ stillEqual = (BYTES[j] == quantum);
+ ++j;
+ }
+ assertTrue(CLASSNAME + " must capture the right bytes as Bytes", stillEqual);
+ assertEquals(CLASSNAME + " iterable character count", BYTES.length, j);
+ }
+
+ public void testSize() {
+ assertEquals(CLASSNAME + " must have the expected size", BYTES.length,
+ TEST_STRING.size());
+ }
+
+ public void testGetTreeDepth() {
+ assertEquals(CLASSNAME + " must have depth 0", 0, TEST_STRING.getTreeDepth());
+ }
+
+ public void testIsBalanced() {
+ assertTrue(CLASSNAME + " is technically balanced", TEST_STRING.isBalanced());
+ }
+
+ public void testCopyTo_ByteArrayOffsetLength() {
+ int destinationOffset = 50;
+ int length = 100;
+ byte[] destination = new byte[destinationOffset + length];
+ int sourceOffset = 213;
+ TEST_STRING.copyTo(destination, sourceOffset, destinationOffset, length);
+ boolean stillEqual = true;
+ for (int i = 0; stillEqual && i < length; ++i) {
+ stillEqual = BYTES[i + sourceOffset] == destination[i + destinationOffset];
+ }
+ assertTrue(CLASSNAME + ".copyTo(4 arg) must give the expected bytes", stillEqual);
+ }
+
+ public void testCopyTo_ByteArrayOffsetLengthErrors() {
+ int destinationOffset = 50;
+ int length = 100;
+ byte[] destination = new byte[destinationOffset + length];
+
+ try {
+ // Copy one too many bytes
+ TEST_STRING.copyTo(destination, TEST_STRING.size() + 1 - length,
+ destinationOffset, length);
+ fail("Should have thrown an exception when copying too many bytes of a "
+ + CLASSNAME);
+ } catch (IndexOutOfBoundsException expected) {
+ // This is success
+ }
+
+ try {
+ // Copy with illegal negative sourceOffset
+ TEST_STRING.copyTo(destination, -1, destinationOffset, length);
+ fail("Should have thrown an exception when given a negative sourceOffset in "
+ + CLASSNAME);
+ } catch (IndexOutOfBoundsException expected) {
+ // This is success
+ }
+
+ try {
+ // Copy with illegal negative destinationOffset
+ TEST_STRING.copyTo(destination, 0, -1, length);
+ fail("Should have thrown an exception when given a negative destinationOffset in "
+ + CLASSNAME);
+ } catch (IndexOutOfBoundsException expected) {
+ // This is success
+ }
+
+ try {
+ // Copy with illegal negative size
+ TEST_STRING.copyTo(destination, 0, 0, -1);
+ fail("Should have thrown an exception when given a negative size in "
+ + CLASSNAME);
+ } catch (IndexOutOfBoundsException expected) {
+ // This is success
+ }
+
+ try {
+ // Copy with illegal too-large sourceOffset
+ TEST_STRING.copyTo(destination, 2 * TEST_STRING.size(), 0, length);
+ fail("Should have thrown an exception when the destinationOffset is too large in "
+ + CLASSNAME);
+ } catch (IndexOutOfBoundsException expected) {
+ // This is success
+ }
+
+ try {
+ // Copy with illegal too-large destinationOffset
+ TEST_STRING.copyTo(destination, 0, 2 * destination.length, length);
+ fail("Should have thrown an exception when the destinationOffset is too large in "
+ + CLASSNAME);
+ } catch (IndexOutOfBoundsException expected) {
+ // This is success
+ }
+ }
+
+ public void testCopyTo_ByteBuffer() {
+ // Same length.
+ ByteBuffer myBuffer = ByteBuffer.allocate(BYTES.length);
+ TEST_STRING.copyTo(myBuffer);
+ myBuffer.flip();
+ assertEquals(CLASSNAME + ".copyTo(ByteBuffer) must give back the same bytes",
+ BUFFER, myBuffer);
+
+ // Target buffer bigger than required.
+ myBuffer = ByteBuffer.allocate(TEST_STRING.size() + 1);
+ TEST_STRING.copyTo(myBuffer);
+ myBuffer.flip();
+ assertEquals(BUFFER, myBuffer);
+
+ // Target buffer has no space.
+ myBuffer = ByteBuffer.allocate(0);
+ try {
+ TEST_STRING.copyTo(myBuffer);
+ fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
+ } catch (BufferOverflowException e) {
+ // Expected.
+ }
+
+ // Target buffer too small.
+ myBuffer = ByteBuffer.allocate(1);
+ try {
+ TEST_STRING.copyTo(myBuffer);
+ fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
+ } catch (BufferOverflowException e) {
+ // Expected.
+ }
+ }
+
+ public void testMarkSupported() {
+ InputStream stream = TEST_STRING.newInput();
+ assertTrue(CLASSNAME + ".newInput() must support marking", stream.markSupported());
+ }
+
+ public void testMarkAndReset() throws IOException {
+ int fraction = TEST_STRING.size() / 3;
+
+ InputStream stream = TEST_STRING.newInput();
+ stream.mark(TEST_STRING.size()); // First, mark() the end.
+
+ skipFully(stream, fraction); // Skip a large fraction, but not all.
+ assertEquals(
+ CLASSNAME + ": after skipping to the 'middle', half the bytes are available",
+ (TEST_STRING.size() - fraction), stream.available());
+ stream.reset();
+ assertEquals(
+ CLASSNAME + ": after resetting, all bytes are available",
+ TEST_STRING.size(), stream.available());
+
+ skipFully(stream, TEST_STRING.size()); // Skip to the end.
+ assertEquals(
+ CLASSNAME + ": after skipping to the end, no more bytes are available",
+ 0, stream.available());
+ }
+
+ /**
+ * Discards {@code n} bytes of data from the input stream. This method
+ * will block until the full amount has been skipped. Does not close the
+ * stream.
+ * <p>Copied from com.google.common.io.ByteStreams to avoid adding dependency.
+ *
+ * @param in the input stream to read from
+ * @param n the number of bytes to skip
+ * @throws EOFException if this stream reaches the end before skipping all
+ * the bytes
+ * @throws IOException if an I/O error occurs, or the stream does not
+ * support skipping
+ */
+ static void skipFully(InputStream in, long n) throws IOException {
+ long toSkip = n;
+ while (n > 0) {
+ long amt = in.skip(n);
+ if (amt == 0) {
+ // Force a blocking read to avoid infinite loop
+ if (in.read() == -1) {
+ long skipped = toSkip - n;
+ throw new EOFException("reached end of stream after skipping "
+ + skipped + " bytes; " + toSkip + " bytes expected");
+ }
+ n--;
+ } else {
+ n -= amt;
+ }
+ }
+ }
+
+ public void testAsReadOnlyByteBuffer() {
+ ByteBuffer byteBuffer = TEST_STRING.asReadOnlyByteBuffer();
+ byte[] roundTripBytes = new byte[BYTES.length];
+ assertTrue(byteBuffer.remaining() == BYTES.length);
+ assertTrue(byteBuffer.isReadOnly());
+ byteBuffer.get(roundTripBytes);
+ assertTrue(CLASSNAME + ".asReadOnlyByteBuffer() must give back the same bytes",
+ Arrays.equals(BYTES, roundTripBytes));
+ }
+
+ public void testAsReadOnlyByteBufferList() {
+ List<ByteBuffer> byteBuffers = TEST_STRING.asReadOnlyByteBufferList();
+ int bytesSeen = 0;
+ byte[] roundTripBytes = new byte[BYTES.length];
+ for (ByteBuffer byteBuffer : byteBuffers) {
+ int thisLength = byteBuffer.remaining();
+ assertTrue(byteBuffer.isReadOnly());
+ assertTrue(bytesSeen + thisLength <= BYTES.length);
+ byteBuffer.get(roundTripBytes, bytesSeen, thisLength);
+ bytesSeen += thisLength;
+ }
+ assertTrue(bytesSeen == BYTES.length);
+ assertTrue(CLASSNAME + ".asReadOnlyByteBufferTest() must give back the same bytes",
+ Arrays.equals(BYTES, roundTripBytes));
+ }
+
+ public void testToByteArray() {
+ byte[] roundTripBytes = TEST_STRING.toByteArray();
+ assertTrue(CLASSNAME + ".toByteArray() must give back the same bytes",
+ Arrays.equals(BYTES, roundTripBytes));
+ }
+
+ public void testWriteTo() throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ TEST_STRING.writeTo(bos);
+ byte[] roundTripBytes = bos.toByteArray();
+ assertTrue(CLASSNAME + ".writeTo() must give back the same bytes",
+ Arrays.equals(BYTES, roundTripBytes));
+ }
+
+ public void testNewOutput() throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ByteString.Output output = ByteString.newOutput();
+ TEST_STRING.writeTo(output);
+ assertEquals("Output Size returns correct result",
+ output.size(), TEST_STRING.size());
+ output.writeTo(bos);
+ assertTrue("Output.writeTo() must give back the same bytes",
+ Arrays.equals(BYTES, bos.toByteArray()));
+
+ // write the output stream to itself! This should cause it to double
+ output.writeTo(output);
+ assertEquals("Writing an output stream to itself is successful",
+ TEST_STRING.concat(TEST_STRING), output.toByteString());
+
+ output.reset();
+ assertEquals("Output.reset() resets the output", 0, output.size());
+ assertEquals("Output.reset() resets the output",
+ EMPTY, output.toByteString());
+ }
+
+ public void testToString() {
+ String testString = "I love unicode \u1234\u5678 characters";
+ ByteString unicode = forString(testString);
+ String roundTripString = unicode.toString(UTF_8);
+ assertEquals(CLASSNAME + " unicode must match", testString, roundTripString);
+ }
+
+ public void testCharsetToString() {
+ String testString = "I love unicode \u1234\u5678 characters";
+ ByteString unicode = forString(testString);
+ String roundTripString = unicode.toString(UTF_8);
+ assertEquals(CLASSNAME + " unicode must match", testString, roundTripString);
+ }
+
+ public void testToString_returnsCanonicalEmptyString() {
+ assertSame(CLASSNAME + " must be the same string references",
+ EMPTY.toString(UTF_8),
+ new NioByteString(ByteBuffer.wrap(new byte[0])).toString(UTF_8));
+ }
+
+ public void testToString_raisesException() {
+ try {
+ EMPTY.toString("invalid");
+ fail("Should have thrown an exception.");
+ } catch (UnsupportedEncodingException expected) {
+ // This is success
+ }
+
+ try {
+ TEST_STRING.toString("invalid");
+ fail("Should have thrown an exception.");
+ } catch (UnsupportedEncodingException expected) {
+ // This is success
+ }
+ }
+
+ public void testEquals() {
+ assertEquals(CLASSNAME + " must not equal null", false, TEST_STRING.equals(null));
+ assertEquals(CLASSNAME + " must equal self", TEST_STRING, TEST_STRING);
+ assertFalse(CLASSNAME + " must not equal the empty string",
+ TEST_STRING.equals(EMPTY));
+ assertEquals(CLASSNAME + " empty strings must be equal",
+ EMPTY, TEST_STRING.substring(55, 55));
+ assertEquals(CLASSNAME + " must equal another string with the same value",
+ TEST_STRING, new NioByteString(BUFFER));
+
+ byte[] mungedBytes = mungedBytes();
+ assertFalse(CLASSNAME + " must not equal every string with the same length",
+ TEST_STRING.equals(new NioByteString(ByteBuffer.wrap(mungedBytes))));
+ }
+
+ public void testEqualsLiteralByteString() {
+ ByteString literal = ByteString.copyFrom(BYTES);
+ assertEquals(CLASSNAME + " must equal LiteralByteString with same value", literal,
+ TEST_STRING);
+ assertEquals(CLASSNAME + " must equal LiteralByteString with same value", TEST_STRING,
+ literal);
+ assertFalse(CLASSNAME + " must not equal the empty string",
+ TEST_STRING.equals(ByteString.EMPTY));
+ assertEquals(CLASSNAME + " empty strings must be equal",
+ ByteString.EMPTY, TEST_STRING.substring(55, 55));
+
+ literal = ByteString.copyFrom(mungedBytes());
+ assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
+ TEST_STRING.equals(literal));
+ assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
+ literal.equals(TEST_STRING));
+ }
+
+ public void testEqualsRopeByteString() {
+ ByteString p1 = ByteString.copyFrom(BYTES, 0, 5);
+ ByteString p2 = ByteString.copyFrom(BYTES, 5, BYTES.length - 5);
+ ByteString rope = p1.concat(p2);
+
+ assertEquals(CLASSNAME + " must equal RopeByteString with same value", rope,
+ TEST_STRING);
+ assertEquals(CLASSNAME + " must equal RopeByteString with same value", TEST_STRING,
+ rope);
+ assertFalse(CLASSNAME + " must not equal the empty string",
+ TEST_STRING.equals(ByteString.EMPTY.concat(ByteString.EMPTY)));
+ assertEquals(CLASSNAME + " empty strings must be equal",
+ ByteString.EMPTY.concat(ByteString.EMPTY), TEST_STRING.substring(55, 55));
+
+ byte[] mungedBytes = mungedBytes();
+ p1 = ByteString.copyFrom(mungedBytes, 0, 5);
+ p2 = ByteString.copyFrom(mungedBytes, 5, mungedBytes.length - 5);
+ rope = p1.concat(p2);
+ assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
+ TEST_STRING.equals(rope));
+ assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
+ rope.equals(TEST_STRING));
+ }
+
+ private byte[] mungedBytes() {
+ byte[] mungedBytes = new byte[BYTES.length];
+ System.arraycopy(BYTES, 0, mungedBytes, 0, BYTES.length);
+ mungedBytes[mungedBytes.length - 5] = (byte) (mungedBytes[mungedBytes.length - 5] ^ 0xFF);
+ return mungedBytes;
+ }
+
+ public void testHashCode() {
+ int hash = TEST_STRING.hashCode();
+ assertEquals(CLASSNAME + " must have expected hashCode", EXPECTED_HASH, hash);
+ }
+
+ public void testPeekCachedHashCode() {
+ ByteString newString = new NioByteString(BUFFER);
+ assertEquals(CLASSNAME + ".peekCachedHashCode() should return zero at first", 0,
+ newString.peekCachedHashCode());
+ newString.hashCode();
+ assertEquals(CLASSNAME + ".peekCachedHashCode should return zero at first",
+ EXPECTED_HASH, newString.peekCachedHashCode());
+ }
+
+ public void testPartialHash() {
+ // partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
+ // This test would fail if the expected hash were 1. It's not.
+ int hash = TEST_STRING.partialHash(TEST_STRING.size(), 0, TEST_STRING.size());
+ assertEquals(CLASSNAME + ".partialHash() must yield expected hashCode",
+ EXPECTED_HASH, hash);
+ }
+
+ public void testNewInput() throws IOException {
+ InputStream input = TEST_STRING.newInput();
+ assertEquals("InputStream.available() returns correct value",
+ TEST_STRING.size(), input.available());
+ boolean stillEqual = true;
+ for (byte referenceByte : BYTES) {
+ int expectedInt = (referenceByte & 0xFF);
+ stillEqual = (expectedInt == input.read());
+ }
+ assertEquals("InputStream.available() returns correct value",
+ 0, input.available());
+ assertTrue(CLASSNAME + " must give the same bytes from the InputStream", stillEqual);
+ assertEquals(CLASSNAME + " InputStream must now be exhausted", -1, input.read());
+ }
+
+ public void testNewInput_skip() throws IOException {
+ InputStream input = TEST_STRING.newInput();
+ int stringSize = TEST_STRING.size();
+ int nearEndIndex = stringSize * 2 / 3;
+ long skipped1 = input.skip(nearEndIndex);
+ assertEquals("InputStream.skip()", skipped1, nearEndIndex);
+ assertEquals("InputStream.available()",
+ stringSize - skipped1, input.available());
+ assertTrue("InputStream.mark() is available", input.markSupported());
+ input.mark(0);
+ assertEquals("InputStream.skip(), read()",
+ TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read());
+ assertEquals("InputStream.available()",
+ stringSize - skipped1 - 1, input.available());
+ long skipped2 = input.skip(stringSize);
+ assertEquals("InputStream.skip() incomplete",
+ skipped2, stringSize - skipped1 - 1);
+ assertEquals("InputStream.skip(), no more input", 0, input.available());
+ assertEquals("InputStream.skip(), no more input", -1, input.read());
+ input.reset();
+ assertEquals("InputStream.reset() succeded",
+ stringSize - skipped1, input.available());
+ assertEquals("InputStream.reset(), read()",
+ TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read());
+ }
+
+ public void testNewCodedInput() throws IOException {
+ CodedInputStream cis = TEST_STRING.newCodedInput();
+ byte[] roundTripBytes = cis.readRawBytes(BYTES.length);
+ assertTrue(CLASSNAME + " must give the same bytes back from the CodedInputStream",
+ Arrays.equals(BYTES, roundTripBytes));
+ assertTrue(CLASSNAME + " CodedInputStream must now be exhausted", cis.isAtEnd());
+ }
+
+ /**
+ * Make sure we keep things simple when concatenating with empty. See also
+ * {@link ByteStringTest#testConcat_empty()}.
+ */
+ public void testConcat_empty() {
+ assertSame(CLASSNAME + " concatenated with empty must give " + CLASSNAME,
+ TEST_STRING.concat(EMPTY), TEST_STRING);
+ assertSame("empty concatenated with " + CLASSNAME + " must give " + CLASSNAME,
+ EMPTY.concat(TEST_STRING), TEST_STRING);
+ }
+
+ public void testJavaSerialization() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(out);
+ oos.writeObject(TEST_STRING);
+ oos.close();
+ byte[] pickled = out.toByteArray();
+ InputStream in = new ByteArrayInputStream(pickled);
+ ObjectInputStream ois = new ObjectInputStream(in);
+ Object o = ois.readObject();
+ assertTrue("Didn't get a ByteString back", o instanceof ByteString);
+ assertEquals("Should get an equal ByteString back", TEST_STRING, o);
+ }
+
+ private static ByteString forString(String str) {
+ return new NioByteString(ByteBuffer.wrap(str.getBytes(UTF_8)));
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java b/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java
new file mode 100644
index 00000000..37fa242d
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java
@@ -0,0 +1,211 @@
+package com.google.protobuf;
+
+import com.google.protobuf.DescriptorProtos.DescriptorProto;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser}
+ * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want
+ * to distinguish between invalid protos vs. actual I/O errors (like failures reading from a
+ * socket, etc.). So, when we're not using the parser directly, an {@link IOException} should be
+ * thrown where appropriate, instead of always an {@link InvalidProtocolBufferException}.
+ *
+ * @author jh@squareup.com (Joshua Humphries)
+ */
+public class ParseExceptionsTest {
+
+ private interface ParseTester {
+ DescriptorProto parse(InputStream in) throws IOException;
+ }
+
+ private byte serializedProto[];
+
+ private void setup() {
+ serializedProto = DescriptorProto.getDescriptor().toProto().toByteArray();
+ }
+
+ private void setupDelimited() {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ DescriptorProto.getDescriptor().toProto().writeDelimitedTo(bos);
+ } catch (IOException e) {
+ fail("Exception not expected: " + e);
+ }
+ serializedProto = bos.toByteArray();
+ }
+
+ @Test public void message_parseFrom_InputStream() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(in);
+ }
+ });
+ }
+
+ @Test public void message_parseFrom_InputStreamAndExtensionRegistry() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
+ }
+ });
+ }
+
+ @Test public void message_parseFrom_CodedInputStream() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
+ }
+ });
+ }
+
+ @Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(CodedInputStream.newInstance(in),
+ ExtensionRegistry.newInstance());
+ }
+ });
+ }
+
+ @Test public void message_parseDelimitedFrom_InputStream() {
+ setupDelimited();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseDelimitedFrom(in);
+ }
+ });
+ }
+
+ @Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
+ setupDelimited();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
+ }
+ });
+ }
+
+ @Test public void messageBuilder_mergeFrom_InputStream() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder().mergeFrom(in).build();
+ }
+ });
+ }
+
+ @Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder().mergeFrom(in, ExtensionRegistry.newInstance()).build();
+ }
+ });
+ }
+
+ @Test public void messageBuilder_mergeFrom_CodedInputStream() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
+ }
+ });
+ }
+
+ @Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() {
+ setup();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder()
+ .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()).build();
+ }
+ });
+ }
+
+ @Test public void messageBuilder_mergeDelimitedFrom_InputStream() {
+ setupDelimited();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ DescriptorProto.Builder builder = DescriptorProto.newBuilder();
+ builder.mergeDelimitedFrom(in);
+ return builder.build();
+ }
+ });
+ }
+
+ @Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() {
+ setupDelimited();
+ verifyExceptions(new ParseTester() {
+ public DescriptorProto parse(InputStream in) throws IOException {
+ DescriptorProto.Builder builder = DescriptorProto.newBuilder();
+ builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
+ return builder.build();
+ }
+ });
+ }
+
+ private void verifyExceptions(ParseTester parseTester) {
+ // No exception
+ try {
+ assertEquals(DescriptorProto.getDescriptor().toProto(),
+ parseTester.parse(new ByteArrayInputStream(serializedProto)));
+ } catch (IOException e) {
+ fail("No exception expected: " + e);
+ }
+
+ // IOException
+ try {
+ // using a "broken" stream that will throw part-way through reading the message
+ parseTester.parse(broken(new ByteArrayInputStream(serializedProto)));
+ fail("IOException expected but not thrown");
+ } catch (IOException e) {
+ assertFalse(e instanceof InvalidProtocolBufferException);
+ }
+
+ // InvalidProtocolBufferException
+ try {
+ // make the serialized proto invalid
+ for (int i = 0; i < 50; i++) {
+ serializedProto[i] = -1;
+ }
+ parseTester.parse(new ByteArrayInputStream(serializedProto));
+ fail("InvalidProtocolBufferException expected but not thrown");
+ } catch (IOException e) {
+ assertTrue(e instanceof InvalidProtocolBufferException);
+ }
+ }
+
+ private InputStream broken(InputStream i) {
+ return new FilterInputStream(i) {
+ int count = 0;
+
+ @Override public int read() throws IOException {
+ if (count++ >= 50) {
+ throw new IOException("I'm broken!");
+ }
+ return super.read();
+ }
+
+ @Override public int read(byte b[], int off, int len) throws IOException {
+ if ((count += len) >= 50) {
+ throw new IOException("I'm broken!");
+ }
+ return super.read(b, off, len);
+ }
+ };
+ }
+}
diff --git a/java/src/test/java/com/google/protobuf/ParserTest.java b/java/core/src/test/java/com/google/protobuf/ParserTest.java
index 5a92bacf..5a92bacf 100644
--- a/java/src/test/java/com/google/protobuf/ParserTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ParserTest.java
diff --git a/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java b/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
index f9f5e9c7..245c3dee 100644
--- a/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
@@ -243,7 +243,7 @@ public class ProtobufArrayListTest extends TestCase {
}
try {
- list.removeAll(Collections.<Double>emptyList());
+ list.removeAll(Collections.emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
@@ -264,7 +264,7 @@ public class ProtobufArrayListTest extends TestCase {
}
try {
- list.retainAll(Collections.<Double>emptyList());
+ list.retainAll(Collections.emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
diff --git a/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java b/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java
index 49d52321..49d52321 100644
--- a/java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java
+++ b/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java
diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java b/java/core/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java
index cc385599..dc56f2e9 100644
--- a/java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java
@@ -96,7 +96,7 @@ public class RopeByteStringSubstringTest extends LiteralByteStringTest {
}
@Override
- public void testCharsetToString() throws UnsupportedEncodingException {
+ public void testCharsetToString() {
String sourceString = "I love unicode \u1234\u5678 characters";
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
int copies = 250;
diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java b/java/core/src/test/java/com/google/protobuf/RopeByteStringTest.java
index 4ec3a409..4ec3a409 100644
--- a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/RopeByteStringTest.java
diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/core/src/test/java/com/google/protobuf/ServiceTest.java
index ff980d66..ff980d66 100644
--- a/java/src/test/java/com/google/protobuf/ServiceTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ServiceTest.java
diff --git a/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java b/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java
index 58b80007..58b80007 100644
--- a/java/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java
+++ b/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java
diff --git a/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java
index 366086d3..366086d3 100644
--- a/java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
+++ b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java
diff --git a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java b/java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java
index 2c60fe0e..2c60fe0e 100644
--- a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java
+++ b/java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java
diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/core/src/test/java/com/google/protobuf/TestUtil.java
index 792e8665..01acb884 100644
--- a/java/src/test/java/com/google/protobuf/TestUtil.java
+++ b/java/core/src/test/java/com/google/protobuf/TestUtil.java
@@ -300,6 +300,16 @@ public final class TestUtil {
}
/**
+ * Get a {@code TestAllTypesLite.Builder} with all fields set as they would be by
+ * {@link #setAllFields(TestAllTypesLite.Builder)}.
+ */
+ public static TestAllTypesLite.Builder getAllLiteSetBuilder() {
+ TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder();
+ setAllFields(builder);
+ return builder;
+ }
+
+ /**
* Get a {@code TestAllExtensions} with all fields set as they would be by
* {@link #setAllExtensions(TestAllExtensions.Builder)}.
*/
@@ -339,6 +349,150 @@ public final class TestUtil {
setPackedExtensions(builder);
return builder.build();
}
+
+ /**
+ * Set every field of {@code builder} to the values expected by
+ * {@code assertAllFieldsSet()}.
+ */
+ public static void setAllFields(TestAllTypesLite.Builder builder) {
+ builder.setOptionalInt32 (101);
+ builder.setOptionalInt64 (102);
+ builder.setOptionalUint32 (103);
+ builder.setOptionalUint64 (104);
+ builder.setOptionalSint32 (105);
+ builder.setOptionalSint64 (106);
+ builder.setOptionalFixed32 (107);
+ builder.setOptionalFixed64 (108);
+ builder.setOptionalSfixed32(109);
+ builder.setOptionalSfixed64(110);
+ builder.setOptionalFloat (111);
+ builder.setOptionalDouble (112);
+ builder.setOptionalBool (true);
+ builder.setOptionalString ("115");
+ builder.setOptionalBytes (toBytes("116"));
+
+ builder.setOptionalGroup(
+ TestAllTypesLite.OptionalGroup.newBuilder().setA(117).build());
+ builder.setOptionalNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build());
+ builder.setOptionalForeignMessage(
+ ForeignMessageLite.newBuilder().setC(119).build());
+ builder.setOptionalImportMessage(
+ ImportMessageLite.newBuilder().setD(120).build());
+ builder.setOptionalPublicImportMessage(
+ PublicImportMessageLite.newBuilder().setE(126).build());
+ builder.setOptionalLazyMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build());
+
+ builder.setOptionalNestedEnum (TestAllTypesLite.NestedEnum.BAZ);
+ builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ);
+ builder.setOptionalImportEnum (ImportEnumLite.IMPORT_LITE_BAZ);
+
+ builder.setOptionalStringPiece("124");
+ builder.setOptionalCord("125");
+
+ // -----------------------------------------------------------------
+
+ builder.addRepeatedInt32 (201);
+ builder.addRepeatedInt64 (202);
+ builder.addRepeatedUint32 (203);
+ builder.addRepeatedUint64 (204);
+ builder.addRepeatedSint32 (205);
+ builder.addRepeatedSint64 (206);
+ builder.addRepeatedFixed32 (207);
+ builder.addRepeatedFixed64 (208);
+ builder.addRepeatedSfixed32(209);
+ builder.addRepeatedSfixed64(210);
+ builder.addRepeatedFloat (211);
+ builder.addRepeatedDouble (212);
+ builder.addRepeatedBool (true);
+ builder.addRepeatedString ("215");
+ builder.addRepeatedBytes (toBytes("216"));
+
+ builder.addRepeatedGroup(
+ TestAllTypesLite.RepeatedGroup.newBuilder().setA(217).build());
+ builder.addRepeatedNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build());
+ builder.addRepeatedForeignMessage(
+ ForeignMessageLite.newBuilder().setC(219).build());
+ builder.addRepeatedImportMessage(
+ ImportMessageLite.newBuilder().setD(220).build());
+ builder.addRepeatedLazyMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build());
+
+ builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAR);
+ builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
+ builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAR);
+
+ builder.addRepeatedStringPiece("224");
+ builder.addRepeatedCord("225");
+
+ // Add a second one of each field.
+ builder.addRepeatedInt32 (301);
+ builder.addRepeatedInt64 (302);
+ builder.addRepeatedUint32 (303);
+ builder.addRepeatedUint64 (304);
+ builder.addRepeatedSint32 (305);
+ builder.addRepeatedSint64 (306);
+ builder.addRepeatedFixed32 (307);
+ builder.addRepeatedFixed64 (308);
+ builder.addRepeatedSfixed32(309);
+ builder.addRepeatedSfixed64(310);
+ builder.addRepeatedFloat (311);
+ builder.addRepeatedDouble (312);
+ builder.addRepeatedBool (false);
+ builder.addRepeatedString ("315");
+ builder.addRepeatedBytes (toBytes("316"));
+
+ builder.addRepeatedGroup(
+ TestAllTypesLite.RepeatedGroup.newBuilder().setA(317).build());
+ builder.addRepeatedNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build());
+ builder.addRepeatedForeignMessage(
+ ForeignMessageLite.newBuilder().setC(319).build());
+ builder.addRepeatedImportMessage(
+ ImportMessageLite.newBuilder().setD(320).build());
+ builder.addRepeatedLazyMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build());
+
+ builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAZ);
+ builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ);
+ builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAZ);
+
+ builder.addRepeatedStringPiece("324");
+ builder.addRepeatedCord("325");
+
+ // -----------------------------------------------------------------
+
+ builder.setDefaultInt32 (401);
+ builder.setDefaultInt64 (402);
+ builder.setDefaultUint32 (403);
+ builder.setDefaultUint64 (404);
+ builder.setDefaultSint32 (405);
+ builder.setDefaultSint64 (406);
+ builder.setDefaultFixed32 (407);
+ builder.setDefaultFixed64 (408);
+ builder.setDefaultSfixed32(409);
+ builder.setDefaultSfixed64(410);
+ builder.setDefaultFloat (411);
+ builder.setDefaultDouble (412);
+ builder.setDefaultBool (false);
+ builder.setDefaultString ("415");
+ builder.setDefaultBytes (toBytes("416"));
+
+ builder.setDefaultNestedEnum (TestAllTypesLite.NestedEnum.FOO);
+ builder.setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_FOO);
+ builder.setDefaultImportEnum (ImportEnumLite.IMPORT_LITE_FOO);
+
+ builder.setDefaultStringPiece("424");
+ builder.setDefaultCord("425");
+
+ builder.setOneofUint32(601);
+ builder.setOneofNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build());
+ builder.setOneofString("603");
+ builder.setOneofBytes(toBytes("604"));
+ }
/**
* Set every field of {@code message} to the values expected by
diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
index 1df4fad7..1df4fad7 100644
--- a/java/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
diff --git a/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java b/java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
index 8f45976f..8f45976f 100644
--- a/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
index dc987379..dc987379 100644
--- a/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
index 8c9dcafe..8c9dcafe 100644
--- a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
diff --git a/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java b/java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
index b1c75fc3..b1c75fc3 100644
--- a/java/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
diff --git a/java/src/test/java/com/google/protobuf/WellKnownTypesTest.java b/java/core/src/test/java/com/google/protobuf/WellKnownTypesTest.java
index 982e200f..982e200f 100644
--- a/java/src/test/java/com/google/protobuf/WellKnownTypesTest.java
+++ b/java/core/src/test/java/com/google/protobuf/WellKnownTypesTest.java
diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java
index 0175005d..0175005d 100644
--- a/java/src/test/java/com/google/protobuf/WireFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java
diff --git a/java/src/test/java/com/google/protobuf/any_test.proto b/java/core/src/test/proto/com/google/protobuf/any_test.proto
index 80173d8a..80173d8a 100644
--- a/java/src/test/java/com/google/protobuf/any_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/any_test.proto
diff --git a/java/src/test/java/com/google/protobuf/field_presence_test.proto b/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto
index 8f3ca8c6..8f3ca8c6 100644
--- a/java/src/test/java/com/google/protobuf/field_presence_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto
diff --git a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto b/java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto
index 5580f72d..5580f72d 100644
--- a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto
+++ b/java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto
diff --git a/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto
index 86837250..86837250 100644
--- a/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto
+++ b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto
diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto
index d5418f28..d5418f28 100644
--- a/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto
diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto
index a9be5166..a9be5166 100644
--- a/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto
diff --git a/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto b/java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto
index b02ac599..b02ac599 100644
--- a/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto
diff --git a/java/src/test/java/com/google/protobuf/map_test.proto b/java/core/src/test/proto/com/google/protobuf/map_test.proto
index 2f7709be..2280ac03 100644
--- a/java/src/test/java/com/google/protobuf/map_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_test.proto
@@ -36,7 +36,6 @@ option java_package = "map_test";
option java_outer_classname = "MapTestProto";
option java_generate_equals_and_hash = true;
-
message TestMap {
message MessageValue {
int32 value = 1;
diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto
index 92790506..92790506 100644
--- a/java/src/test/java/com/google/protobuf/multiple_files_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto
diff --git a/java/src/test/java/com/google/protobuf/nested_builders_test.proto b/java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto
index a5dd66d8..a5dd66d8 100644
--- a/java/src/test/java/com/google/protobuf/nested_builders_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto
diff --git a/java/src/test/java/com/google/protobuf/nested_extension.proto b/java/core/src/test/proto/com/google/protobuf/nested_extension.proto
index 704e649e..704e649e 100644
--- a/java/src/test/java/com/google/protobuf/nested_extension.proto
+++ b/java/core/src/test/proto/com/google/protobuf/nested_extension.proto
diff --git a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto b/java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto
index a95c38b2..a95c38b2 100644
--- a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto
+++ b/java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto
diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension.proto b/java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto
index 31fac552..31fac552 100644
--- a/java/src/test/java/com/google/protobuf/non_nested_extension.proto
+++ b/java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto
diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto b/java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto
index 37c369ed..37c369ed 100644
--- a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto
+++ b/java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto
diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test.proto b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto
index 42083681..42083681 100644
--- a/java/src/test/java/com/google/protobuf/outer_class_name_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto
diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto
index 3e5956b0..3e5956b0 100644
--- a/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto
+++ b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto
diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto
index 74a8ba3c..74a8ba3c 100644
--- a/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto
+++ b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto
diff --git a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto b/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto
index 8c37c03c..2b1f65e4 100644
--- a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto
@@ -45,7 +45,6 @@ option java_package = "com.google.protobuf";
option java_outer_classname = "TestBadIdentifiersProto";
option java_generate_equals_and_hash = true;
-
message TestMessage {
optional string cached_size = 1;
optional string serialized_size = 2;
diff --git a/java/src/test/java/com/google/protobuf/test_check_utf8.proto b/java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto
index 119c1dcb..119c1dcb 100644
--- a/java/src/test/java/com/google/protobuf/test_check_utf8.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto
diff --git a/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto b/java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto
index f06d76d6..f06d76d6 100644
--- a/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto
diff --git a/java/src/test/java/com/google/protobuf/test_custom_options.proto b/java/core/src/test/proto/com/google/protobuf/test_custom_options.proto
index f8efd455..f8efd455 100644
--- a/java/src/test/java/com/google/protobuf/test_custom_options.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_custom_options.proto
diff --git a/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto b/java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto
index 645f57b4..645f57b4 100644
--- a/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto
diff --git a/java/lite/pom.xml b/java/lite/pom.xml
new file mode 100644
index 00000000..70c7d047
--- /dev/null
+++ b/java/lite/pom.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-parent</artifactId>
+ <version>3.0.0-beta-2</version>
+ </parent>
+
+ <artifactId>protobuf-lite</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Protocol Buffers [Lite]</name>
+ <description>A trimmed-down version of the Protocol Buffers library.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <core.root>../core</core.root>
+ <test.proto.dir>${core.root}/src/test/proto</test.proto.dir>
+ </properties>
+
+ <build>
+ <sourceDirectory>${core.root}/src/main/java</sourceDirectory>
+ <testSourceDirectory>${core.root}/src/test/java</testSourceDirectory>
+
+ <plugins>
+ <!-- Use Antrun plugin to generate sources with protoc -->
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <!-- Generate core protos -->
+ <execution>
+ <id>generate-sources</id>
+ <phase>generate-sources</phase>
+ <configuration>
+ <target>
+ <ant antfile="${core.root}/generate-sources-build.xml"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <!-- Generate the test protos -->
+ <execution>
+ <id>generate-test-sources</id>
+ <phase>generate-test-sources</phase>
+ <configuration>
+ <target>
+ <ant antfile="${core.root}/generate-test-sources-build.xml"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- Only compile a subset of the files -->
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <generatedSourcesDirectory>${generated.sources.dir}</generatedSourcesDirectory>
+ <generatedTestSourcesDirectory>${generated.testsources.dir}</generatedTestSourcesDirectory>
+ <includes>
+ <include>**/AbstractMessageLite.java</include>
+ <include>**/AbstractParser.java</include>
+ <include>**/AbstractProtobufList.java</include>
+ <include>**/BooleanArrayList.java</include>
+ <include>**/ByteString.java</include>
+ <include>**/CodedInputStream.java</include>
+ <include>**/CodedOutputStream.java</include>
+ <include>**/DoubleArrayList.java</include>
+ <include>**/ExtensionLite.java</include>
+ <include>**/ExtensionRegistryLite.java</include>
+ <include>**/FieldSet.java</include>
+ <include>**/FloatArrayList.java</include>
+ <include>**/GeneratedMessageLite.java</include>
+ <include>**/IntArrayList.java</include>
+ <include>**/Internal.java</include>
+ <include>**/InvalidProtocolBufferException.java</include>
+ <include>**/LazyFieldLite.java</include>
+ <include>**/LazyStringArrayList.java</include>
+ <include>**/LazyStringList.java</include>
+ <include>**/LongArrayList.java</include>
+ <include>**/MapEntryLite.java</include>
+ <include>**/MapFieldLite.java</include>
+ <include>**/MessageLite.java</include>
+ <include>**/MessageLiteOrBuilder.java</include>
+ <include>**/MessageLiteToString.java</include>
+ <include>**/MutabilityOracle.java</include>
+ <include>**/NioByteString.java</include>
+ <include>**/Parser.java</include>
+ <include>**/ProtobufArrayList.java</include>
+ <include>**/ProtocolStringList.java</include>
+ <include>**/RopeByteString.java</include>
+ <include>**/SmallSortedMap.java</include>
+ <include>**/TextFormatEscaper.java</include>
+ <include>**/UninitializedMessageException.java</include>
+ <include>**/UnknownFieldSetLite.java</include>
+ <include>**/UnmodifiableLazyStringList.java</include>
+ <include>**/UnsafeByteOperations.java</include>
+ <include>**/Utf8.java</include>
+ <include>**/WireFormat.java</include>
+ </includes>
+ <testIncludes>
+ <testInclude>**/*Lite.java</testInclude>
+ <testInclude>**/BooleanArrayListTest.java</testInclude>
+ <testInclude>**/DoubleArrayListTest.java</testInclude>
+ <testInclude>**/FloatArrayListTest.java</testInclude>
+ <testInclude>**/IntArrayListTest.java</testInclude>
+ <testInclude>**/LazyMessageLiteTest.java</testInclude>
+ <testInclude>**/LiteTest.java</testInclude>
+ <testInclude>**/LongArrayListTest.java</testInclude>
+ <testInclude>**/NioByteStringTest.java</testInclude>
+ <testInclude>**/ProtobufArrayListTest.java</testInclude>
+ <testInclude>**/UnknownFieldSetLiteTest.java</testInclude>
+ </testIncludes>
+ </configuration>
+ </plugin>
+
+ <!-- OSGI bundle configuration -->
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
+ <Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
+ <Export-Package>com.google.${project.artifactId};version=${project.version}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/java/pom.xml b/java/pom.xml
index 7b99c035..d5719edf 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -8,17 +8,33 @@
<artifactId>google</artifactId>
<version>1</version>
</parent>
+
<groupId>com.google.protobuf</groupId>
- <artifactId>protobuf-java</artifactId>
- <version>3.0.0-beta-1</version>
- <packaging>bundle</packaging>
- <name>Protocol Buffer Java API</name>
+ <artifactId>protobuf-parent</artifactId>
+ <version>3.0.0-beta-2</version>
+ <packaging>pom</packaging>
+
+ <name>Protocol Buffers [Parent]</name>
+ <inceptionYear>2008</inceptionYear>
+ <url>https://developers.google.com/protocol-buffers/</url>
<description>
Protocol Buffers are a way of encoding structured data in an efficient yet
extensible format.
</description>
- <inceptionYear>2008</inceptionYear>
- <url>https://developers.google.com/protocol-buffers/</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+ <!-- These are relative to the submodules -->
+ <protobuf.basedir>${project.basedir}/../..</protobuf.basedir>
+ <protobuf.source.dir>${protobuf.basedir}/src</protobuf.source.dir>
+ <protoc>${protobuf.source.dir}/protoc</protoc>
+ <test.proto.dir>src/test/proto</test.proto.dir>
+ <generated.sources.dir>${project.build.directory}/generated-sources</generated.sources.dir>
+ <generated.testsources.dir>${project.build.directory}/generated-test-sources</generated.testsources.dir>
+ </properties>
+
<licenses>
<license>
<name>New BSD license</name>
@@ -26,217 +42,116 @@
<distribution>repo</distribution>
</license>
</licenses>
+
<scm>
<url>https://github.com/google/protobuf</url>
- <connection>
- scm:git:https://github.com/google/protobuf.git
- </connection>
+ <connection>scm:git:https://github.com/google/protobuf.git</connection>
</scm>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.4</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.easymock</groupId>
- <artifactId>easymock</artifactId>
- <version>2.2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.easymock</groupId>
- <artifactId>easymockclassextension</artifactId>
- <version>2.2.1</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
+
+ <distributionManagement>
+ <snapshotRepository>
+ <id>sonatype-nexus-staging</id>
+ <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+ </snapshotRepository>
+ <repository>
+ <id>sonatype-nexus-staging</id>
+ <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+ </repository>
+ </distributionManagement>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>2.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <version>2.2.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>18.0</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
<build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.5</source>
- <target>1.5</target>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <includes>
- <include>**/*Test.java</include>
- </includes>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-antrun-plugin</artifactId>
- <executions>
- <execution>
- <id>generate-sources</id>
- <phase>generate-sources</phase>
- <configuration>
- <tasks>
- <mkdir dir="target/generated-sources" />
- <exec executable="../src/protoc">
- <arg value="--java_out=target/generated-sources" />
- <arg value="--proto_path=../src" />
- <arg value="../src/google/protobuf/any.proto" />
- <arg value="../src/google/protobuf/api.proto" />
- <arg value="../src/google/protobuf/descriptor.proto" />
- <arg value="../src/google/protobuf/duration.proto" />
- <arg value="../src/google/protobuf/empty.proto" />
- <arg value="../src/google/protobuf/field_mask.proto" />
- <arg value="../src/google/protobuf/source_context.proto" />
- <arg value="../src/google/protobuf/struct.proto" />
- <arg value="../src/google/protobuf/timestamp.proto" />
- <arg value="../src/google/protobuf/type.proto" />
- <arg value="../src/google/protobuf/wrappers.proto" />
- <arg value="../src/google/protobuf/compiler/plugin.proto" />
- </exec>
- </tasks>
- <sourceRoot>target/generated-sources</sourceRoot>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- <execution>
- <id>generate-test-sources</id>
- <phase>generate-test-sources</phase>
- <configuration>
- <tasks>
- <mkdir dir="target/generated-test-sources" />
- <exec executable="../src/protoc">
- <arg value="--java_out=target/generated-test-sources" />
- <arg value="--proto_path=../src" />
- <arg value="--proto_path=src/test/java" />
- <arg value="../src/google/protobuf/unittest.proto" />
- <arg value="../src/google/protobuf/unittest_import.proto" />
- <arg value="../src/google/protobuf/unittest_import_public.proto" />
- <arg value="../src/google/protobuf/unittest_mset.proto" />
- <arg value="../src/google/protobuf/unittest_mset_wire_format.proto" />
- <arg value="src/test/java/com/google/protobuf/lazy_fields_lite.proto" />
- <arg value="src/test/java/com/google/protobuf/lite_equals_and_hash.proto" />
- <arg
- value="src/test/java/com/google/protobuf/multiple_files_test.proto" />
- <arg value="src/test/java/com/google/protobuf/nested_builders_test.proto" />
- <arg value="src/test/java/com/google/protobuf/nested_extension.proto" />
- <arg value="src/test/java/com/google/protobuf/nested_extension_lite.proto" />
- <arg value="src/test/java/com/google/protobuf/non_nested_extension.proto" />
- <arg value="src/test/java/com/google/protobuf/non_nested_extension_lite.proto" />
- <arg value="src/test/java/com/google/protobuf/outer_class_name_test.proto" />
- <arg value="src/test/java/com/google/protobuf/outer_class_name_test2.proto" />
- <arg value="src/test/java/com/google/protobuf/outer_class_name_test3.proto" />
- <arg value="src/test/java/com/google/protobuf/test_bad_identifiers.proto" />
- <arg value="src/test/java/com/google/protobuf/test_check_utf8.proto" />
- <arg value="src/test/java/com/google/protobuf/test_check_utf8_size.proto" />
- <arg value="src/test/java/com/google/protobuf/test_custom_options.proto" />
- <arg
- value="../src/google/protobuf/unittest_optimize_for.proto" />
- <arg
- value="../src/google/protobuf/unittest_custom_options.proto" />
- <arg value="../src/google/protobuf/unittest_lite.proto" />
- <arg value="../src/google/protobuf/unittest_import_lite.proto" />
- <arg value="../src/google/protobuf/unittest_import_public_lite.proto" />
- <arg value="../src/google/protobuf/unittest_lite_imports_nonlite.proto" />
- <arg value="../src/google/protobuf/unittest_enormous_descriptor.proto" />
- <arg value="../src/google/protobuf/unittest_no_generic_services.proto" />
- <arg value="../src/google/protobuf/unittest_well_known_types.proto" />
- <arg value="src/test/java/com/google/protobuf/any_test.proto" />
- <arg value="src/test/java/com/google/protobuf/field_presence_test.proto" />
- <arg value="src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto" />
- <arg value="src/test/java/com/google/protobuf/map_for_proto2_test.proto" />
- <arg value="src/test/java/com/google/protobuf/map_test.proto" />
- <arg value="src/test/java/com/google/protobuf/map_initialization_order_test.proto" />
- </exec>
- </tasks>
- <testSourceRoot>target/generated-test-sources</testSourceRoot>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
- <Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
- <Export-Package>com.google.protobuf;version=3.0.0-beta-1</Export-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- <resources>
- <resource>
- <directory>../src</directory>
- <includes>
- <include>google/protobuf/any.proto</include>
- <include>google/protobuf/api.proto</include>
- <include>google/protobuf/descriptor.proto</include>
- <include>google/protobuf/duration.proto</include>
- <include>google/protobuf/empty.proto</include>
- <include>google/protobuf/field_mask.proto</include>
- <include>google/protobuf/source_context.proto</include>
- <include>google/protobuf/struct.proto</include>
- <include>google/protobuf/timestamp.proto</include>
- <include>google/protobuf/type.proto</include>
- <include>google/protobuf/wrappers.proto</include>
- <include>google/protobuf/compiler/plugin.proto</include>
- </includes>
- </resource>
- </resources>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.3</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.4</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.10.3</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.6</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.10</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>3.0.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.8</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
</build>
+
<profiles>
<profile>
<id>release</id>
- <distributionManagement>
- <snapshotRepository>
- <id>sonatype-nexus-staging</id>
- <url>https://oss.sonatype.org/content/repositories/snapshots</url>
- </snapshotRepository>
- <repository>
- <id>sonatype-nexus-staging</id>
- <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
- </repository>
- </distributionManagement>
<build>
<plugins>
<plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- <version>2.2.1</version>
- <executions>
- <execution>
- <id>attach-sources</id>
- <goals>
- <goal>jar-no-fork</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>2.9.1</version>
- <executions>
- <execution>
- <id>attach-javadocs</id>
- <goals>
- <goal>jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
- <version>1.5</version>
+ <version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
@@ -250,93 +165,23 @@
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
- <version>1.6.3</version>
+ <version>1.6.6</version>
<extensions>true</extensions>
<configuration>
- <serverId>sonatype-nexus-staging</serverId>
- <nexusUrl>https://oss.sonatype.org/</nexusUrl>
- <autoReleaseAfterClose>false</autoReleaseAfterClose>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- <profile>
- <id>lite</id>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <includes>
- <include>**/AbstractMessageLite.java</include>
- <include>**/AbstractParser.java</include>
- <include>**/AbstractProtobufList.java</include>
- <include>**/BoundedByteString.java</include>
- <include>**/BooleanArrayList.java</include>
- <include>**/ByteString.java</include>
- <include>**/CodedInputStream.java</include>
- <include>**/CodedOutputStream.java</include>
- <include>**/DoublerrayList.java</include>
- <include>**/ExtensionLite.java</include>
- <include>**/ExtensionRegistryLite.java</include>
- <include>**/FieldSet.java</include>
- <include>**/FloatArrayList.java</include>
- <include>**/GeneratedMessageLite.java</include>
- <include>**/IntArrayList.java</include>
- <include>**/Internal.java</include>
- <include>**/InvalidProtocolBufferException.java</include>
- <include>**/LazyFieldLite.java</include>
- <include>**/LazyStringArrayList.java</include>
- <include>**/LazyStringList.java</include>
- <include>**/LiteralByteString.java</include>
- <include>**/LongArrayList.java</include>
- <include>**/MapEntryLite.java</include>
- <include>**/MapFieldLite.java</include>
- <include>**/MessageLite.java</include>
- <include>**/MessageLiteOrBuilder.java</include>
- <include>**/MutabilityOracle.java</include>
- <include>**/Parser.java</include>
- <include>**/ProtobufArrayList.java</include>
- <include>**/ProtocolStringList.java</include>
- <include>**/RopeByteString.java</include>
- <include>**/SmallSortedMap.java</include>
- <include>**/UninitializedMessageException.java</include>
- <include>**/UnknownFieldSetLite.java</include>
- <include>**/UnmodifiableLazyStringList.java</include>
- <include>**/Utf8.java</include>
- <include>**/WireFormat.java</include>
- </includes>
- <testIncludes>
- <testInclude>**/*Lite.java</testInclude>
- <testInclude>**/BooleanArrayListTest.java</testInclude>
- <testInclude>**/DoubleArrayListTest.java</testInclude>
- <testInclude>**/FloatArrayListTest.java</testInclude>
- <testInclude>**/IntArrayListTest.java</testInclude>
- <testInclude>**/LazyMessageLiteTest.java</testInclude>
- <testInclude>**/LiteTest.java</testInclude>
- <testInclude>**/LongArrayListTest.java</testInclude>
- <testInclude>**/ProtobufArrayListTest.java</testInclude>
- <testInclude>**/UnknownFieldSetLiteTest.java</testInclude>
- </testIncludes>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <includes>
- <include>**/*Test.java</include>
- </includes>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <classifier>lite</classifier>
+ <serverId>sonatype-nexus-staging</serverId>
+ <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+ <autoReleaseAfterClose>false</autoReleaseAfterClose>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
+
+ <modules>
+ <module>core</module>
+ <module>lite</module>
+ <module>util</module>
+ </modules>
+
</project>
diff --git a/java/src/main/java/com/google/protobuf/BoundedByteString.java b/java/src/main/java/com/google/protobuf/BoundedByteString.java
deleted file mode 100644
index b4c3fb1b..00000000
--- a/java/src/main/java/com/google/protobuf/BoundedByteString.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package com.google.protobuf;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.io.ObjectInputStream;
-import java.util.NoSuchElementException;
-
-/**
- * This class is used to represent the substring of a {@link ByteString} over a
- * single byte array. In terms of the public API of {@link ByteString}, you end
- * up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link
- * ByteString#substring(int, int)}.
- *
- * <p>This class contains most of the overhead involved in creating a substring
- * from a {@link LiteralByteString}. The overhead involves some range-checking
- * and two extra fields.
- *
- * @author carlanton@google.com (Carl Haverl)
- */
-class BoundedByteString extends LiteralByteString {
-
- private final int bytesOffset;
- private final int bytesLength;
-
- /**
- * Creates a {@code BoundedByteString} backed by the sub-range of given array,
- * without copying.
- *
- * @param bytes array to wrap
- * @param offset index to first byte to use in bytes
- * @param length number of bytes to use from bytes
- * @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0},
- * or if {@code offset + length >
- * bytes.length}.
- */
- BoundedByteString(byte[] bytes, int offset, int length) {
- super(bytes);
- if (offset < 0) {
- throw new IllegalArgumentException("Offset too small: " + offset);
- }
- if (length < 0) {
- throw new IllegalArgumentException("Length too small: " + offset);
- }
- if ((long) offset + length > bytes.length) {
- throw new IllegalArgumentException(
- "Offset+Length too large: " + offset + "+" + length);
- }
-
- this.bytesOffset = offset;
- this.bytesLength = length;
- }
-
- /**
- * Gets the byte at the given index.
- * Throws {@link ArrayIndexOutOfBoundsException}
- * for backwards-compatibility reasons although it would more properly be
- * {@link IndexOutOfBoundsException}.
- *
- * @param index index of byte
- * @return the value
- * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
- */
- @Override
- public byte byteAt(int index) {
- // We must check the index ourselves as we cannot rely on Java array index
- // checking for substrings.
- if (index < 0) {
- throw new ArrayIndexOutOfBoundsException("Index too small: " + index);
- }
- if (index >= size()) {
- throw new ArrayIndexOutOfBoundsException(
- "Index too large: " + index + ", " + size());
- }
-
- return bytes[bytesOffset + index];
- }
-
- @Override
- public int size() {
- return bytesLength;
- }
-
- @Override
- protected int getOffsetIntoBytes() {
- return bytesOffset;
- }
-
- // =================================================================
- // ByteString -> byte[]
-
- @Override
- protected void copyToInternal(byte[] target, int sourceOffset,
- int targetOffset, int numberToCopy) {
- System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target,
- targetOffset, numberToCopy);
- }
-
- // =================================================================
- // Serializable
-
- private static final long serialVersionUID = 1L;
-
- Object writeReplace() {
- return new LiteralByteString(toByteArray());
- }
-
- private void readObject(ObjectInputStream in) throws IOException {
- throw new InvalidObjectException(
- "BoundedByteStream instances are not to be serialized directly");
- }
-
- // =================================================================
- // ByteIterator
-
- @Override
- public ByteIterator iterator() {
- return new BoundedByteIterator();
- }
-
- private class BoundedByteIterator implements ByteIterator {
-
- private int position;
- private final int limit;
-
- private BoundedByteIterator() {
- position = getOffsetIntoBytes();
- limit = position + size();
- }
-
- public boolean hasNext() {
- return (position < limit);
- }
-
- public Byte next() {
- // Boxing calls Byte.valueOf(byte), which does not instantiate.
- return nextByte();
- }
-
- public byte nextByte() {
- if (position >= limit) {
- throw new NoSuchElementException();
- }
- return bytes[position++];
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/java/src/main/java/com/google/protobuf/LiteralByteString.java b/java/src/main/java/com/google/protobuf/LiteralByteString.java
deleted file mode 100644
index c5a8512a..00000000
--- a/java/src/main/java/com/google/protobuf/LiteralByteString.java
+++ /dev/null
@@ -1,372 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package com.google.protobuf;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-/**
- * This class implements a {@link com.google.protobuf.ByteString} backed by a
- * single array of bytes, contiguous in memory. It supports substring by
- * pointing to only a sub-range of the underlying byte array, meaning that a
- * substring will reference the full byte-array of the string it's made from,
- * exactly as with {@link String}.
- *
- * @author carlanton@google.com (Carl Haverl)
- */
-class LiteralByteString extends ByteString {
-
- private static final long serialVersionUID = 1L;
-
- protected final byte[] bytes;
-
- /**
- * Creates a {@code LiteralByteString} backed by the given array, without
- * copying.
- *
- * @param bytes array to wrap
- */
- LiteralByteString(byte[] bytes) {
- this.bytes = bytes;
- }
-
- @Override
- public byte byteAt(int index) {
- // Unlike most methods in this class, this one is a direct implementation
- // ignoring the potential offset because we need to do range-checking in the
- // substring case anyway.
- return bytes[index];
- }
-
- @Override
- public int size() {
- return bytes.length;
- }
-
- // =================================================================
- // ByteString -> substring
-
- @Override
- public ByteString substring(int beginIndex, int endIndex) {
- if (beginIndex < 0) {
- throw new IndexOutOfBoundsException(
- "Beginning index: " + beginIndex + " < 0");
- }
- if (endIndex > size()) {
- throw new IndexOutOfBoundsException("End index: " + endIndex + " > " +
- size());
- }
- int substringLength = endIndex - beginIndex;
- if (substringLength < 0) {
- throw new IndexOutOfBoundsException(
- "Beginning index larger than ending index: " + beginIndex + ", "
- + endIndex);
- }
-
- ByteString result;
- if (substringLength == 0) {
- result = ByteString.EMPTY;
- } else {
- result = new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex,
- substringLength);
- }
- return result;
- }
-
- // =================================================================
- // ByteString -> byte[]
-
- @Override
- protected void copyToInternal(byte[] target, int sourceOffset,
- int targetOffset, int numberToCopy) {
- // Optimized form, not for subclasses, since we don't call
- // getOffsetIntoBytes() or check the 'numberToCopy' parameter.
- System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy);
- }
-
- @Override
- public void copyTo(ByteBuffer target) {
- target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes
- }
-
- @Override
- public ByteBuffer asReadOnlyByteBuffer() {
- ByteBuffer byteBuffer =
- ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size());
- return byteBuffer.asReadOnlyBuffer();
- }
-
- @Override
- public List<ByteBuffer> asReadOnlyByteBufferList() {
- // Return the ByteBuffer generated by asReadOnlyByteBuffer() as a singleton
- List<ByteBuffer> result = new ArrayList<ByteBuffer>(1);
- result.add(asReadOnlyByteBuffer());
- return result;
- }
-
- @Override
- public void writeTo(OutputStream outputStream) throws IOException {
- outputStream.write(toByteArray());
- }
-
- @Override
- void writeToInternal(OutputStream outputStream, int sourceOffset,
- int numberToWrite) throws IOException {
- outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset,
- numberToWrite);
- }
-
- @Override
- protected String toStringInternal(Charset charset) {
- return new String(bytes, getOffsetIntoBytes(), size(), charset);
- }
-
- // =================================================================
- // UTF-8 decoding
-
- @Override
- public boolean isValidUtf8() {
- int offset = getOffsetIntoBytes();
- return Utf8.isValidUtf8(bytes, offset, offset + size());
- }
-
- @Override
- protected int partialIsValidUtf8(int state, int offset, int length) {
- int index = getOffsetIntoBytes() + offset;
- return Utf8.partialIsValidUtf8(state, bytes, index, index + length);
- }
-
- // =================================================================
- // equals() and hashCode()
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
- if (!(other instanceof ByteString)) {
- return false;
- }
-
- if (size() != ((ByteString) other).size()) {
- return false;
- }
- if (size() == 0) {
- return true;
- }
-
- if (other instanceof LiteralByteString) {
- LiteralByteString otherAsLiteral = (LiteralByteString) other;
- // If we know the hash codes and they are not equal, we know the byte
- // strings are not equal.
- if (hash != 0
- && otherAsLiteral.hash != 0
- && hash != otherAsLiteral.hash) {
- return false;
- }
-
- return equalsRange((LiteralByteString) other, 0, size());
- } else if (other instanceof RopeByteString) {
- return other.equals(this);
- } else {
- throw new IllegalArgumentException(
- "Has a new type of ByteString been created? Found "
- + other.getClass());
- }
- }
-
- /**
- * Check equality of the substring of given length of this object starting at
- * zero with another {@code LiteralByteString} substring starting at offset.
- *
- * @param other what to compare a substring in
- * @param offset offset into other
- * @param length number of bytes to compare
- * @return true for equality of substrings, else false.
- */
- boolean equalsRange(LiteralByteString other, int offset, int length) {
- if (length > other.size()) {
- throw new IllegalArgumentException(
- "Length too large: " + length + size());
- }
- if (offset + length > other.size()) {
- throw new IllegalArgumentException(
- "Ran off end of other: " + offset + ", " + length + ", " +
- other.size());
- }
-
- byte[] thisBytes = bytes;
- byte[] otherBytes = other.bytes;
- int thisLimit = getOffsetIntoBytes() + length;
- for (int thisIndex = getOffsetIntoBytes(), otherIndex =
- other.getOffsetIntoBytes() + offset;
- (thisIndex < thisLimit); ++thisIndex, ++otherIndex) {
- if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Cached hash value. Intentionally accessed via a data race, which
- * is safe because of the Java Memory Model's "no out-of-thin-air values"
- * guarantees for ints.
- */
- private int hash = 0;
-
- /**
- * Compute the hashCode using the traditional algorithm from {@link
- * ByteString}.
- *
- * @return hashCode value
- */
- @Override
- public int hashCode() {
- int h = hash;
-
- if (h == 0) {
- int size = size();
- h = partialHash(size, 0, size);
- if (h == 0) {
- h = 1;
- }
- hash = h;
- }
- return h;
- }
-
- @Override
- protected int peekCachedHashCode() {
- return hash;
- }
-
- @Override
- protected int partialHash(int h, int offset, int length) {
- return hashCode(h, bytes, getOffsetIntoBytes() + offset, length);
- }
-
- static int hashCode(int h, byte[] bytes, int offset, int length) {
- for (int i = offset; i < offset + length; i++) {
- h = h * 31 + bytes[i];
- }
- return h;
- }
-
- static int hashCode(byte[] bytes) {
- int h = hashCode(bytes.length, bytes, 0, bytes.length);
- return h == 0 ? 1 : h;
- }
-
- // =================================================================
- // Input stream
-
- @Override
- public InputStream newInput() {
- return new ByteArrayInputStream(bytes, getOffsetIntoBytes(),
- size()); // No copy
- }
-
- @Override
- public CodedInputStream newCodedInput() {
- // We trust CodedInputStream not to modify the bytes, or to give anyone
- // else access to them.
- return CodedInputStream.newInstance(this);
- }
-
- // =================================================================
- // ByteIterator
-
- @Override
- public ByteIterator iterator() {
- return new LiteralByteIterator();
- }
-
- private class LiteralByteIterator implements ByteIterator {
- private int position;
- private final int limit;
-
- private LiteralByteIterator() {
- position = 0;
- limit = size();
- }
-
- public boolean hasNext() {
- return (position < limit);
- }
-
- public Byte next() {
- // Boxing calls Byte.valueOf(byte), which does not instantiate.
- return nextByte();
- }
-
- public byte nextByte() {
- try {
- return bytes[position++];
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new NoSuchElementException(e.getMessage());
- }
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- // =================================================================
- // Internal methods
-
- @Override
- protected int getTreeDepth() {
- return 0;
- }
-
- @Override
- protected boolean isBalanced() {
- return true;
- }
-
- /**
- * Offset into {@code bytes[]} to use, non-zero for substrings.
- *
- * @return always 0 for this class
- */
- protected int getOffsetIntoBytes() {
- return 0;
- }
-}
diff --git a/java/util/pom.xml b/java/util/pom.xml
index 44a5662d..26c12c82 100644
--- a/java/util/pom.xml
+++ b/java/util/pom.xml
@@ -4,110 +4,73 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
- <groupId>com.google</groupId>
- <artifactId>google</artifactId>
- <version>1</version>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-parent</artifactId>
+ <version>3.0.0-beta-2</version>
</parent>
- <groupId>com.google.protobuf</groupId>
+
<artifactId>protobuf-java-util</artifactId>
- <version>3.0.0-beta-1</version>
<packaging>bundle</packaging>
- <name>Protocol Buffer Java API</name>
- <description>
- Protocol Buffers are a way of encoding structured data in an efficient yet
- extensible format.
- </description>
- <inceptionYear>2008</inceptionYear>
- <url>https://developers.google.com/protocol-buffers/</url>
- <licenses>
- <license>
- <name>New BSD license</name>
- <url>http://www.opensource.org/licenses/bsd-license.php</url>
- <distribution>repo</distribution>
- </license>
- </licenses>
- <scm>
- <url>https://github.com/google/protobuf</url>
- <connection>
- scm:git:https://github.com/google/protobuf.git
- </connection>
- </scm>
+
+ <name>Protocol Buffers [Util]</name>
+ <description>Utilities for Protocol Buffers</description>
+
<dependencies>
<dependency>
- <groupId>com.google.protobuf</groupId>
+ <groupId>${project.groupId}</groupId>
<artifactId>protobuf-java</artifactId>
- <version>3.0.0-beta-1</version>
- <scope>compile</scope>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>18.0</version>
- <scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3</version>
- <scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.4</version>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
- <version>2.2</version>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
- <version>2.2.1</version>
- <scope>test</scope>
</dependency>
</dependencies>
+
+ <properties>
+ <!-- Use the core proto dir so that we can call the core generation script -->
+ <test.proto.dir>../core/src/test/proto</test.proto.dir>
+ </properties>
+
<build>
<plugins>
<plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.5</source>
- <target>1.5</target>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <includes>
- <include>**/*Test.java</include>
- <include>../src/main/java/com/google/protobuf/TestUtil.java</include>
- </includes>
- </configuration>
- </plugin>
- <plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
+ <!-- Generate the test protos -->
<execution>
<id>generate-test-sources</id>
<phase>generate-test-sources</phase>
<configuration>
- <tasks>
- <mkdir dir="target/generated-test-sources" />
- <exec executable="../../src/protoc">
- <arg value="--java_out=target/generated-test-sources" />
- <arg value="--proto_path=../../src" />
- <arg value="--proto_path=src/test/java" />
- <arg value="../../src/google/protobuf/unittest.proto" />
- <arg value="../../src/google/protobuf/unittest_import.proto" />
- <arg value="../../src/google/protobuf/unittest_import_public.proto" />
- <arg value="src/test/java/com/google/protobuf/util/json_test.proto" />
+ <target>
+ <!-- Generate all of the test protos from the core module -->
+ <ant antfile="../core/generate-test-sources-build.xml"/>
+
+ <!-- Generate additional test protos for this module -->
+ <exec executable="${protoc}">
+ <arg value="--java_out=${generated.testsources.dir}" />
+ <arg value="--proto_path=${protobuf.source.dir}" />
+ <arg value="--proto_path=src/test/proto" />
+ <arg value="src/test/proto/com/google/protobuf/util/json_test.proto" />
</exec>
- </tasks>
- <testSourceRoot>target/generated-test-sources</testSourceRoot>
+ </target>
</configuration>
<goals>
<goal>run</goal>
@@ -115,6 +78,16 @@
</execution>
</executions>
</plugin>
+
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <!-- Add the generated test sources to the build -->
+ <generatedTestSourcesDirectory>${generated.testsources.dir}</generatedTestSourcesDirectory>
+ </configuration>
+ </plugin>
+
+ <!-- Configure the OSGI bundle -->
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
@@ -123,80 +96,20 @@
<instructions>
<Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
<Bundle-SymbolicName>com.google.protobuf.util</Bundle-SymbolicName>
- <Export-Package>com.google.protobuf.util;version=3.0.0-beta-1</Export-Package>
+ <Export-Package>com.google.protobuf.util;version=${project.version}</Export-Package>
</instructions>
</configuration>
</plugin>
+
+ <!-- Configure the fat jar to include all dependencies -->
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </plugin>
</plugins>
</build>
- <profiles>
- <profile>
- <id>release</id>
- <distributionManagement>
- <snapshotRepository>
- <id>sonatype-nexus-staging</id>
- <url>https://oss.sonatype.org/content/repositories/snapshots</url>
- </snapshotRepository>
- <repository>
- <id>sonatype-nexus-staging</id>
- <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
- </repository>
- </distributionManagement>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- <version>2.2.1</version>
- <executions>
- <execution>
- <id>attach-sources</id>
- <goals>
- <goal>jar-no-fork</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>2.9.1</version>
- <executions>
- <execution>
- <id>attach-javadocs</id>
- <goals>
- <goal>jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-gpg-plugin</artifactId>
- <version>1.5</version>
- <executions>
- <execution>
- <id>sign-artifacts</id>
- <phase>verify</phase>
- <goals>
- <goal>sign</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.sonatype.plugins</groupId>
- <artifactId>nexus-staging-maven-plugin</artifactId>
- <version>1.6.3</version>
- <extensions>true</extensions>
- <configuration>
- <serverId>sonatype-nexus-staging</serverId>
- <nexusUrl>https://oss.sonatype.org/</nexusUrl>
- <autoReleaseAfterClose>false</autoReleaseAfterClose>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
</project>
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
index 7bf87858..0b3060a7 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
@@ -30,6 +30,9 @@
package com.google.protobuf.util;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.primitives.Ints;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.FieldMask;
@@ -37,7 +40,6 @@ import com.google.protobuf.Internal;
import com.google.protobuf.Message;
import java.util.Arrays;
-import java.util.List;
/**
* Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
@@ -53,6 +55,7 @@ public class FieldMaskUtil {
* Converts a FieldMask to a string.
*/
public static String toString(FieldMask fieldMask) {
+ // TODO(xiaofeng): Consider using com.google.common.base.Joiner here instead.
StringBuilder result = new StringBuilder();
boolean first = true;
for (String value : fieldMask.getPathsList()) {
@@ -74,6 +77,7 @@ public class FieldMaskUtil {
* Parses from a string to a FieldMask.
*/
public static FieldMask fromString(String value) {
+ // TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
return fromStringList(
null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
}
@@ -83,8 +87,8 @@ public class FieldMaskUtil {
*
* @throws IllegalArgumentException if any of the field path is invalid.
*/
- public static FieldMask fromString(Class<? extends Message> type, String value)
- throws IllegalArgumentException {
+ public static FieldMask fromString(Class<? extends Message> type, String value) {
+ // TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
return fromStringList(
type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
}
@@ -94,9 +98,9 @@ public class FieldMaskUtil {
*
* @throws IllegalArgumentException if any of the field path is not valid.
*/
+ // TODO(xiaofeng): Consider renaming fromStrings()
public static FieldMask fromStringList(
- Class<? extends Message> type, List<String> paths)
- throws IllegalArgumentException {
+ Class<? extends Message> type, Iterable<String> paths) {
FieldMask.Builder builder = FieldMask.newBuilder();
for (String path : paths) {
if (path.isEmpty()) {
@@ -113,15 +117,74 @@ public class FieldMaskUtil {
}
/**
+ * Constructs a FieldMask from the passed field numbers.
+ *
+ * @throws IllegalArgumentException if any of the fields are invalid for the message.
+ */
+ public static FieldMask fromFieldNumbers(Class<? extends Message> type, int... fieldNumbers) {
+ return fromFieldNumbers(type, Ints.asList(fieldNumbers));
+ }
+
+ /**
+ * Constructs a FieldMask from the passed field numbers.
+ *
+ * @throws IllegalArgumentException if any of the fields are invalid for the message.
+ */
+ public static FieldMask fromFieldNumbers(
+ Class<? extends Message> type, Iterable<Integer> fieldNumbers) {
+ Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
+
+ FieldMask.Builder builder = FieldMask.newBuilder();
+ for (Integer fieldNumber : fieldNumbers) {
+ FieldDescriptor field = descriptor.findFieldByNumber(fieldNumber);
+ checkArgument(
+ field != null,
+ String.format("%s is not a valid field number for %s.", fieldNumber, type));
+ builder.addPaths(field.getName());
+ }
+ return builder.build();
+ }
+
+ /**
+ * Checks whether paths in a given fields mask are valid.
+ */
+ public static boolean isValid(Class<? extends Message> type, FieldMask fieldMask) {
+ Descriptor descriptor =
+ Internal.getDefaultInstance(type).getDescriptorForType();
+
+ return isValid(descriptor, fieldMask);
+ }
+
+ /**
+ * Checks whether paths in a given fields mask are valid.
+ */
+ public static boolean isValid(Descriptor descriptor, FieldMask fieldMask) {
+ for (String path : fieldMask.getPathsList()) {
+ if (!isValid(descriptor, path)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Checks whether a given field path is valid.
*/
public static boolean isValid(Class<? extends Message> type, String path) {
+ Descriptor descriptor =
+ Internal.getDefaultInstance(type).getDescriptorForType();
+
+ return isValid(descriptor, path);
+ }
+
+ /**
+ * Checks whether paths in a given fields mask are valid.
+ */
+ public static boolean isValid(Descriptor descriptor, String path) {
String[] parts = path.split(FIELD_SEPARATOR_REGEX);
if (parts.length == 0) {
return false;
}
- Descriptor descriptor =
- Internal.getDefaultInstance(type).getDescriptorForType();
for (String name : parts) {
if (descriptor == null) {
return false;
@@ -171,7 +234,7 @@ public class FieldMaskUtil {
/**
* Options to customize merging behavior.
*/
- public static class MergeOptions {
+ public static final class MergeOptions {
private boolean replaceMessageFields = false;
private boolean replaceRepeatedFields = false;
diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
index c9a39153..d13ff0ed 100644
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -78,6 +78,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.logging.Logger;
/**
@@ -99,7 +100,7 @@ public class JsonFormat {
* Creates a {@link Printer} with default configurations.
*/
public static Printer printer() {
- return new Printer(TypeRegistry.getEmptyTypeRegistry());
+ return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false);
}
/**
@@ -107,9 +108,16 @@ public class JsonFormat {
*/
public static class Printer {
private final TypeRegistry registry;
-
- private Printer(TypeRegistry registry) {
+ private final boolean includingDefaultValueFields;
+ private final boolean preservingProtoFieldNames;
+
+ private Printer(
+ TypeRegistry registry,
+ boolean includingDefaultValueFields,
+ boolean preservingProtoFieldNames) {
this.registry = registry;
+ this.includingDefaultValueFields = includingDefaultValueFields;
+ this.preservingProtoFieldNames = preservingProtoFieldNames;
}
/**
@@ -122,7 +130,27 @@ public class JsonFormat {
if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
throw new IllegalArgumentException("Only one registry is allowed.");
}
- return new Printer(registry);
+ return new Printer(registry, includingDefaultValueFields, preservingProtoFieldNames);
+ }
+
+ /**
+ * Creates a new {@link Printer} that will also print fields set to their
+ * defaults. Empty repeated fields and map fields will be printed as well.
+ * The new Printer clones all other configurations from the current
+ * {@link Printer}.
+ */
+ public Printer includingDefaultValueFields() {
+ return new Printer(registry, true, preservingProtoFieldNames);
+ }
+
+ /**
+ * Creates a new {@link Printer} that is configured to use the original proto
+ * field names as defined in the .proto file rather than converting them to
+ * lowerCamelCase. The new Printer clones all other configurations from the
+ * current {@link Printer}.
+ */
+ public Printer preservingProtoFieldNames() {
+ return new Printer(registry, includingDefaultValueFields, true);
}
/**
@@ -136,7 +164,8 @@ public class JsonFormat {
throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
- new PrinterImpl(registry, output).print(message);
+ new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output)
+ .print(message);
}
/**
@@ -298,7 +327,7 @@ public class JsonFormat {
private void addFile(FileDescriptor file) {
// Skip the file if it's already added.
- if (files.contains(file.getName())) {
+ if (!files.add(file.getFullName())) {
return;
}
for (FileDescriptor dependency : file.getDependencies()) {
@@ -397,6 +426,8 @@ public class JsonFormat {
*/
private static final class PrinterImpl {
private final TypeRegistry registry;
+ private final boolean includingDefaultValueFields;
+ private final boolean preservingProtoFieldNames;
private final TextGenerator generator;
// We use Gson to help handle string escapes.
private final Gson gson;
@@ -405,8 +436,14 @@ public class JsonFormat {
private static final Gson DEFAULT_GSON = new Gson();
}
- PrinterImpl(TypeRegistry registry, Appendable jsonOutput) {
+ PrinterImpl(
+ TypeRegistry registry,
+ boolean includingDefaultValueFields,
+ boolean preservingProtoFieldNames,
+ Appendable jsonOutput) {
this.registry = registry;
+ this.includingDefaultValueFields = includingDefaultValueFields;
+ this.preservingProtoFieldNames = preservingProtoFieldNames;
this.generator = new TextGenerator(jsonOutput);
this.gson = GsonHolder.DEFAULT_GSON;
}
@@ -647,13 +684,23 @@ public class JsonFormat {
generator.print("\"@type\": " + gson.toJson(typeUrl));
printedField = true;
}
- for (Map.Entry<FieldDescriptor, Object> field
- : message.getAllFields().entrySet()) {
- // Skip unknown enum fields.
- if (field.getValue() instanceof EnumValueDescriptor
- && ((EnumValueDescriptor) field.getValue()).getIndex() == -1) {
- continue;
+ Map<FieldDescriptor, Object> fieldsToPrint = null;
+ if (includingDefaultValueFields) {
+ fieldsToPrint = new TreeMap<FieldDescriptor, Object>();
+ for (FieldDescriptor field : message.getDescriptorForType().getFields()) {
+ if (field.isOptional()
+ && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
+ && !message.hasField(field)) {
+ // Always skip empty optional message fields. If not we will recurse indefinitely if
+ // a message has itself as a sub-field.
+ continue;
+ }
+ fieldsToPrint.put(field, message.getField(field));
}
+ } else {
+ fieldsToPrint = message.getAllFields();
+ }
+ for (Map.Entry<FieldDescriptor, Object> field : fieldsToPrint.entrySet()) {
if (printedField) {
// Add line-endings for the previous field.
generator.print(",\n");
@@ -673,7 +720,11 @@ public class JsonFormat {
private void printField(FieldDescriptor field, Object value)
throws IOException {
- generator.print("\"" + fieldNameToCamelName(field.getName()) + "\": ");
+ if (preservingProtoFieldNames) {
+ generator.print("\"" + field.getName() + "\": ");
+ } else {
+ generator.print("\"" + field.getJsonName() + "\": ");
+ }
if (field.isMapField()) {
printMapFieldValue(field, value);
} else if (field.isRepeated()) {
@@ -689,11 +740,6 @@ public class JsonFormat {
generator.print("[");
boolean printedElement = false;
for (Object element : (List) value) {
- // Skip unknown enum entries.
- if (element instanceof EnumValueDescriptor
- && ((EnumValueDescriptor) element).getIndex() == -1) {
- continue;
- }
if (printedElement) {
generator.print(", ");
} else {
@@ -720,11 +766,6 @@ public class JsonFormat {
Message entry = (Message) element;
Object entryKey = entry.getField(keyField);
Object entryValue = entry.getField(valueField);
- // Skip unknown enum entries.
- if (entryValue instanceof EnumValueDescriptor
- && ((EnumValueDescriptor) entryValue).getIndex() == -1) {
- continue;
- }
if (printedElement) {
generator.print(",\n");
} else {
@@ -871,8 +912,13 @@ public class JsonFormat {
generator.print("\"");
}
} else {
- generator.print(
- "\"" + ((EnumValueDescriptor) value).getName() + "\"");
+ if (((EnumValueDescriptor) value).getIndex() == -1) {
+ generator.print(
+ String.valueOf(((EnumValueDescriptor) value).getNumber()));
+ } else {
+ generator.print(
+ "\"" + ((EnumValueDescriptor) value).getName() + "\"");
+ }
}
break;
@@ -916,38 +962,6 @@ public class JsonFormat {
}
return parts[1];
}
-
- private static String fieldNameToCamelName(String name) {
- StringBuilder result = new StringBuilder(name.length());
- boolean isNextUpperCase = false;
- for (int i = 0; i < name.length(); i++) {
- Character ch = name.charAt(i);
- if (Character.isLowerCase(ch)) {
- if (isNextUpperCase) {
- result.append(Character.toUpperCase(ch));
- } else {
- result.append(ch);
- }
- isNextUpperCase = false;
- } else if (Character.isUpperCase(ch)) {
- if (i == 0 && !isNextUpperCase) {
- // Force first letter to lower-case unless explicitly told to
- // capitalize it.
- result.append(Character.toLowerCase(ch));
- } else {
- // Capital letters after the first are left as-is.
- result.append(ch);
- }
- isNextUpperCase = false;
- } else if (Character.isDigit(ch)) {
- result.append(ch);
- isNextUpperCase = true;
- } else {
- isNextUpperCase = true;
- }
- }
- return result.toString();
- }
private static class ParserImpl {
private final TypeRegistry registry;
@@ -1085,7 +1099,8 @@ public class JsonFormat {
Map<String, FieldDescriptor> fieldNameMap =
new HashMap<String, FieldDescriptor>();
for (FieldDescriptor field : descriptor.getFields()) {
- fieldNameMap.put(fieldNameToCamelName(field.getName()), field);
+ fieldNameMap.put(field.getName(), field);
+ fieldNameMap.put(field.getJsonName(), field);
}
fieldNameMaps.put(descriptor, fieldNameMap);
return fieldNameMap;
@@ -1244,7 +1259,25 @@ public class JsonFormat {
private void mergeField(FieldDescriptor field, JsonElement json,
Message.Builder builder) throws InvalidProtocolBufferException {
- if (json instanceof JsonNull) {
+ if (field.isRepeated()) {
+ if (builder.getRepeatedFieldCount(field) > 0) {
+ throw new InvalidProtocolBufferException(
+ "Field " + field.getFullName() + " has already been set.");
+ }
+ } else {
+ if (builder.hasField(field)) {
+ throw new InvalidProtocolBufferException(
+ "Field " + field.getFullName() + " has already been set.");
+ }
+ if (field.getContainingOneof() != null
+ && builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
+ FieldDescriptor other = builder.getOneofFieldDescriptor(field.getContainingOneof());
+ throw new InvalidProtocolBufferException(
+ "Cannot set field " + field.getFullName() + " because another field "
+ + other.getFullName() + " belonging to the same oneof has already been set ");
+ }
+ }
+ if (field.isRepeated() && json instanceof JsonNull) {
// We allow "null" as value for all field types and treat it as if the
// field is not present.
return;
@@ -1282,7 +1315,8 @@ public class JsonFormat {
Object value = parseFieldValue(
valueField, entry.getValue(), entryBuilder);
if (value == null) {
- value = getDefaultValue(valueField, entryBuilder);
+ throw new InvalidProtocolBufferException(
+ "Map value cannot be null.");
}
entryBuilder.setField(keyField, key);
entryBuilder.setField(valueField, value);
@@ -1341,7 +1375,8 @@ public class JsonFormat {
for (int i = 0; i < array.size(); ++i) {
Object value = parseFieldValue(field, array.get(i), builder);
if (value == null) {
- value = getDefaultValue(field, builder);
+ throw new InvalidProtocolBufferException(
+ "Repeated field elements cannot be null");
}
builder.addRepeatedField(field, value);
}
@@ -1352,6 +1387,15 @@ public class JsonFormat {
try {
return Integer.parseInt(json.getAsString());
} catch (Exception e) {
+ // Fall through.
+ }
+ // JSON doesn't distinguish between integer values and floating point values so "1" and
+ // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
+ // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
+ try {
+ BigDecimal value = new BigDecimal(json.getAsString());
+ return value.intValueExact();
+ } catch (Exception e) {
throw new InvalidProtocolBufferException("Not an int32 value: " + json);
}
}
@@ -1361,7 +1405,16 @@ public class JsonFormat {
try {
return Long.parseLong(json.getAsString());
} catch (Exception e) {
- throw new InvalidProtocolBufferException("Not an int64 value: " + json);
+ // Fall through.
+ }
+ // JSON doesn't distinguish between integer values and floating point values so "1" and
+ // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
+ // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
+ try {
+ BigDecimal value = new BigDecimal(json.getAsString());
+ return value.longValueExact();
+ } catch (Exception e) {
+ throw new InvalidProtocolBufferException("Not an int32 value: " + json);
}
}
@@ -1377,6 +1430,21 @@ public class JsonFormat {
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
+ // Fall through.
+ }
+ // JSON doesn't distinguish between integer values and floating point values so "1" and
+ // "1.000" are treated as equal in JSON. For this reason we accept floating point values for
+ // integer fields as well as long as it actually is an integer (i.e., round(value) == value).
+ try {
+ BigDecimal decimalValue = new BigDecimal(json.getAsString());
+ BigInteger value = decimalValue.toBigIntegerExact();
+ if (value.signum() < 0 || value.compareTo(new BigInteger("FFFFFFFF", 16)) > 0) {
+ throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
+ }
+ return value.intValue();
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (Exception e) {
throw new InvalidProtocolBufferException(
"Not an uint32 value: " + json);
}
@@ -1388,7 +1456,8 @@ public class JsonFormat {
private long parseUint64(JsonElement json)
throws InvalidProtocolBufferException {
try {
- BigInteger value = new BigInteger(json.getAsString());
+ BigDecimal decimalValue = new BigDecimal(json.getAsString());
+ BigInteger value = decimalValue.toBigIntegerExact();
if (value.compareTo(BigInteger.ZERO) < 0
|| value.compareTo(MAX_UINT64) > 0) {
throw new InvalidProtocolBufferException(
@@ -1488,7 +1557,12 @@ public class JsonFormat {
return json.getAsString();
}
- private ByteString parseBytes(JsonElement json) {
+ private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
+ String encoded = json.getAsString();
+ if (encoded.length() % 4 != 0) {
+ throw new InvalidProtocolBufferException(
+ "Bytes field is not encoded in standard BASE64 with paddings: " + encoded);
+ }
return ByteString.copyFrom(
BaseEncoding.base64().decode(json.getAsString()));
}
@@ -1498,9 +1572,25 @@ public class JsonFormat {
String value = json.getAsString();
EnumValueDescriptor result = enumDescriptor.findValueByName(value);
if (result == null) {
- throw new InvalidProtocolBufferException(
- "Invalid enum value: " + value + " for enum type: "
- + enumDescriptor.getFullName());
+ // Try to interpret the value as a number.
+ try {
+ int numericValue = parseInt32(json);
+ if (enumDescriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
+ result = enumDescriptor.findValueByNumberCreatingIfUnknown(numericValue);
+ } else {
+ result = enumDescriptor.findValueByNumber(numericValue);
+ }
+ } catch (InvalidProtocolBufferException e) {
+ // Fall through. This exception is about invalid int32 value we get from parseInt32() but
+ // that's not the exception we want the user to see. Since result == null, we will throw
+ // an exception later.
+ }
+
+ if (result == null) {
+ throw new InvalidProtocolBufferException(
+ "Invalid enum value: " + value + " for enum type: "
+ + enumDescriptor.getFullName());
+ }
}
return result;
}
diff --git a/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java b/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
index 6e4b7c03..3033182a 100644
--- a/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
+++ b/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
@@ -58,8 +58,12 @@ public class TimeUtil {
private static final long MILLIS_PER_SECOND = 1000;
private static final long MICROS_PER_SECOND = 1000000;
- private static final SimpleDateFormat timestampFormat =
- createTimestampFormat();
+ private static final ThreadLocal<SimpleDateFormat> timestampFormat =
+ new ThreadLocal<SimpleDateFormat>() {
+ protected SimpleDateFormat initialValue() {
+ return createTimestampFormat();
+ }
+ };
private static SimpleDateFormat createTimestampFormat() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
@@ -96,7 +100,7 @@ public class TimeUtil {
throw new IllegalArgumentException("Timestamp is out of range.");
}
Date date = new Date(timestamp.getSeconds() * MILLIS_PER_SECOND);
- result.append(timestampFormat.format(date));
+ result.append(timestampFormat.get().format(date));
// Format the nanos part.
if (timestamp.getNanos() < 0 || timestamp.getNanos() >= NANOS_PER_SECOND) {
throw new IllegalArgumentException("Timestamp has invalid nanos value.");
@@ -147,7 +151,7 @@ public class TimeUtil {
secondValue = timeValue.substring(0, pointPosition);
nanoValue = timeValue.substring(pointPosition + 1);
}
- Date date = timestampFormat.parse(secondValue);
+ Date date = timestampFormat.get().parse(secondValue);
long seconds = date.getTime() / MILLIS_PER_SECOND;
int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
// Parse timezone offsets.
diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
index 67fbe0b1..a312fc33 100644
--- a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
@@ -53,6 +53,21 @@ public class FieldMaskUtilTest extends TestCase {
NestedTestAllTypes.class, "payload.nonexist"));
assertTrue(FieldMaskUtil.isValid(
+ NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
+ assertFalse(FieldMaskUtil.isValid(
+ NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
+ assertFalse(FieldMaskUtil.isValid(
+ NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")));
+
+ assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload"));
+ assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist"));
+
+ assertTrue(FieldMaskUtil.isValid(
+ NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
+ assertFalse(FieldMaskUtil.isValid(
+ NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
+
+ assertTrue(FieldMaskUtil.isValid(
NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
// Repeated fields cannot have sub-paths.
assertFalse(FieldMaskUtil.isValid(
@@ -74,7 +89,7 @@ public class FieldMaskUtilTest extends TestCase {
addPaths("bar").addPaths("").build();
assertEquals("foo,bar", FieldMaskUtil.toString(mask));
}
-
+
public void testFromString() throws Exception {
FieldMask mask = FieldMaskUtil.fromString("");
assertEquals(0, mask.getPathsCount());
@@ -85,16 +100,16 @@ public class FieldMaskUtilTest extends TestCase {
assertEquals(2, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
assertEquals("bar.baz", mask.getPaths(1));
-
+
// Empty field paths are ignore.
mask = FieldMaskUtil.fromString(",foo,,bar,");
assertEquals(2, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
assertEquals("bar", mask.getPaths(1));
-
+
// Check whether the field paths are valid if a class parameter is provided.
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload");
-
+
try {
mask = FieldMaskUtil.fromString(
NestedTestAllTypes.class, "payload,nonexist");
@@ -103,6 +118,31 @@ public class FieldMaskUtilTest extends TestCase {
// Expected.
}
}
+
+ public void testFromFieldNumbers() throws Exception {
+ FieldMask mask = FieldMaskUtil.fromFieldNumbers(TestAllTypes.class);
+ assertEquals(0, mask.getPathsCount());
+ mask =
+ FieldMaskUtil.fromFieldNumbers(
+ TestAllTypes.class, TestAllTypes.OPTIONAL_INT32_FIELD_NUMBER);
+ assertEquals(1, mask.getPathsCount());
+ assertEquals("optional_int32", mask.getPaths(0));
+ mask =
+ FieldMaskUtil.fromFieldNumbers(
+ TestAllTypes.class,
+ TestAllTypes.OPTIONAL_INT32_FIELD_NUMBER,
+ TestAllTypes.OPTIONAL_INT64_FIELD_NUMBER);
+ assertEquals(2, mask.getPathsCount());
+ assertEquals("optional_int32", mask.getPaths(0));
+ assertEquals("optional_int64", mask.getPaths(1));
+
+ try {
+ int invalidFieldNumber = 1000;
+ mask = FieldMaskUtil.fromFieldNumbers(TestAllTypes.class, invalidFieldNumber);
+ fail("Exception is expected.");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
public void testUnion() throws Exception {
// Only test a simple case here and expect
diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
index ddf5ad2a..c0eb0330 100644
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -51,9 +51,11 @@ import com.google.protobuf.util.JsonTestProto.TestAllTypes;
import com.google.protobuf.util.JsonTestProto.TestAllTypes.NestedEnum;
import com.google.protobuf.util.JsonTestProto.TestAllTypes.NestedMessage;
import com.google.protobuf.util.JsonTestProto.TestAny;
+import com.google.protobuf.util.JsonTestProto.TestCustomJsonName;
import com.google.protobuf.util.JsonTestProto.TestDuration;
import com.google.protobuf.util.JsonTestProto.TestFieldMask;
import com.google.protobuf.util.JsonTestProto.TestMap;
+import com.google.protobuf.util.JsonTestProto.TestOneof;
import com.google.protobuf.util.JsonTestProto.TestStruct;
import com.google.protobuf.util.JsonTestProto.TestTimestamp;
import com.google.protobuf.util.JsonTestProto.TestWrappers;
@@ -196,9 +198,6 @@ public class JsonFormatTest extends TestCase {
}
public void testUnknownEnumValues() throws Exception {
- // Unknown enum values will be dropped.
- // TODO(xiaofeng): We may want to revisit this (whether we should omit
- // unknown enum values).
TestAllTypes message = TestAllTypes.newBuilder()
.setOptionalNestedEnumValue(12345)
.addRepeatedNestedEnumValue(12345)
@@ -206,8 +205,10 @@ public class JsonFormatTest extends TestCase {
.build();
assertEquals(
"{\n"
- + " \"repeatedNestedEnum\": [\"FOO\"]\n"
+ + " \"optionalNestedEnum\": 12345,\n"
+ + " \"repeatedNestedEnum\": [12345, \"FOO\"]\n"
+ "}", toJsonString(message));
+ assertRoundTripEquals(message);
TestMap.Builder mapBuilder = TestMap.newBuilder();
mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0);
@@ -216,9 +217,11 @@ public class JsonFormatTest extends TestCase {
assertEquals(
"{\n"
+ " \"int32ToEnumMap\": {\n"
- + " \"1\": \"FOO\"\n"
+ + " \"1\": \"FOO\",\n"
+ + " \"2\": 12345\n"
+ " }\n"
+ "}", toJsonString(mapMessage));
+ assertRoundTripEquals(mapMessage);
}
public void testSpecialFloatValues() throws Exception {
@@ -263,6 +266,35 @@ public class JsonFormatTest extends TestCase {
assertEquals(true, message.getOptionalBool());
}
+ public void testParserAcceptFloatingPointValueForIntegerField() throws Exception {
+ // Test that numeric values like "1.000", "1e5" will also be accepted.
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"repeatedInt32\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ + " \"repeatedUint32\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ + " \"repeatedInt64\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ + " \"repeatedUint64\": [1.000, 1e5, \"1.000\", \"1e5\"]\n"
+ + "}", builder);
+ int[] expectedValues = new int[]{1, 100000, 1, 100000};
+ assertEquals(4, builder.getRepeatedInt32Count());
+ assertEquals(4, builder.getRepeatedUint32Count());
+ assertEquals(4, builder.getRepeatedInt64Count());
+ assertEquals(4, builder.getRepeatedUint64Count());
+ for (int i = 0; i < 4; ++i) {
+ assertEquals(expectedValues[i], builder.getRepeatedInt32(i));
+ assertEquals(expectedValues[i], builder.getRepeatedUint32(i));
+ assertEquals(expectedValues[i], builder.getRepeatedInt64(i));
+ assertEquals(expectedValues[i], builder.getRepeatedUint64(i));
+ }
+
+ // Non-integers will still be rejected.
+ assertRejects("optionalInt32", "1.5");
+ assertRejects("optionalUint32", "1.5");
+ assertRejects("optionalInt64", "1.5");
+ assertRejects("optionalUint64", "1.5");
+ }
+
private void assertRejects(String name, String value) {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
@@ -285,6 +317,7 @@ public class JsonFormatTest extends TestCase {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
// Both numeric form and string form are accepted.
mergeFromJson("{\"" + name + "\":" + value + "}", builder);
+ builder.clear();
mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder);
}
@@ -370,84 +403,74 @@ public class JsonFormatTest extends TestCase {
TestAllTypes message = builder.build();
assertEquals(TestAllTypes.getDefaultInstance(), message);
- // Repeated field elements can also be null.
- builder = TestAllTypes.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"repeatedInt32\": [null, null],\n"
- + " \"repeatedInt64\": [null, null],\n"
- + " \"repeatedUint32\": [null, null],\n"
- + " \"repeatedUint64\": [null, null],\n"
- + " \"repeatedSint32\": [null, null],\n"
- + " \"repeatedSint64\": [null, null],\n"
- + " \"repeatedFixed32\": [null, null],\n"
- + " \"repeatedFixed64\": [null, null],\n"
- + " \"repeatedSfixed32\": [null, null],\n"
- + " \"repeatedSfixed64\": [null, null],\n"
- + " \"repeatedFloat\": [null, null],\n"
- + " \"repeatedDouble\": [null, null],\n"
- + " \"repeatedBool\": [null, null],\n"
- + " \"repeatedString\": [null, null],\n"
- + " \"repeatedBytes\": [null, null],\n"
- + " \"repeatedNestedMessage\": [null, null],\n"
- + " \"repeatedNestedEnum\": [null, null]\n"
- + "}", builder);
- message = builder.build();
- // "null" elements will be parsed to default values.
- assertEquals(2, message.getRepeatedInt32Count());
- assertEquals(0, message.getRepeatedInt32(0));
- assertEquals(0, message.getRepeatedInt32(1));
- assertEquals(2, message.getRepeatedInt32Count());
- assertEquals(0, message.getRepeatedInt32(0));
- assertEquals(0, message.getRepeatedInt32(1));
- assertEquals(2, message.getRepeatedInt64Count());
- assertEquals(0, message.getRepeatedInt64(0));
- assertEquals(0, message.getRepeatedInt64(1));
- assertEquals(2, message.getRepeatedUint32Count());
- assertEquals(0, message.getRepeatedUint32(0));
- assertEquals(0, message.getRepeatedUint32(1));
- assertEquals(2, message.getRepeatedUint64Count());
- assertEquals(0, message.getRepeatedUint64(0));
- assertEquals(0, message.getRepeatedUint64(1));
- assertEquals(2, message.getRepeatedSint32Count());
- assertEquals(0, message.getRepeatedSint32(0));
- assertEquals(0, message.getRepeatedSint32(1));
- assertEquals(2, message.getRepeatedSint64Count());
- assertEquals(0, message.getRepeatedSint64(0));
- assertEquals(0, message.getRepeatedSint64(1));
- assertEquals(2, message.getRepeatedFixed32Count());
- assertEquals(0, message.getRepeatedFixed32(0));
- assertEquals(0, message.getRepeatedFixed32(1));
- assertEquals(2, message.getRepeatedFixed64Count());
- assertEquals(0, message.getRepeatedFixed64(0));
- assertEquals(0, message.getRepeatedFixed64(1));
- assertEquals(2, message.getRepeatedSfixed32Count());
- assertEquals(0, message.getRepeatedSfixed32(0));
- assertEquals(0, message.getRepeatedSfixed32(1));
- assertEquals(2, message.getRepeatedSfixed64Count());
- assertEquals(0, message.getRepeatedSfixed64(0));
- assertEquals(0, message.getRepeatedSfixed64(1));
- assertEquals(2, message.getRepeatedFloatCount());
- assertEquals(0f, message.getRepeatedFloat(0));
- assertEquals(0f, message.getRepeatedFloat(1));
- assertEquals(2, message.getRepeatedDoubleCount());
- assertEquals(0.0, message.getRepeatedDouble(0));
- assertEquals(0.0, message.getRepeatedDouble(1));
- assertEquals(2, message.getRepeatedBoolCount());
- assertFalse(message.getRepeatedBool(0));
- assertFalse(message.getRepeatedBool(1));
- assertEquals(2, message.getRepeatedStringCount());
- assertTrue(message.getRepeatedString(0).isEmpty());
- assertTrue(message.getRepeatedString(1).isEmpty());
- assertEquals(2, message.getRepeatedBytesCount());
- assertTrue(message.getRepeatedBytes(0).isEmpty());
- assertTrue(message.getRepeatedBytes(1).isEmpty());
- assertEquals(2, message.getRepeatedNestedMessageCount());
- assertEquals(NestedMessage.getDefaultInstance(), message.getRepeatedNestedMessage(0));
- assertEquals(NestedMessage.getDefaultInstance(), message.getRepeatedNestedMessage(1));
- assertEquals(2, message.getRepeatedNestedEnumCount());
- assertEquals(0, message.getRepeatedNestedEnumValue(0));
- assertEquals(0, message.getRepeatedNestedEnumValue(1));
+ // Repeated field elements cannot be null.
+ try {
+ builder = TestAllTypes.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"repeatedInt32\": [null, null],\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
+
+ try {
+ builder = TestAllTypes.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"repeatedNestedMessage\": [null, null],\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
+ }
+
+ public void testParserRejectDuplicatedFields() throws Exception {
+ // TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last
+ // one if multiple entries have the same name. This is not the desired behavior but it can
+ // only be fixed by using our own parser. Here we only test the cases where the names are
+ // different but still referring to the same field.
+
+ // Duplicated optional fields.
+ try {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"optionalNestedMessage\": {},\n"
+ + " \"optional_nested_message\": {}\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
+
+ // Duplicated repeated fields.
+ try {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"repeatedNestedMessage\": [null, null],\n"
+ + " \"repeated_nested_message\": [null, null]\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
+
+ // Duplicated oneof fields.
+ try {
+ TestOneof.Builder builder = TestOneof.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"oneofInt32\": 1,\n"
+ + " \"oneof_int32\": 2\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
}
public void testMapFields() throws Exception {
@@ -592,18 +615,30 @@ public class JsonFormatTest extends TestCase {
assertRoundTripEquals(message);
}
- public void testMapNullValueIsDefault() throws Exception {
- TestMap.Builder builder = TestMap.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"int32ToInt32Map\": {\"1\": null},\n"
- + " \"int32ToMessageMap\": {\"2\": null}\n"
- + "}", builder);
- TestMap message = builder.build();
- assertTrue(message.getInt32ToInt32Map().containsKey(1));
- assertEquals(0, message.getInt32ToInt32Map().get(1).intValue());
- assertTrue(message.getInt32ToMessageMap().containsKey(2));
- assertEquals(0, message.getInt32ToMessageMap().get(2).getValue());
+ public void testMapNullValueIsRejected() throws Exception {
+ try {
+ TestMap.Builder builder = TestMap.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"int32ToInt32Map\": {null: 1},\n"
+ + " \"int32ToMessageMap\": {null: 2}\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
+
+ try {
+ TestMap.Builder builder = TestMap.newBuilder();
+ mergeFromJson(
+ "{\n"
+ + " \"int32ToInt32Map\": {\"1\": null},\n"
+ + " \"int32ToMessageMap\": {\"2\": null}\n"
+ + "}", builder);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Exception expected.
+ }
}
public void testParserAcceptNonQuotedObjectKey() throws Exception {
@@ -743,6 +778,15 @@ public class JsonFormatTest extends TestCase {
+ " }\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
+
+ builder = TestStruct.newBuilder();
+ builder.setValue(Value.newBuilder().setNullValueValue(0).build());
+ message = builder.build();
+ assertEquals(
+ "{\n"
+ + " \"value\": null\n"
+ + "}", toJsonString(message));
+ assertRoundTripEquals(message);
}
public void testAnyFields() throws Exception {
@@ -891,6 +935,15 @@ public class JsonFormatTest extends TestCase {
+ " }\n"
+ "}", printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
+ Value.Builder valueBuilder = Value.newBuilder();
+ valueBuilder.setNumberValue(1);
+ anyMessage = Any.pack(valueBuilder.build());
+ assertEquals(
+ "{\n"
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n"
+ + " \"value\": 1.0\n"
+ + "}", printer.print(anyMessage));
+ assertRoundTripEquals(anyMessage, registry);
}
public void testParserMissingTypeUrl() throws Exception {
@@ -949,16 +1002,9 @@ public class JsonFormatTest extends TestCase {
}
public void testParserRejectInvalidBase64() throws Exception {
- try {
- TestAllTypes.Builder builder = TestAllTypes.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"optionalBytes\": \"!@#$\"\n"
- + "}", builder);
- fail("Exception is expected.");
- } catch (InvalidProtocolBufferException e) {
- // Expected.
- }
+ assertRejects("optionalBytes", "!@#$");
+ // We use standard BASE64 with paddings.
+ assertRejects("optionalBytes", "AQI");
}
public void testParserRejectInvalidEnumValue() throws Exception {
@@ -973,4 +1019,138 @@ public class JsonFormatTest extends TestCase {
// Expected.
}
}
+
+ public void testCustomJsonName() throws Exception {
+ TestCustomJsonName message = TestCustomJsonName.newBuilder().setValue(12345).build();
+ assertEquals("{\n" + " \"@value\": 12345\n" + "}", JsonFormat.printer().print(message));
+ assertRoundTripEquals(message);
+ }
+
+ public void testIncludingDefaultValueFields() throws Exception {
+ TestAllTypes message = TestAllTypes.getDefaultInstance();
+ assertEquals("{\n}", JsonFormat.printer().print(message));
+ assertEquals(
+ "{\n"
+ + " \"optionalInt32\": 0,\n"
+ + " \"optionalInt64\": \"0\",\n"
+ + " \"optionalUint32\": 0,\n"
+ + " \"optionalUint64\": \"0\",\n"
+ + " \"optionalSint32\": 0,\n"
+ + " \"optionalSint64\": \"0\",\n"
+ + " \"optionalFixed32\": 0,\n"
+ + " \"optionalFixed64\": \"0\",\n"
+ + " \"optionalSfixed32\": 0,\n"
+ + " \"optionalSfixed64\": \"0\",\n"
+ + " \"optionalFloat\": 0.0,\n"
+ + " \"optionalDouble\": 0.0,\n"
+ + " \"optionalBool\": false,\n"
+ + " \"optionalString\": \"\",\n"
+ + " \"optionalBytes\": \"\",\n"
+ + " \"optionalNestedEnum\": \"FOO\",\n"
+ + " \"repeatedInt32\": [],\n"
+ + " \"repeatedInt64\": [],\n"
+ + " \"repeatedUint32\": [],\n"
+ + " \"repeatedUint64\": [],\n"
+ + " \"repeatedSint32\": [],\n"
+ + " \"repeatedSint64\": [],\n"
+ + " \"repeatedFixed32\": [],\n"
+ + " \"repeatedFixed64\": [],\n"
+ + " \"repeatedSfixed32\": [],\n"
+ + " \"repeatedSfixed64\": [],\n"
+ + " \"repeatedFloat\": [],\n"
+ + " \"repeatedDouble\": [],\n"
+ + " \"repeatedBool\": [],\n"
+ + " \"repeatedString\": [],\n"
+ + " \"repeatedBytes\": [],\n"
+ + " \"repeatedNestedMessage\": [],\n"
+ + " \"repeatedNestedEnum\": []\n"
+ + "}",
+ JsonFormat.printer().includingDefaultValueFields().print(message));
+
+ TestMap mapMessage = TestMap.getDefaultInstance();
+ assertEquals("{\n}", JsonFormat.printer().print(mapMessage));
+ assertEquals(
+ "{\n"
+ + " \"int32ToInt32Map\": {\n"
+ + " },\n"
+ + " \"int64ToInt32Map\": {\n"
+ + " },\n"
+ + " \"uint32ToInt32Map\": {\n"
+ + " },\n"
+ + " \"uint64ToInt32Map\": {\n"
+ + " },\n"
+ + " \"sint32ToInt32Map\": {\n"
+ + " },\n"
+ + " \"sint64ToInt32Map\": {\n"
+ + " },\n"
+ + " \"fixed32ToInt32Map\": {\n"
+ + " },\n"
+ + " \"fixed64ToInt32Map\": {\n"
+ + " },\n"
+ + " \"sfixed32ToInt32Map\": {\n"
+ + " },\n"
+ + " \"sfixed64ToInt32Map\": {\n"
+ + " },\n"
+ + " \"boolToInt32Map\": {\n"
+ + " },\n"
+ + " \"stringToInt32Map\": {\n"
+ + " },\n"
+ + " \"int32ToInt64Map\": {\n"
+ + " },\n"
+ + " \"int32ToUint32Map\": {\n"
+ + " },\n"
+ + " \"int32ToUint64Map\": {\n"
+ + " },\n"
+ + " \"int32ToSint32Map\": {\n"
+ + " },\n"
+ + " \"int32ToSint64Map\": {\n"
+ + " },\n"
+ + " \"int32ToFixed32Map\": {\n"
+ + " },\n"
+ + " \"int32ToFixed64Map\": {\n"
+ + " },\n"
+ + " \"int32ToSfixed32Map\": {\n"
+ + " },\n"
+ + " \"int32ToSfixed64Map\": {\n"
+ + " },\n"
+ + " \"int32ToFloatMap\": {\n"
+ + " },\n"
+ + " \"int32ToDoubleMap\": {\n"
+ + " },\n"
+ + " \"int32ToBoolMap\": {\n"
+ + " },\n"
+ + " \"int32ToStringMap\": {\n"
+ + " },\n"
+ + " \"int32ToBytesMap\": {\n"
+ + " },\n"
+ + " \"int32ToMessageMap\": {\n"
+ + " },\n"
+ + " \"int32ToEnumMap\": {\n"
+ + " }\n"
+ + "}",
+ JsonFormat.printer().includingDefaultValueFields().print(mapMessage));
+ }
+
+ public void testPreservingProtoFieldNames() throws Exception {
+ TestAllTypes message = TestAllTypes.newBuilder().setOptionalInt32(12345).build();
+ assertEquals("{\n" + " \"optionalInt32\": 12345\n" + "}", JsonFormat.printer().print(message));
+ assertEquals(
+ "{\n" + " \"optional_int32\": 12345\n" + "}",
+ JsonFormat.printer().preservingProtoFieldNames().print(message));
+
+ // The json_name field option is ignored when configured to use original proto field names.
+ TestCustomJsonName messageWithCustomJsonName =
+ TestCustomJsonName.newBuilder().setValue(12345).build();
+ assertEquals(
+ "{\n" + " \"value\": 12345\n" + "}",
+ JsonFormat.printer().preservingProtoFieldNames().print(messageWithCustomJsonName));
+
+ // Parsers accept both original proto field names and lowerCamelCase names.
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ JsonFormat.parser().merge("{\"optionalInt32\": 12345}", builder);
+ assertEquals(12345, builder.getOptionalInt32());
+ builder.clear();
+ JsonFormat.parser().merge("{\"optional_int32\": 54321}", builder);
+ assertEquals(54321, builder.getOptionalInt32());
+ }
}
diff --git a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
index fe5617e1..4c31b2b3 100644
--- a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
@@ -38,6 +38,8 @@ import junit.framework.TestCase;
import org.junit.Assert;
import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
/** Unit tests for {@link TimeUtil}. */
public class TimeUtilTest extends TestCase {
@@ -76,6 +78,71 @@ public class TimeUtilTest extends TestCase {
assertEquals("1970-01-01T08:00:00.010Z", TimeUtil.toString(value));
}
+ private volatile boolean stopParsingThreads = false;
+ private volatile String errorMessage = "";
+
+ private class ParseTimestampThread extends Thread {
+ private final String[] strings;
+ private final Timestamp[] values;
+ public ParseTimestampThread(String[] strings, Timestamp[] values) {
+ this.strings = strings;
+ this.values = values;
+ }
+
+ @Override
+ public void run() {
+ int index = 0;
+ while (!stopParsingThreads) {
+ Timestamp result;
+ try {
+ result = TimeUtil.parseTimestamp(strings[index]);
+ } catch (ParseException e) {
+ errorMessage = "Failed to parse timestamp: " + strings[index];
+ break;
+ }
+ if (result.getSeconds() != values[index].getSeconds()
+ || result.getNanos() != values[index].getNanos()) {
+ errorMessage = "Actual result: " + result.toString() + ", expected: "
+ + values[index].toString();
+ break;
+ }
+ index = (index + 1) % strings.length;
+ }
+ }
+ }
+
+ public void testTimestampConcurrentParsing() throws Exception {
+ String[] timestampStrings = new String[]{
+ "0001-01-01T00:00:00Z",
+ "9999-12-31T23:59:59.999999999Z",
+ "1970-01-01T00:00:00Z",
+ "1969-12-31T23:59:59.999Z",
+ };
+ Timestamp[] timestampValues = new Timestamp[timestampStrings.length];
+ for (int i = 0; i < timestampStrings.length; i++) {
+ timestampValues[i] = TimeUtil.parseTimestamp(timestampStrings[i]);
+ }
+
+ final int THREAD_COUNT = 16;
+ final int RUNNING_TIME = 5000; // in milliseconds.
+ final List<Thread> threads = new ArrayList<Thread>();
+
+ stopParsingThreads = false;
+ errorMessage = "";
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ Thread thread = new ParseTimestampThread(
+ timestampStrings, timestampValues);
+ thread.start();
+ threads.add(thread);
+ }
+ Thread.sleep(RUNNING_TIME);
+ stopParsingThreads = true;
+ for (Thread thread : threads) {
+ thread.join();
+ }
+ Assert.assertEquals("", errorMessage);
+ }
+
public void testTimetampInvalidFormat() throws Exception {
try {
// Value too small.
diff --git a/java/util/src/test/java/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
index b2753af6..509c1d69 100644
--- a/java/util/src/test/java/com/google/protobuf/util/json_test.proto
+++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
@@ -90,6 +90,13 @@ message TestAllTypes {
repeated NestedEnum repeated_nested_enum = 51;
}
+message TestOneof {
+ oneof oneof_field {
+ int32 oneof_int32 = 1;
+ TestAllTypes.NestedMessage oneof_nested_message = 2;
+ }
+}
+
message TestMap {
// Instead of testing all combinations (too many), we only make sure all
// valid types have been used at least in one field as key and in one
@@ -151,8 +158,13 @@ message TestFieldMask {
message TestStruct {
google.protobuf.Struct struct_value = 1;
+ google.protobuf.Value value = 2;
}
message TestAny {
google.protobuf.Any any_value = 1;
}
+
+message TestCustomJsonName {
+ int32 value = 1 [json_name = "@value"];
+}
diff --git a/javanano/pom.xml b/javanano/pom.xml
index 1a95a43e..2c9dd943 100644
--- a/javanano/pom.xml
+++ b/javanano/pom.xml
@@ -10,7 +10,7 @@
</parent>
<groupId>com.google.protobuf.nano</groupId>
<artifactId>protobuf-javanano</artifactId>
- <version>3.0.0-alpha-4</version>
+ <version>3.0.0-alpha-5</version>
<packaging>bundle</packaging>
<name>Protocol Buffer JavaNano API</name>
<description>
@@ -165,7 +165,7 @@
<instructions>
<Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
<Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
- <Export-Package>com.google.protobuf;version=3.0.0-alpha-4</Export-Package>
+ <Export-Package>com.google.protobuf;version=3.0.0-alpha-5</Export-Package>
</instructions>
</configuration>
</plugin>
diff --git a/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java b/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
index eca9c0d9..b49a97fa 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
@@ -32,13 +32,13 @@ package com.google.protobuf.nano;
/**
- * A custom version of {@link android.util.SparseArray} with the minimal API
+ * A custom version of {@code android.util.SparseArray} with the minimal API
* for storing {@link FieldData} objects.
*
* <p>This class is an internal implementation detail of nano and should not
* be called directly by clients.
*
- * Based on {@link android.support.v4.util.SpareArrayCompat}.
+ * Based on {@code android.support.v4.util.SpareArrayCompat}.
*/
public final class FieldArray implements Cloneable {
private static final FieldData DELETED = new FieldData();
diff --git a/js/README.md b/js/README.md
new file mode 100644
index 00000000..fc144a3d
--- /dev/null
+++ b/js/README.md
@@ -0,0 +1,14 @@
+This directory contains Protocol Buffer support for JavaScript. This code works
+in browsers and in Node.js.
+
+The packaging work for this is still in-progress. For now you can just run the
+tests. First you need to build the main C++ distribution because the code
+generator for JavaScript is written in C++:
+
+ $ ./autogen.sh
+ $ ./configure
+ $ make
+
+Then you can run the JavaScript tests in this directory:
+
+ $ cd js && gulp test
diff --git a/js/binary/arith.js b/js/binary/arith.js
new file mode 100644
index 00000000..70257de7
--- /dev/null
+++ b/js/binary/arith.js
@@ -0,0 +1,413 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview This file contains helper code used by jspb.utils to
+ * handle 64-bit integer conversion to/from strings.
+ *
+ * @author cfallin@google.com (Chris Fallin)
+ *
+ * TODO(haberman): move this to javascript/closure/math?
+ */
+
+goog.provide('jspb.arith.Int64');
+goog.provide('jspb.arith.UInt64');
+
+/**
+ * UInt64 implements some 64-bit arithmetic routines necessary for properly
+ * handling 64-bit integer fields. It implements lossless integer arithmetic on
+ * top of JavaScript's number type, which has only 53 bits of precision, by
+ * representing 64-bit integers as two 32-bit halves.
+ *
+ * @param {number} lo The low 32 bits.
+ * @param {number} hi The high 32 bits.
+ * @constructor
+ */
+jspb.arith.UInt64 = function(lo, hi) {
+ /**
+ * The low 32 bits.
+ * @public {number}
+ */
+ this.lo = lo;
+ /**
+ * The high 32 bits.
+ * @public {number}
+ */
+ this.hi = hi;
+};
+
+
+/**
+ * Compare two 64-bit numbers. Returns -1 if the first is
+ * less, +1 if the first is greater, or 0 if both are equal.
+ * @param {!jspb.arith.UInt64} other
+ * @return {number}
+ */
+jspb.arith.UInt64.prototype.cmp = function(other) {
+ if (this.hi < other.hi || (this.hi == other.hi && this.lo < other.lo)) {
+ return -1;
+ } else if (this.hi == other.hi && this.lo == other.lo) {
+ return 0;
+ } else {
+ return 1;
+ }
+};
+
+
+/**
+ * Right-shift this number by one bit.
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.prototype.rightShift = function() {
+ var hi = this.hi >>> 1;
+ var lo = (this.lo >>> 1) | ((this.hi & 1) << 31);
+ return new jspb.arith.UInt64(lo >>> 0, hi >>> 0);
+};
+
+
+/**
+ * Left-shift this number by one bit.
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.prototype.leftShift = function() {
+ var lo = this.lo << 1;
+ var hi = (this.hi << 1) | (this.lo >>> 31);
+ return new jspb.arith.UInt64(lo >>> 0, hi >>> 0);
+};
+
+
+/**
+ * Test the MSB.
+ * @return {boolean}
+ */
+jspb.arith.UInt64.prototype.msb = function() {
+ return !!(this.hi & 0x80000000);
+};
+
+
+/**
+ * Test the LSB.
+ * @return {boolean}
+ */
+jspb.arith.UInt64.prototype.lsb = function() {
+ return !!(this.lo & 1);
+};
+
+
+/**
+ * Test whether this number is zero.
+ * @return {boolean}
+ */
+jspb.arith.UInt64.prototype.zero = function() {
+ return this.lo == 0 && this.hi == 0;
+};
+
+
+/**
+ * Add two 64-bit numbers to produce a 64-bit number.
+ * @param {!jspb.arith.UInt64} other
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.prototype.add = function(other) {
+ var lo = ((this.lo + other.lo) & 0xffffffff) >>> 0;
+ var hi =
+ (((this.hi + other.hi) & 0xffffffff) >>> 0) +
+ (((this.lo + other.lo) >= 0x100000000) ? 1 : 0);
+ return new jspb.arith.UInt64(lo >>> 0, hi >>> 0);
+};
+
+
+/**
+ * Subtract two 64-bit numbers to produce a 64-bit number.
+ * @param {!jspb.arith.UInt64} other
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.prototype.sub = function(other) {
+ var lo = ((this.lo - other.lo) & 0xffffffff) >>> 0;
+ var hi =
+ (((this.hi - other.hi) & 0xffffffff) >>> 0) -
+ (((this.lo - other.lo) < 0) ? 1 : 0);
+ return new jspb.arith.UInt64(lo >>> 0, hi >>> 0);
+};
+
+
+/**
+ * Multiply two 32-bit numbers to produce a 64-bit number.
+ * @param {number} a The first integer: must be in [0, 2^32-1).
+ * @param {number} b The second integer: must be in [0, 2^32-1).
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.mul32x32 = function(a, b) {
+ // Directly multiplying two 32-bit numbers may produce up to 64 bits of
+ // precision, thus losing precision because of the 53-bit mantissa of
+ // JavaScript numbers. So we multiply with 16-bit digits (radix 65536)
+ // instead.
+ var aLow = (a & 0xffff);
+ var aHigh = (a >>> 16);
+ var bLow = (b & 0xffff);
+ var bHigh = (b >>> 16);
+ var productLow =
+ // 32-bit result, result bits 0-31, take all 32 bits
+ (aLow * bLow) +
+ // 32-bit result, result bits 16-47, take bottom 16 as our top 16
+ ((aLow * bHigh) & 0xffff) * 0x10000 +
+ // 32-bit result, result bits 16-47, take bottom 16 as our top 16
+ ((aHigh * bLow) & 0xffff) * 0x10000;
+ var productHigh =
+ // 32-bit result, result bits 32-63, take all 32 bits
+ (aHigh * bHigh) +
+ // 32-bit result, result bits 16-47, take top 16 as our bottom 16
+ ((aLow * bHigh) >>> 16) +
+ // 32-bit result, result bits 16-47, take top 16 as our bottom 16
+ ((aHigh * bLow) >>> 16);
+
+ // Carry. Note that we actually have up to *two* carries due to addition of
+ // three terms.
+ while (productLow >= 0x100000000) {
+ productLow -= 0x100000000;
+ productHigh += 1;
+ }
+
+ return new jspb.arith.UInt64(productLow >>> 0, productHigh >>> 0);
+};
+
+
+/**
+ * Multiply this number by a 32-bit number, producing a 96-bit number, then
+ * truncate the top 32 bits.
+ * @param {number} a The multiplier.
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.prototype.mul = function(a) {
+ // Produce two parts: at bits 0-63, and 32-95.
+ var lo = jspb.arith.UInt64.mul32x32(this.lo, a);
+ var hi = jspb.arith.UInt64.mul32x32(this.hi, a);
+ // Left-shift hi by 32 bits, truncating its top bits. The parts will then be
+ // aligned for addition.
+ hi.hi = hi.lo;
+ hi.lo = 0;
+ return lo.add(hi);
+};
+
+
+/**
+ * Divide a 64-bit number by a 32-bit number to produce a
+ * 64-bit quotient and a 32-bit remainder.
+ * @param {number} _divisor
+ * @return {Array.<jspb.arith.UInt64>} array of [quotient, remainder],
+ * unless divisor is 0, in which case an empty array is returned.
+ */
+jspb.arith.UInt64.prototype.div = function(_divisor) {
+ if (_divisor == 0) {
+ return [];
+ }
+
+ // We perform long division using a radix-2 algorithm, for simplicity (i.e.,
+ // one bit at a time). TODO: optimize to a radix-2^32 algorithm, taking care
+ // to get the variable shifts right.
+ var quotient = new jspb.arith.UInt64(0, 0);
+ var remainder = new jspb.arith.UInt64(this.lo, this.hi);
+ var divisor = new jspb.arith.UInt64(_divisor, 0);
+ var unit = new jspb.arith.UInt64(1, 0);
+
+ // Left-shift the divisor and unit until the high bit of divisor is set.
+ while (!divisor.msb()) {
+ divisor = divisor.leftShift();
+ unit = unit.leftShift();
+ }
+
+ // Perform long division one bit at a time.
+ while (!unit.zero()) {
+ // If divisor < remainder, add unit to quotient and subtract divisor from
+ // remainder.
+ if (divisor.cmp(remainder) <= 0) {
+ quotient = quotient.add(unit);
+ remainder = remainder.sub(divisor);
+ }
+ // Right-shift the divisor and unit.
+ divisor = divisor.rightShift();
+ unit = unit.rightShift();
+ }
+
+ return [quotient, remainder];
+};
+
+
+/**
+ * Convert a 64-bit number to a string.
+ * @return {string}
+ * @override
+ */
+jspb.arith.UInt64.prototype.toString = function() {
+ var result = '';
+ var num = this;
+ while (!num.zero()) {
+ var divResult = num.div(10);
+ var quotient = divResult[0], remainder = divResult[1];
+ result = remainder.lo + result;
+ num = quotient;
+ }
+ if (result == '') {
+ result = '0';
+ }
+ return result;
+};
+
+
+/**
+ * Parse a string into a 64-bit number. Returns `null` on a parse error.
+ * @param {string} s
+ * @return {?jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.fromString = function(s) {
+ var result = new jspb.arith.UInt64(0, 0);
+ // optimization: reuse this instance for each digit.
+ var digit64 = new jspb.arith.UInt64(0, 0);
+ for (var i = 0; i < s.length; i++) {
+ if (s[i] < '0' || s[i] > '9') {
+ return null;
+ }
+ var digit = parseInt(s[i], 10);
+ digit64.lo = digit;
+ result = result.mul(10).add(digit64);
+ }
+ return result;
+};
+
+
+/**
+ * Make a copy of the uint64.
+ * @return {!jspb.arith.UInt64}
+ */
+jspb.arith.UInt64.prototype.clone = function() {
+ return new jspb.arith.UInt64(this.lo, this.hi);
+};
+
+
+/**
+ * Int64 is like UInt64, but modifies string conversions to interpret the stored
+ * 64-bit value as a twos-complement-signed integer. It does *not* support the
+ * full range of operations that UInt64 does: only add, subtract, and string
+ * conversions.
+ *
+ * N.B. that multiply and divide routines are *NOT* supported. They will throw
+ * exceptions. (They are not necessary to implement string conversions, which
+ * are the only operations we really need in jspb.)
+ *
+ * @param {number} lo The low 32 bits.
+ * @param {number} hi The high 32 bits.
+ * @constructor
+ */
+jspb.arith.Int64 = function(lo, hi) {
+ /**
+ * The low 32 bits.
+ * @public {number}
+ */
+ this.lo = lo;
+ /**
+ * The high 32 bits.
+ * @public {number}
+ */
+ this.hi = hi;
+};
+
+
+/**
+ * Add two 64-bit numbers to produce a 64-bit number.
+ * @param {!jspb.arith.Int64} other
+ * @return {!jspb.arith.Int64}
+ */
+jspb.arith.Int64.prototype.add = function(other) {
+ var lo = ((this.lo + other.lo) & 0xffffffff) >>> 0;
+ var hi =
+ (((this.hi + other.hi) & 0xffffffff) >>> 0) +
+ (((this.lo + other.lo) >= 0x100000000) ? 1 : 0);
+ return new jspb.arith.Int64(lo >>> 0, hi >>> 0);
+};
+
+
+/**
+ * Subtract two 64-bit numbers to produce a 64-bit number.
+ * @param {!jspb.arith.Int64} other
+ * @return {!jspb.arith.Int64}
+ */
+jspb.arith.Int64.prototype.sub = function(other) {
+ var lo = ((this.lo - other.lo) & 0xffffffff) >>> 0;
+ var hi =
+ (((this.hi - other.hi) & 0xffffffff) >>> 0) -
+ (((this.lo - other.lo) < 0) ? 1 : 0);
+ return new jspb.arith.Int64(lo >>> 0, hi >>> 0);
+};
+
+
+/**
+ * Make a copy of the int64.
+ * @return {!jspb.arith.Int64}
+ */
+jspb.arith.Int64.prototype.clone = function() {
+ return new jspb.arith.Int64(this.lo, this.hi);
+};
+
+
+/**
+ * Convert a 64-bit number to a string.
+ * @return {string}
+ * @override
+ */
+jspb.arith.Int64.prototype.toString = function() {
+ // If the number is negative, find its twos-complement inverse.
+ var sign = (this.hi & 0x80000000) != 0;
+ var num = new jspb.arith.UInt64(this.lo, this.hi);
+ if (sign) {
+ num = new jspb.arith.UInt64(0, 0).sub(num);
+ }
+ return (sign ? '-' : '') + num.toString();
+};
+
+
+/**
+ * Parse a string into a 64-bit number. Returns `null` on a parse error.
+ * @param {string} s
+ * @return {?jspb.arith.Int64}
+ */
+jspb.arith.Int64.fromString = function(s) {
+ var hasNegative = (s.length > 0 && s[0] == '-');
+ if (hasNegative) {
+ s = s.substring(1);
+ }
+ var num = jspb.arith.UInt64.fromString(s);
+ if (num === null) {
+ return null;
+ }
+ if (hasNegative) {
+ num = new jspb.arith.UInt64(0, 0).sub(num);
+ }
+ return new jspb.arith.Int64(num.lo, num.hi);
+};
diff --git a/js/binary/arith_test.js b/js/binary/arith_test.js
new file mode 100644
index 00000000..89796bf7
--- /dev/null
+++ b/js/binary/arith_test.js
@@ -0,0 +1,355 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for Int64-manipulation functions.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author cfallin@google.com (Chris Fallin)
+ */
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.arith.Int64');
+goog.require('jspb.arith.UInt64');
+
+
+describe('binaryArithTest', function() {
+ /**
+ * Tests comparison operations.
+ */
+ it('testCompare', function() {
+ var a = new jspb.arith.UInt64(1234, 5678);
+ var b = new jspb.arith.UInt64(1234, 5678);
+ assertEquals(a.cmp(b), 0);
+ assertEquals(b.cmp(a), 0);
+ b.lo -= 1;
+ assertEquals(a.cmp(b), 1);
+ assertEquals(b.cmp(a), -1);
+ b.lo += 2;
+ assertEquals(a.cmp(b), -1);
+ assertEquals(b.cmp(a), 1);
+ b.lo = a.lo;
+ b.hi = a.hi - 1;
+ assertEquals(a.cmp(b), 1);
+ assertEquals(b.cmp(a), -1);
+
+ assertEquals(a.zero(), false);
+ assertEquals(a.msb(), false);
+ assertEquals(a.lsb(), false);
+ a.hi = 0;
+ a.lo = 0;
+ assertEquals(a.zero(), true);
+ a.hi = 0x80000000;
+ assertEquals(a.zero(), false);
+ assertEquals(a.msb(), true);
+ a.lo = 0x00000001;
+ assertEquals(a.lsb(), true);
+ });
+
+
+ /**
+ * Tests shifts.
+ */
+ it('testShifts', function() {
+ var a = new jspb.arith.UInt64(1, 0);
+ assertEquals(a.lo, 1);
+ assertEquals(a.hi, 0);
+ var orig = a;
+ a = a.leftShift();
+ assertEquals(orig.lo, 1); // original unmodified.
+ assertEquals(orig.hi, 0);
+ assertEquals(a.lo, 2);
+ assertEquals(a.hi, 0);
+ a = a.leftShift();
+ assertEquals(a.lo, 4);
+ assertEquals(a.hi, 0);
+ for (var i = 0; i < 29; i++) {
+ a = a.leftShift();
+ }
+ assertEquals(a.lo, 0x80000000);
+ assertEquals(a.hi, 0);
+ a = a.leftShift();
+ assertEquals(a.lo, 0);
+ assertEquals(a.hi, 1);
+ a = a.leftShift();
+ assertEquals(a.lo, 0);
+ assertEquals(a.hi, 2);
+ a = a.rightShift();
+ a = a.rightShift();
+ assertEquals(a.lo, 0x80000000);
+ assertEquals(a.hi, 0);
+ a = a.rightShift();
+ assertEquals(a.lo, 0x40000000);
+ assertEquals(a.hi, 0);
+ });
+
+
+ /**
+ * Tests additions.
+ */
+ it('testAdd', function() {
+ var a = new jspb.arith.UInt64(/* lo = */ 0x89abcdef,
+ /* hi = */ 0x01234567);
+ var b = new jspb.arith.UInt64(/* lo = */ 0xff52ab91,
+ /* hi = */ 0x92fa2123);
+ // Addition with carry.
+ var c = a.add(b);
+ assertEquals(a.lo, 0x89abcdef); // originals unmodified.
+ assertEquals(a.hi, 0x01234567);
+ assertEquals(b.lo, 0xff52ab91);
+ assertEquals(b.hi, 0x92fa2123);
+ assertEquals(c.lo, 0x88fe7980);
+ assertEquals(c.hi, 0x941d668b);
+
+ // Simple addition without carry.
+ a.lo = 2;
+ a.hi = 0;
+ b.lo = 3;
+ b.hi = 0;
+ c = a.add(b);
+ assertEquals(c.lo, 5);
+ assertEquals(c.hi, 0);
+ });
+
+
+ /**
+ * Test subtractions.
+ */
+ it('testSub', function() {
+ var kLength = 10;
+ var hiValues = [0x1682ef32,
+ 0x583902f7,
+ 0xb62f5955,
+ 0x6ea99bbf,
+ 0x25a39c20,
+ 0x0700a08b,
+ 0x00f7304d,
+ 0x91a5b5af,
+ 0x89077fd2,
+ 0xe09e347c];
+ var loValues = [0xe1538b18,
+ 0xbeacd556,
+ 0x74100758,
+ 0x96e3cb26,
+ 0x56c37c3f,
+ 0xe00b3f7d,
+ 0x859f25d7,
+ 0xc2ee614a,
+ 0xe1d21cd7,
+ 0x30aae6a4];
+ for (var i = 0; i < kLength; i++) {
+ for (var j = 0; j < kLength; j++) {
+ var a = new jspb.arith.UInt64(loValues[i], hiValues[j]);
+ var b = new jspb.arith.UInt64(loValues[j], hiValues[i]);
+ var c = a.add(b).sub(b);
+ assertEquals(c.hi, a.hi);
+ assertEquals(c.lo, a.lo);
+ }
+ }
+ });
+
+
+ /**
+ * Tests 32-by-32 multiplication.
+ */
+ it('testMul32x32', function() {
+ var testData = [
+ // a b low(a*b) high(a*b)
+ [0xc0abe2f8, 0x1607898a, 0x5de711b0, 0x109471b8],
+ [0x915eb3cb, 0x4fb66d0e, 0xbd0d441a, 0x2d43d0bc],
+ [0xfe4efe70, 0x80b48c37, 0xbcddea10, 0x7fdada0c],
+ [0xe222fd4a, 0xe43d524a, 0xd5e0eb64, 0xc99d549c],
+ [0xd171f469, 0xb94ebd01, 0x4be17969, 0x979bc4fa],
+ [0x829cc1df, 0xe2598b38, 0xf4157dc8, 0x737c12ad],
+ [0xf10c3767, 0x8382881e, 0x942b3612, 0x7bd428b8],
+ [0xb0f6dd24, 0x232597e1, 0x079c98a4, 0x184bbce7],
+ [0xfcdb05a7, 0x902f55bc, 0x636199a4, 0x8e69f412],
+ [0x0dd0bfa9, 0x916e27b1, 0x6e2542d9, 0x07d92e65]
+ ];
+
+ for (var i = 0; i < testData.length; i++) {
+ var a = testData[i][0] >>> 0;
+ var b = testData[i][1] >>> 0;
+ var cLow = testData[i][2] >>> 0;
+ var cHigh = testData[i][3] >>> 0;
+ var c = jspb.arith.UInt64.mul32x32(a, b);
+ assertEquals(c.lo, cLow);
+ assertEquals(c.hi, cHigh);
+ }
+ });
+
+
+ /**
+ * Tests 64-by-32 multiplication.
+ */
+ it('testMul', function() {
+ // 64x32 bits produces 96 bits of product. The multiplication function under
+ // test truncates the top 32 bits, so we compare against a 64-bit expected
+ // product.
+ var testData = [
+ // low(a) high(a) low(a*b) high(a*b)
+ [0xec10955b, 0x360eb168, 0x4b7f3f5b, 0xbfcb7c59, 0x9517da5f],
+ [0x42b000fc, 0x9d101642, 0x6fa1ab72, 0x2584c438, 0x6a9e6d2b],
+ [0xf42d4fb4, 0xae366403, 0xa65a1000, 0x92434000, 0x1ff978df],
+ [0x17e2f56b, 0x25487693, 0xf13f98c7, 0x73794e2d, 0xa96b0c6a],
+ [0x492f241f, 0x76c0eb67, 0x7377ac44, 0xd4336c3c, 0xfc4b1ebe],
+ [0xd6b92321, 0xe184fa48, 0xd6e76904, 0x93141584, 0xcbf44da1],
+ [0x4bf007ea, 0x968c0a9e, 0xf5e4026a, 0x4fdb1ae4, 0x61b9fb7d],
+ [0x10a83be7, 0x2d685ba6, 0xc9e5fb7f, 0x2ad43499, 0x3742473d],
+ [0x2f261829, 0x1aca681a, 0x3d3494e3, 0x8213205b, 0x283719f8],
+ [0xe4f2ce21, 0x2e74b7bd, 0xd801b38b, 0xbc17feeb, 0xc6c44e0f]
+ ];
+
+ for (var i = 0; i < testData.length; i++) {
+ var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]);
+ var prod = a.mul(testData[i][2]);
+ assertEquals(prod.lo, testData[i][3]);
+ assertEquals(prod.hi, testData[i][4]);
+ }
+ });
+
+
+ /**
+ * Tests 64-div-by-32 division.
+ */
+ it('testDiv', function() {
+ // Compute a/b, yielding quot = a/b and rem = a%b.
+ var testData = [
+ // --- divisors in (0, 2^32-1) to test full divisor range
+ // low(a) high(a) b low(quot) high(quot) rem
+ [0x712443f1, 0xe85cefcc, 0xc1a7050b, 0x332c79ad, 0x00000001, 0x92ffa882],
+ [0x11912915, 0xb2699eb5, 0x30467cbe, 0xb21b4be4, 0x00000003, 0x283465dd],
+ [0x0d917982, 0x201f2a6e, 0x3f35bf03, 0x8217c8e4, 0x00000000, 0x153402d6],
+ [0xa072c108, 0x74020c96, 0xc60568fd, 0x95f9613e, 0x00000000, 0x3f4676c2],
+ [0xd845d5d8, 0xcdd235c4, 0x20426475, 0x6154e78b, 0x00000006, 0x202fb751],
+ [0xa4dbf71f, 0x9e90465e, 0xf08e022f, 0xa8be947f, 0x00000000, 0xbe43b5ce],
+ [0x3dbe627f, 0xa791f4b9, 0x28a5bd89, 0x1f5dfe93, 0x00000004, 0x02bf9ed4],
+ [0x5c1c53ee, 0xccf5102e, 0x198576e7, 0x07e3ae31, 0x00000008, 0x02ea8fb7],
+ [0xfef1e581, 0x04714067, 0xca6540c1, 0x059e73ec, 0x00000000, 0x31658095],
+ [0x1e2dd90c, 0x13dd6667, 0x8b2184c3, 0x248d1a42, 0x00000000, 0x4ca6d0c6],
+ // --- divisors in (0, 2^16-1) to test larger quotient high-words
+ // low(a) high(a) b low(quot) high(quot) rem
+ [0x86722b47, 0x2cd57c9a, 0x00003123, 0x2ae41b7a, 0x0000e995, 0x00000f99],
+ [0x1dd7884c, 0xf5e839bc, 0x00009eeb, 0x5c886242, 0x00018c21, 0x000099b6],
+ [0x5c53d625, 0x899fc7e5, 0x000087d7, 0xd625007a, 0x0001035c, 0x000019af],
+ [0x6932d932, 0x9d0a5488, 0x000051fb, 0x9d976143, 0x0001ea63, 0x00004981],
+ [0x4d18bb85, 0x0c92fb31, 0x00001d9f, 0x03265ab4, 0x00006cac, 0x000001b9],
+ [0xbe756768, 0xdea67ccb, 0x00008a03, 0x58add442, 0x00019cff, 0x000056a2],
+ [0xe2466f9a, 0x2521f114, 0x0000c350, 0xa0c0860d, 0x000030ab, 0x0000a48a],
+ [0xf00ddad1, 0xe2f5446a, 0x00002cfc, 0x762697a6, 0x00050b96, 0x00000b69],
+ [0xa879152a, 0x0a70e0a5, 0x00007cdf, 0xb44151b3, 0x00001567, 0x0000363d],
+ [0x7179a74c, 0x46083fff, 0x0000253c, 0x4d39ba6e, 0x0001e17f, 0x00000f84]
+ ];
+
+ for (var i = 0; i < testData.length; i++) {
+ var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]);
+ var result = a.div(testData[i][2]);
+ var quotient = result[0];
+ var remainder = result[1];
+ assertEquals(quotient.lo, testData[i][3]);
+ assertEquals(quotient.hi, testData[i][4]);
+ assertEquals(remainder.lo, testData[i][5]);
+ }
+ });
+
+
+ /**
+ * Tests .toString() and .fromString().
+ */
+ it('testStrings', function() {
+ var testData = [
+ [0x5e84c935, 0xcae33d0e, '14619595947299359029'],
+ [0x62b3b8b8, 0x93480544, '10612738313170434232'],
+ [0x319bfb13, 0xc01c4172, '13843011313344445203'],
+ [0x5b8a65fb, 0xa5885b31, '11927883880638080507'],
+ [0x6bdb80f1, 0xb0d1b16b, '12741159895737008369'],
+ [0x4b82b442, 0x2e0d8c97, '3318463081876730946'],
+ [0x780d5208, 0x7d76752c, '9040542135845999112'],
+ [0x2e46800f, 0x0993778d, '690026616168284175'],
+ [0xf00a7e32, 0xcd8e3931, '14811839111111540274'],
+ [0x1baeccd6, 0x923048c4, '10533999535534820566'],
+ [0x03669d29, 0xbff3ab72, '13831587386756603177'],
+ [0x2526073e, 0x01affc81, '121593346566522686'],
+ [0xc24244e0, 0xd7f40d0e, '15561076969511732448'],
+ [0xc56a341e, 0xa68b66a7, '12000798502816461854'],
+ [0x8738d64d, 0xbfe78604, '13828168534871037517'],
+ [0x5baff03b, 0xd7572aea, '15516918227177304123'],
+ [0x4a843d8a, 0x864e132b, '9677693725920476554'],
+ [0x25b4e94d, 0x22b54dc6, '2500990681505655117'],
+ [0x6bbe664b, 0x55a5cc0e, '6171563226690381387'],
+ [0xee916c81, 0xb00aabb3, '12685140089732426881']
+ ];
+
+ for (var i = 0; i < testData.length; i++) {
+ var a = new jspb.arith.UInt64(testData[i][0], testData[i][1]);
+ var roundtrip = jspb.arith.UInt64.fromString(a.toString());
+ assertEquals(roundtrip.lo, a.lo);
+ assertEquals(roundtrip.hi, a.hi);
+ assertEquals(a.toString(), testData[i][2]);
+ }
+ });
+
+
+ /**
+ * Tests signed Int64s. These are built on UInt64s, so we only need to test
+ * the explicit overrides: .toString() and .fromString().
+ */
+ it('testSignedInt64', function() {
+ var testStrings = [
+ '-7847499644178593666',
+ '3771946501229139523',
+ '2872856549054995060',
+ '-5780049594274350904',
+ '3383785956695105201',
+ '2973055184857072610',
+ '-3879428459215627206',
+ '4589812431064156631',
+ '8484075557333689940',
+ '1075325817098092407',
+ '-4346697501012292314',
+ '2488620459718316637',
+ '6112655187423520672',
+ '-3655278273928612104',
+ '3439154019435803196',
+ '1004112478843763757',
+ '-6587790776614368413',
+ '664320065099714586',
+ '4760412909973292912',
+ '-7911903989602274672'
+ ];
+
+ for (var i = 0; i < testStrings.length; i++) {
+ var roundtrip =
+ jspb.arith.Int64.fromString(testStrings[i]).toString();
+ assertEquals(roundtrip, testStrings[i]);
+ }
+ });
+});
diff --git a/js/binary/constants.js b/js/binary/constants.js
new file mode 100644
index 00000000..a976e0b6
--- /dev/null
+++ b/js/binary/constants.js
@@ -0,0 +1,320 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview This file contains constants and typedefs used by
+ * jspb.BinaryReader and BinaryWriter.
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.provide('jspb.AnyFieldType');
+goog.provide('jspb.BinaryConstants');
+goog.provide('jspb.BinaryMessage');
+goog.provide('jspb.BuilderFunction');
+goog.provide('jspb.ByteSource');
+goog.provide('jspb.ClonerFunction');
+goog.provide('jspb.ConstBinaryMessage');
+goog.provide('jspb.ReaderFunction');
+goog.provide('jspb.RecyclerFunction');
+goog.provide('jspb.WriterFunction');
+
+goog.forwardDeclare('jspb.Message');
+goog.forwardDeclare('jsproto.BinaryExtension');
+
+
+
+/**
+ * Base interface class for all const messages. Does __not__ define any
+ * methods, as doing so on a widely-used interface defeats dead-code
+ * elimination.
+ * @interface
+ */
+jspb.ConstBinaryMessage = function() {};
+
+
+
+/**
+ * Base interface class for all messages. Does __not__ define any methods, as
+ * doing so on a widely-used interface defeats dead-code elimination.
+ * @interface
+ * @extends {jspb.ConstBinaryMessage}
+ */
+jspb.BinaryMessage = function() {};
+
+
+/**
+ * The types convertible to Uint8Arrays. Strings are assumed to be
+ * base64-encoded.
+ * @typedef {ArrayBuffer|Uint8Array|Array<number>|string}
+ */
+jspb.ByteSource;
+
+
+/**
+ * A field in jspb can be a scalar, a block of bytes, another proto, or an
+ * array of any of the above.
+ * @typedef {boolean|number|string|Uint8Array|
+ jspb.BinaryMessage|jsproto.BinaryExtension|
+ Array<jspb.AnyFieldType>}
+ */
+jspb.AnyFieldType;
+
+
+/**
+ * A builder function creates an instance of a message object.
+ * @typedef {function():!jspb.BinaryMessage}
+ */
+jspb.BuilderFunction;
+
+
+/**
+ * A cloner function creates a deep copy of a message object.
+ * @typedef {function(jspb.ConstBinaryMessage):jspb.BinaryMessage}
+ */
+jspb.ClonerFunction;
+
+
+/**
+ * A recycler function destroys an instance of a message object.
+ * @typedef {function(!jspb.BinaryMessage):void}
+ */
+jspb.RecyclerFunction;
+
+
+/**
+ * A reader function initializes a message using data from a BinaryReader.
+ * @typedef {function(!jspb.BinaryMessage, !jspb.BinaryReader):void}
+ */
+jspb.ReaderFunction;
+
+
+/**
+ * A writer function serializes a message to a BinaryWriter.
+ * @typedef {!function(!jspb.Message, !jspb.BinaryWriter):void |
+ * !function(!jspb.ConstBinaryMessage, !jspb.BinaryWriter):void}
+ */
+jspb.WriterFunction;
+
+
+/**
+ * Field type codes, taken from proto2/public/wire_format_lite.h.
+ * @enum {number}
+ */
+jspb.BinaryConstants.FieldType = {
+ INVALID: -1,
+ DOUBLE: 1,
+ FLOAT: 2,
+ INT64: 3,
+ UINT64: 4,
+ INT32: 5,
+ FIXED64: 6,
+ FIXED32: 7,
+ BOOL: 8,
+ STRING: 9,
+ GROUP: 10,
+ MESSAGE: 11,
+ BYTES: 12,
+ UINT32: 13,
+ ENUM: 14,
+ SFIXED32: 15,
+ SFIXED64: 16,
+ SINT32: 17,
+ SINT64: 18,
+
+ // Extended types for Javascript
+
+ FHASH64: 30, // 64-bit hash string, fixed-length encoding.
+ VHASH64: 31 // 64-bit hash string, varint encoding.
+};
+
+
+/**
+ * Wire-format type codes, taken from proto2/public/wire_format_lite.h.
+ * @enum {number}
+ */
+jspb.BinaryConstants.WireType = {
+ INVALID: -1,
+ VARINT: 0,
+ FIXED64: 1,
+ DELIMITED: 2,
+ START_GROUP: 3,
+ END_GROUP: 4,
+ FIXED32: 5
+};
+
+
+/**
+ * Translates field type to wire type.
+ * @param {jspb.BinaryConstants.FieldType} fieldType
+ * @return {jspb.BinaryConstants.WireType}
+ */
+jspb.BinaryConstants.FieldTypeToWireType = function(fieldType) {
+ var fieldTypes = jspb.BinaryConstants.FieldType;
+ var wireTypes = jspb.BinaryConstants.WireType;
+ switch (fieldType) {
+ case fieldTypes.INT32:
+ case fieldTypes.INT64:
+ case fieldTypes.UINT32:
+ case fieldTypes.UINT64:
+ case fieldTypes.SINT32:
+ case fieldTypes.SINT64:
+ case fieldTypes.BOOL:
+ case fieldTypes.ENUM:
+ case fieldTypes.VHASH64:
+ return wireTypes.VARINT;
+
+ case fieldTypes.DOUBLE:
+ case fieldTypes.FIXED64:
+ case fieldTypes.SFIXED64:
+ case fieldTypes.FHASH64:
+ return wireTypes.FIXED64;
+
+ case fieldTypes.STRING:
+ case fieldTypes.MESSAGE:
+ case fieldTypes.BYTES:
+ return wireTypes.DELIMITED;
+
+ case fieldTypes.FLOAT:
+ case fieldTypes.FIXED32:
+ case fieldTypes.SFIXED32:
+ return wireTypes.FIXED32;
+
+ case fieldTypes.INVALID:
+ case fieldTypes.GROUP:
+ default:
+ return wireTypes.INVALID;
+ }
+};
+
+
+/**
+ * Flag to indicate a missing field.
+ * @const {number}
+ */
+jspb.BinaryConstants.INVALID_FIELD_NUMBER = -1;
+
+
+/**
+ * The smallest denormal float32 value.
+ * @const {number}
+ */
+jspb.BinaryConstants.FLOAT32_EPS = 1.401298464324817e-45;
+
+
+/**
+ * The smallest normal float64 value.
+ * @const {number}
+ */
+jspb.BinaryConstants.FLOAT32_MIN = 1.1754943508222875e-38;
+
+
+/**
+ * The largest finite float32 value.
+ * @const {number}
+ */
+jspb.BinaryConstants.FLOAT32_MAX = 3.4028234663852886e+38;
+
+
+/**
+ * The smallest denormal float64 value.
+ * @const {number}
+ */
+jspb.BinaryConstants.FLOAT64_EPS = 5e-324;
+
+
+/**
+ * The smallest normal float64 value.
+ * @const {number}
+ */
+jspb.BinaryConstants.FLOAT64_MIN = 2.2250738585072014e-308;
+
+
+/**
+ * The largest finite float64 value.
+ * @const {number}
+ */
+jspb.BinaryConstants.FLOAT64_MAX = 1.7976931348623157e+308;
+
+
+/**
+ * Convenience constant equal to 2^20.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_20 = 1048576;
+
+
+/**
+ * Convenience constant equal to 2^23.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_23 = 8388608;
+
+
+/**
+ * Convenience constant equal to 2^31.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_31 = 2147483648;
+
+
+/**
+ * Convenience constant equal to 2^32.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_32 = 4294967296;
+
+
+/**
+ * Convenience constant equal to 2^52.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_52 = 4503599627370496;
+
+
+/**
+ * Convenience constant equal to 2^63.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_63 = 9223372036854775808;
+
+
+/**
+ * Convenience constant equal to 2^64.
+ * @const {number}
+ */
+jspb.BinaryConstants.TWO_TO_64 = 18446744073709551616;
+
+
+/**
+ * Eight-character string of zeros, used as the default 64-bit hash value.
+ * @const {string}
+ */
+jspb.BinaryConstants.ZERO_HASH = '\0\0\0\0\0\0\0\0';
diff --git a/js/binary/decoder.js b/js/binary/decoder.js
new file mode 100644
index 00000000..9004eff0
--- /dev/null
+++ b/js/binary/decoder.js
@@ -0,0 +1,1005 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview This file contains utilities for decoding primitive values
+ * (signed and unsigned integers, varints, booleans, enums, hashes, strings,
+ * and raw bytes) embedded in Uint8Arrays into their corresponding Javascript
+ * types.
+ *
+ * Major caveat - Javascript is unable to accurately represent integers larger
+ * than 2^53 due to its use of a double-precision floating point format or all
+ * numbers. If you need to guarantee that 64-bit values survive with all bits
+ * intact, you _must_ read them using one of the Hash64 methods, which return
+ * an 8-character string.
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.provide('jspb.BinaryDecoder');
+goog.provide('jspb.BinaryIterator');
+
+goog.require('goog.asserts');
+goog.require('jspb.utils');
+
+
+
+/**
+ * Simple helper class for traversing the contents of repeated scalar fields.
+ * that may or may not have been packed into a wire-format blob.
+ * @param {?jspb.BinaryDecoder=} opt_decoder
+ * @param {?function(this:jspb.BinaryDecoder):(number|boolean|string)=}
+ * opt_next The decoder method to use for next().
+ * @param {?Array.<number|boolean|string>=} opt_elements
+ * @constructor
+ * @struct
+ */
+jspb.BinaryIterator = function(opt_decoder, opt_next, opt_elements) {
+ /** @private {jspb.BinaryDecoder} */
+ this.decoder_ = null;
+
+ /**
+ * The BinaryDecoder member function used when iterating over packed data.
+ * @private {?function(this:jspb.BinaryDecoder):(number|boolean|string)}
+ */
+ this.nextMethod_ = null;
+
+ /** @private {Array.<number>} */
+ this.elements_ = null;
+
+ /** @private {number} */
+ this.cursor_ = 0;
+
+ /** @private {number|boolean|string|null} */
+ this.nextValue_ = null;
+
+ /** @private {boolean} */
+ this.atEnd_ = true;
+
+ this.init_(opt_decoder, opt_next, opt_elements);
+};
+
+
+/**
+ * @param {?jspb.BinaryDecoder=} opt_decoder
+ * @param {?function(this:jspb.BinaryDecoder):(number|boolean|string)=}
+ * opt_next The decoder method to use for next().
+ * @param {?Array.<number|boolean|string>=} opt_elements
+ * @private
+ */
+jspb.BinaryIterator.prototype.init_ =
+ function(opt_decoder, opt_next, opt_elements) {
+ if (opt_decoder && opt_next) {
+ this.decoder_ = opt_decoder;
+ this.nextMethod_ = opt_next;
+ }
+ this.elements_ = opt_elements ? opt_elements : null;
+ this.cursor_ = 0;
+ this.nextValue_ = null;
+ this.atEnd_ = !this.decoder_ && !this.elements_;
+
+ this.next();
+};
+
+
+/**
+ * Global pool of BinaryIterator instances.
+ * @private {!Array.<!jspb.BinaryIterator>}
+ */
+jspb.BinaryIterator.instanceCache_ = [];
+
+
+/**
+ * Allocates a BinaryIterator from the cache, creating a new one if the cache
+ * is empty.
+ * @param {?jspb.BinaryDecoder=} opt_decoder
+ * @param {?function(this:jspb.BinaryDecoder):(number|boolean|string)=}
+ * opt_next The decoder method to use for next().
+ * @param {?Array.<number|boolean|string>=} opt_elements
+ * @return {!jspb.BinaryIterator}
+ */
+jspb.BinaryIterator.alloc = function(opt_decoder, opt_next, opt_elements) {
+ if (jspb.BinaryIterator.instanceCache_.length) {
+ var iterator = jspb.BinaryIterator.instanceCache_.pop();
+ iterator.init_(opt_decoder, opt_next, opt_elements);
+ return iterator;
+ } else {
+ return new jspb.BinaryIterator(opt_decoder, opt_next, opt_elements);
+ }
+};
+
+
+/**
+ * Puts this instance back in the instance cache.
+ */
+jspb.BinaryIterator.prototype.free = function() {
+ this.clear();
+ if (jspb.BinaryIterator.instanceCache_.length < 100) {
+ jspb.BinaryIterator.instanceCache_.push(this);
+ }
+};
+
+
+/**
+ * Clears the iterator.
+ */
+jspb.BinaryIterator.prototype.clear = function() {
+ if (this.decoder_) {
+ this.decoder_.free();
+ }
+ this.decoder_ = null;
+ this.nextMethod_ = null;
+ this.elements_ = null;
+ this.cursor_ = 0;
+ this.nextValue_ = null;
+ this.atEnd_ = true;
+};
+
+
+/**
+ * Returns the element at the iterator, or null if the iterator is invalid or
+ * past the end of the decoder/array.
+ * @return {number|boolean|string|null}
+ */
+jspb.BinaryIterator.prototype.get = function() {
+ return this.nextValue_;
+};
+
+
+/**
+ * Returns true if the iterator is at the end of the decoder/array.
+ * @return {boolean}
+ */
+jspb.BinaryIterator.prototype.atEnd = function() {
+ return this.atEnd_;
+};
+
+
+/**
+ * Returns the element at the iterator and steps to the next element,
+ * equivalent to '*pointer++' in C.
+ * @return {number|boolean|string|null}
+ */
+jspb.BinaryIterator.prototype.next = function() {
+ var lastValue = this.nextValue_;
+ if (this.decoder_) {
+ if (this.decoder_.atEnd()) {
+ this.nextValue_ = null;
+ this.atEnd_ = true;
+ } else {
+ this.nextValue_ = this.nextMethod_.call(this.decoder_);
+ }
+ } else if (this.elements_) {
+ if (this.cursor_ == this.elements_.length) {
+ this.nextValue_ = null;
+ this.atEnd_ = true;
+ } else {
+ this.nextValue_ = this.elements_[this.cursor_++];
+ }
+ }
+ return lastValue;
+};
+
+
+
+/**
+ * BinaryDecoder implements the decoders for all the wire types specified in
+ * https://developers.google.com/protocol-buffers/docs/encoding.
+ *
+ * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from.
+ * @param {number=} opt_start The optional offset to start reading at.
+ * @param {number=} opt_length The optional length of the block to read -
+ * we'll throw an assertion if we go off the end of the block.
+ * @constructor
+ * @struct
+ */
+jspb.BinaryDecoder = function(opt_bytes, opt_start, opt_length) {
+ /**
+ * Typed byte-wise view of the source buffer.
+ * @private {Uint8Array}
+ */
+ this.bytes_ = null;
+
+ /**
+ * Start point of the block to read.
+ * @private {number}
+ */
+ this.start_ = 0;
+
+ /**
+ * End point of the block to read.
+ * @private {number}
+ */
+ this.end_ = 0;
+
+ /**
+ * Current read location in bytes_.
+ * @private {number}
+ */
+ this.cursor_ = 0;
+
+ /**
+ * Temporary storage for the low 32 bits of 64-bit data types that we're
+ * decoding.
+ * @private {number}
+ */
+ this.tempLow_ = 0;
+
+ /**
+ * Temporary storage for the high 32 bits of 64-bit data types that we're
+ * decoding.
+ * @private {number}
+ */
+ this.tempHigh_ = 0;
+
+ /**
+ * Set to true if this decoder encountered an error due to corrupt data.
+ * @private {boolean}
+ */
+ this.error_ = false;
+
+ if (opt_bytes) {
+ this.setBlock(opt_bytes, opt_start, opt_length);
+ }
+};
+
+
+/**
+ * Global pool of BinaryDecoder instances.
+ * @private {!Array.<!jspb.BinaryDecoder>}
+ */
+jspb.BinaryDecoder.instanceCache_ = [];
+
+
+/**
+ * Pops an instance off the instance cache, or creates one if the cache is
+ * empty.
+ * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from.
+ * @param {number=} opt_start The optional offset to start reading at.
+ * @param {number=} opt_length The optional length of the block to read -
+ * we'll throw an assertion if we go off the end of the block.
+ * @return {!jspb.BinaryDecoder}
+ */
+jspb.BinaryDecoder.alloc = function(opt_bytes, opt_start, opt_length) {
+ if (jspb.BinaryDecoder.instanceCache_.length) {
+ var newDecoder = jspb.BinaryDecoder.instanceCache_.pop();
+ if (opt_bytes) {
+ newDecoder.setBlock(opt_bytes, opt_start, opt_length);
+ }
+ return newDecoder;
+ } else {
+ return new jspb.BinaryDecoder(opt_bytes, opt_start, opt_length);
+ }
+};
+
+
+/**
+ * Puts this instance back in the instance cache.
+ */
+jspb.BinaryDecoder.prototype.free = function() {
+ this.clear();
+ if (jspb.BinaryDecoder.instanceCache_.length < 100) {
+ jspb.BinaryDecoder.instanceCache_.push(this);
+ }
+};
+
+
+/**
+ * Makes a copy of this decoder.
+ * @return {!jspb.BinaryDecoder}
+ */
+jspb.BinaryDecoder.prototype.clone = function() {
+ return jspb.BinaryDecoder.alloc(this.bytes_,
+ this.start_, this.end_ - this.start_);
+};
+
+
+/**
+ * Clears the decoder.
+ */
+jspb.BinaryDecoder.prototype.clear = function() {
+ this.bytes_ = null;
+ this.start_ = 0;
+ this.end_ = 0;
+ this.cursor_ = 0;
+ this.error_ = false;
+};
+
+
+/**
+ * Returns the raw buffer.
+ * @return {Uint8Array} The raw buffer.
+ */
+jspb.BinaryDecoder.prototype.getBuffer = function() {
+ return this.bytes_;
+};
+
+
+/**
+ * Changes the block of bytes we're decoding.
+ * @param {!jspb.ByteSource} data The bytes we're reading from.
+ * @param {number=} opt_start The optional offset to start reading at.
+ * @param {number=} opt_length The optional length of the block to read -
+ * we'll throw an assertion if we go off the end of the block.
+ */
+jspb.BinaryDecoder.prototype.setBlock =
+ function(data, opt_start, opt_length) {
+ this.bytes_ = jspb.utils.byteSourceToUint8Array(data);
+ this.start_ = goog.isDef(opt_start) ? opt_start : 0;
+ this.end_ =
+ goog.isDef(opt_length) ? this.start_ + opt_length : this.bytes_.length;
+ this.cursor_ = this.start_;
+};
+
+
+/**
+ * @return {number}
+ */
+jspb.BinaryDecoder.prototype.getEnd = function() {
+ return this.end_;
+};
+
+
+/**
+ * @param {number} end
+ */
+jspb.BinaryDecoder.prototype.setEnd = function(end) {
+ this.end_ = end;
+};
+
+
+/**
+ * Moves the read cursor back to the start of the block.
+ */
+jspb.BinaryDecoder.prototype.reset = function() {
+ this.cursor_ = this.start_;
+};
+
+
+/**
+ * Returns the internal read cursor.
+ * @return {number} The internal read cursor.
+ */
+jspb.BinaryDecoder.prototype.getCursor = function() {
+ return this.cursor_;
+};
+
+
+/**
+ * Returns the internal read cursor.
+ * @param {number} cursor The new cursor.
+ */
+jspb.BinaryDecoder.prototype.setCursor = function(cursor) {
+ this.cursor_ = cursor;
+};
+
+
+/**
+ * Advances the stream cursor by the given number of bytes.
+ * @param {number} count The number of bytes to advance by.
+ */
+jspb.BinaryDecoder.prototype.advance = function(count) {
+ this.cursor_ += count;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+};
+
+
+/**
+ * Returns true if this decoder is at the end of the block.
+ * @return {boolean}
+ */
+jspb.BinaryDecoder.prototype.atEnd = function() {
+ return this.cursor_ == this.end_;
+};
+
+
+/**
+ * Returns true if this decoder is at the end of the block.
+ * @return {boolean}
+ */
+jspb.BinaryDecoder.prototype.pastEnd = function() {
+ return this.cursor_ > this.end_;
+};
+
+
+/**
+ * Returns true if this decoder encountered an error due to corrupt data.
+ * @return {boolean}
+ */
+jspb.BinaryDecoder.prototype.getError = function() {
+ return this.error_ ||
+ (this.cursor_ < 0) ||
+ (this.cursor_ > this.end_);
+};
+
+
+/**
+ * Reads an unsigned varint from the binary stream and stores it as a split
+ * 64-bit integer. Since this does not convert the value to a number, no
+ * precision is lost.
+ *
+ * It's possible for an unsigned varint to be incorrectly encoded - more than
+ * 64 bits' worth of data could be present. If this happens, this method will
+ * throw an error.
+ *
+ * Decoding varints requires doing some funny base-128 math - for more
+ * details on the format, see
+ * https://developers.google.com/protocol-buffers/docs/encoding
+ *
+ * @private
+ */
+jspb.BinaryDecoder.prototype.readSplitVarint64_ = function() {
+ var temp;
+ var lowBits = 0;
+ var highBits = 0;
+
+ // Read the first four bytes of the varint, stopping at the terminator if we
+ // see it.
+ for (var i = 0; i < 4; i++) {
+ temp = this.bytes_[this.cursor_++];
+ lowBits |= (temp & 0x7F) << (i * 7);
+ if (temp < 128) {
+ this.tempLow_ = lowBits >>> 0;
+ this.tempHigh_ = 0;
+ return;
+ }
+ }
+
+ // Read the fifth byte, which straddles the low and high dwords.
+ temp = this.bytes_[this.cursor_++];
+ lowBits |= (temp & 0x7F) << 28;
+ highBits |= (temp & 0x7F) >> 4;
+ if (temp < 128) {
+ this.tempLow_ = lowBits >>> 0;
+ this.tempHigh_ = highBits >>> 0;
+ return;
+ }
+
+ // Read the sixth through tenth byte.
+ for (var i = 0; i < 5; i++) {
+ temp = this.bytes_[this.cursor_++];
+ highBits |= (temp & 0x7F) << (i * 7 + 3);
+ if (temp < 128) {
+ this.tempLow_ = lowBits >>> 0;
+ this.tempHigh_ = highBits >>> 0;
+ return;
+ }
+ }
+
+ // If we did not see the terminator, the encoding was invalid.
+ goog.asserts.fail('Failed to read varint, encoding is invalid.');
+ this.error_ = true;
+};
+
+
+/**
+ * Skips over a varint in the block without decoding it.
+ */
+jspb.BinaryDecoder.prototype.skipVarint = function() {
+ while (this.bytes_[this.cursor_] & 0x80) {
+ this.cursor_++;
+ }
+ this.cursor_++;
+};
+
+
+/**
+ * Skips backwards over a varint in the block - to do this correctly, we have
+ * to know the value we're skipping backwards over or things are ambiguous.
+ * @param {number} value The varint value to unskip.
+ */
+jspb.BinaryDecoder.prototype.unskipVarint = function(value) {
+ while (value > 128) {
+ this.cursor_--;
+ value = value >>> 7;
+ }
+ this.cursor_--;
+};
+
+
+/**
+ * Reads a 32-bit varint from the binary stream. Due to a quirk of the encoding
+ * format and Javascript's handling of bitwise math, this actually works
+ * correctly for both signed and unsigned 32-bit varints.
+ *
+ * This function is called vastly more frequently than any other in
+ * BinaryDecoder, so it has been unrolled and tweaked for performance.
+ *
+ * If there are more than 32 bits of data in the varint, it _must_ be due to
+ * sign-extension. If we're in debug mode and the high 32 bits don't match the
+ * expected sign extension, this method will throw an error.
+ *
+ * Decoding varints requires doing some funny base-128 math - for more
+ * details on the format, see
+ * https://developers.google.com/protocol-buffers/docs/encoding
+ *
+ * @return {number} The decoded unsigned 32-bit varint.
+ */
+jspb.BinaryDecoder.prototype.readUnsignedVarint32 = function() {
+ var temp;
+ var bytes = this.bytes_;
+
+ temp = bytes[this.cursor_ + 0];
+ var x = (temp & 0x7F);
+ if (temp < 128) {
+ this.cursor_ += 1;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return x;
+ }
+
+ temp = bytes[this.cursor_ + 1];
+ x |= (temp & 0x7F) << 7;
+ if (temp < 128) {
+ this.cursor_ += 2;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return x;
+ }
+
+ temp = bytes[this.cursor_ + 2];
+ x |= (temp & 0x7F) << 14;
+ if (temp < 128) {
+ this.cursor_ += 3;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return x;
+ }
+
+ temp = bytes[this.cursor_ + 3];
+ x |= (temp & 0x7F) << 21;
+ if (temp < 128) {
+ this.cursor_ += 4;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return x;
+ }
+
+ temp = bytes[this.cursor_ + 4];
+ x |= (temp & 0x0F) << 28;
+ if (temp < 128) {
+ // We're reading the high bits of an unsigned varint. The byte we just read
+ // also contains bits 33 through 35, which we're going to discard. Those
+ // bits _must_ be zero, or the encoding is invalid.
+ goog.asserts.assert((temp & 0xF0) == 0);
+ this.cursor_ += 5;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return x >>> 0;
+ }
+
+ // If we get here, we're reading the sign extension of a negative 32-bit int.
+ // We can skip these bytes, as we know in advance that they have to be all
+ // 1's if the varint is correctly encoded. Since we also know the value is
+ // negative, we don't have to coerce it to unsigned before we return it.
+
+ goog.asserts.assert((temp & 0xF0) == 0xF0);
+ goog.asserts.assert(bytes[this.cursor_ + 5] == 0xFF);
+ goog.asserts.assert(bytes[this.cursor_ + 6] == 0xFF);
+ goog.asserts.assert(bytes[this.cursor_ + 7] == 0xFF);
+ goog.asserts.assert(bytes[this.cursor_ + 8] == 0xFF);
+ goog.asserts.assert(bytes[this.cursor_ + 9] == 0x01);
+
+ this.cursor_ += 10;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return x;
+};
+
+
+/**
+ * The readUnsignedVarint32 above deals with signed 32-bit varints correctly,
+ * so this is just an alias.
+ *
+ * @return {number} The decoded signed 32-bit varint.
+ */
+jspb.BinaryDecoder.prototype.readSignedVarint32 =
+ jspb.BinaryDecoder.prototype.readUnsignedVarint32;
+
+
+/**
+ * Reads a 32-bit unsigned variant and returns its value as a string.
+ *
+ * @return {string} The decoded unsigned 32-bit varint as a string.
+ */
+jspb.BinaryDecoder.prototype.readUnsignedVarint32String = function() {
+ // 32-bit integers fit in JavaScript numbers without loss of precision, so
+ // string variants of 32-bit varint readers can simply delegate then convert
+ // to string.
+ var value = this.readUnsignedVarint32();
+ return value.toString();
+};
+
+/**
+ * Reads a 32-bit signed variant and returns its value as a string.
+ *
+ * @return {string} The decoded signed 32-bit varint as a string.
+ */
+jspb.BinaryDecoder.prototype.readSignedVarint32String = function() {
+ // 32-bit integers fit in JavaScript numbers without loss of precision, so
+ // string variants of 32-bit varint readers can simply delegate then convert
+ // to string.
+ var value = this.readSignedVarint32();
+ return value.toString();
+};
+
+
+/**
+ * Reads a signed, zigzag-encoded 32-bit varint from the binary stream.
+ *
+ * Zigzag encoding is a modification of varint encoding that reduces the
+ * storage overhead for small negative integers - for more details on the
+ * format, see https://developers.google.com/protocol-buffers/docs/encoding
+ *
+ * @return {number} The decoded signed, zigzag-encoded 32-bit varint.
+ */
+jspb.BinaryDecoder.prototype.readZigzagVarint32 = function() {
+ var result = this.readUnsignedVarint32();
+ return (result >>> 1) ^ - (result & 1);
+};
+
+
+/**
+ * Reads an unsigned 64-bit varint from the binary stream. Note that since
+ * Javascript represents all numbers as double-precision floats, there will be
+ * precision lost if the absolute value of the varint is larger than 2^53.
+ *
+ * @return {number} The decoded unsigned varint. Precision will be lost if the
+ * integer exceeds 2^53.
+ */
+jspb.BinaryDecoder.prototype.readUnsignedVarint64 = function() {
+ this.readSplitVarint64_();
+ return jspb.utils.joinUint64(this.tempLow_, this.tempHigh_);
+};
+
+
+/**
+ * Reads an unsigned 64-bit varint from the binary stream and returns the value
+ * as a decimal string.
+ *
+ * @return {string} The decoded unsigned varint as a decimal string.
+ */
+jspb.BinaryDecoder.prototype.readUnsignedVarint64String = function() {
+ this.readSplitVarint64_();
+ return jspb.utils.joinUnsignedDecimalString(this.tempLow_, this.tempHigh_);
+};
+
+
+/**
+ * Reads a signed 64-bit varint from the binary stream. Note that since
+ * Javascript represents all numbers as double-precision floats, there will be
+ * precision lost if the absolute value of the varint is larger than 2^53.
+ *
+ * @return {number} The decoded signed varint. Precision will be lost if the
+ * integer exceeds 2^53.
+ */
+jspb.BinaryDecoder.prototype.readSignedVarint64 = function() {
+ this.readSplitVarint64_();
+ return jspb.utils.joinInt64(this.tempLow_, this.tempHigh_);
+};
+
+
+/**
+ * Reads an signed 64-bit varint from the binary stream and returns the value
+ * as a decimal string.
+ *
+ * @return {string} The decoded signed varint as a decimal string.
+ */
+jspb.BinaryDecoder.prototype.readSignedVarint64String = function() {
+ this.readSplitVarint64_();
+ return jspb.utils.joinSignedDecimalString(this.tempLow_, this.tempHigh_);
+};
+
+
+/**
+ * Reads a signed, zigzag-encoded 64-bit varint from the binary stream. Note
+ * that since Javascript represents all numbers as double-precision floats,
+ * there will be precision lost if the absolute value of the varint is larger
+ * than 2^53.
+ *
+ * Zigzag encoding is a modification of varint encoding that reduces the
+ * storage overhead for small negative integers - for more details on the
+ * format, see https://developers.google.com/protocol-buffers/docs/encoding
+ *
+ * @return {number} The decoded zigzag varint. Precision will be lost if the
+ * integer exceeds 2^53.
+ */
+jspb.BinaryDecoder.prototype.readZigzagVarint64 = function() {
+ this.readSplitVarint64_();
+ return jspb.utils.joinZigzag64(this.tempLow_, this.tempHigh_);
+};
+
+
+/**
+ * Reads a raw unsigned 8-bit integer from the binary stream.
+ *
+ * @return {number} The unsigned 8-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readUint8 = function() {
+ var a = this.bytes_[this.cursor_ + 0];
+ this.cursor_ += 1;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return a;
+};
+
+
+/**
+ * Reads a raw unsigned 16-bit integer from the binary stream.
+ *
+ * @return {number} The unsigned 16-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readUint16 = function() {
+ var a = this.bytes_[this.cursor_ + 0];
+ var b = this.bytes_[this.cursor_ + 1];
+ this.cursor_ += 2;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return (a << 0) | (b << 8);
+};
+
+
+/**
+ * Reads a raw unsigned 32-bit integer from the binary stream.
+ *
+ * @return {number} The unsigned 32-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readUint32 = function() {
+ var a = this.bytes_[this.cursor_ + 0];
+ var b = this.bytes_[this.cursor_ + 1];
+ var c = this.bytes_[this.cursor_ + 2];
+ var d = this.bytes_[this.cursor_ + 3];
+ this.cursor_ += 4;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return ((a << 0) | (b << 8) | (c << 16) | (d << 24)) >>> 0;
+};
+
+
+/**
+ * Reads a raw unsigned 64-bit integer from the binary stream. Note that since
+ * Javascript represents all numbers as double-precision floats, there will be
+ * precision lost if the absolute value of the integer is larger than 2^53.
+ *
+ * @return {number} The unsigned 64-bit integer read from the binary stream.
+ * Precision will be lost if the integer exceeds 2^53.
+ */
+jspb.BinaryDecoder.prototype.readUint64 = function() {
+ var bitsLow = this.readUint32();
+ var bitsHigh = this.readUint32();
+ return jspb.utils.joinUint64(bitsLow, bitsHigh);
+};
+
+
+/**
+ * Reads a raw signed 8-bit integer from the binary stream.
+ *
+ * @return {number} The signed 8-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readInt8 = function() {
+ var a = this.bytes_[this.cursor_ + 0];
+ this.cursor_ += 1;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return (a << 24) >> 24;
+};
+
+
+/**
+ * Reads a raw signed 16-bit integer from the binary stream.
+ *
+ * @return {number} The signed 16-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readInt16 = function() {
+ var a = this.bytes_[this.cursor_ + 0];
+ var b = this.bytes_[this.cursor_ + 1];
+ this.cursor_ += 2;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return (((a << 0) | (b << 8)) << 16) >> 16;
+};
+
+
+/**
+ * Reads a raw signed 32-bit integer from the binary stream.
+ *
+ * @return {number} The signed 32-bit integer read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readInt32 = function() {
+ var a = this.bytes_[this.cursor_ + 0];
+ var b = this.bytes_[this.cursor_ + 1];
+ var c = this.bytes_[this.cursor_ + 2];
+ var d = this.bytes_[this.cursor_ + 3];
+ this.cursor_ += 4;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return (a << 0) | (b << 8) | (c << 16) | (d << 24);
+};
+
+
+/**
+ * Reads a raw signed 64-bit integer from the binary stream. Note that since
+ * Javascript represents all numbers as double-precision floats, there will be
+ * precision lost if the absolute vlaue of the integer is larger than 2^53.
+ *
+ * @return {number} The signed 64-bit integer read from the binary stream.
+ * Precision will be lost if the integer exceeds 2^53.
+ */
+jspb.BinaryDecoder.prototype.readInt64 = function() {
+ var bitsLow = this.readUint32();
+ var bitsHigh = this.readUint32();
+ return jspb.utils.joinInt64(bitsLow, bitsHigh);
+};
+
+
+/**
+ * Reads a 32-bit floating-point number from the binary stream, using the
+ * temporary buffer to realign the data.
+ *
+ * @return {number} The float read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readFloat = function() {
+ var bitsLow = this.readUint32();
+ var bitsHigh = 0;
+ return jspb.utils.joinFloat32(bitsLow, bitsHigh);
+};
+
+
+/**
+ * Reads a 64-bit floating-point number from the binary stream, using the
+ * temporary buffer to realign the data.
+ *
+ * @return {number} The double read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readDouble = function() {
+ var bitsLow = this.readUint32();
+ var bitsHigh = this.readUint32();
+ return jspb.utils.joinFloat64(bitsLow, bitsHigh);
+};
+
+
+/**
+ * Reads a boolean value from the binary stream.
+ * @return {boolean} The boolean read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readBool = function() {
+ return !!this.bytes_[this.cursor_++];
+};
+
+
+/**
+ * Reads an enum value from the binary stream, which are always encoded as
+ * signed varints.
+ * @return {number} The enum value read from the binary stream.
+ */
+jspb.BinaryDecoder.prototype.readEnum = function() {
+ return this.readSignedVarint32();
+};
+
+
+/**
+ * Reads and parses a UTF-8 encoded unicode string from the stream.
+ * The code is inspired by maps.vectortown.parse.StreamedDataViewReader, with
+ * the exception that the implementation here does not get confused if it
+ * encounters characters longer than three bytes. These characters are ignored
+ * though, as they are extremely rare: three UTF-8 bytes cover virtually all
+ * characters in common use (http://en.wikipedia.org/wiki/UTF-8).
+ * @param {number} length The length of the string to read.
+ * @return {string} The decoded string.
+ */
+jspb.BinaryDecoder.prototype.readString = function(length) {
+ var bytes = this.bytes_;
+ var cursor = this.cursor_;
+ var end = cursor + length;
+ var chars = [];
+
+ while (cursor < end) {
+ var c = bytes[cursor++];
+ if (c < 128) { // Regular 7-bit ASCII.
+ chars.push(c);
+ } else if (c < 192) {
+ // UTF-8 continuation mark. We are out of sync. This
+ // might happen if we attempted to read a character
+ // with more than three bytes.
+ continue;
+ } else if (c < 224) { // UTF-8 with two bytes.
+ var c2 = bytes[cursor++];
+ chars.push(((c & 31) << 6) | (c2 & 63));
+ } else if (c < 240) { // UTF-8 with three bytes.
+ var c2 = bytes[cursor++];
+ var c3 = bytes[cursor++];
+ chars.push(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ }
+ }
+
+ // String.fromCharCode.apply is faster than manually appending characters on
+ // Chrome 25+, and generates no additional cons string garbage.
+ var result = String.fromCharCode.apply(null, chars);
+ this.cursor_ = cursor;
+ return result;
+};
+
+
+/**
+ * Reads and parses a UTF-8 encoded unicode string (with length prefix) from
+ * the stream.
+ * @return {string} The decoded string.
+ */
+jspb.BinaryDecoder.prototype.readStringWithLength = function() {
+ var length = this.readUnsignedVarint32();
+ return this.readString(length);
+};
+
+
+/**
+ * Reads a block of raw bytes from the binary stream.
+ *
+ * @param {number} length The number of bytes to read.
+ * @return {Uint8Array} The decoded block of bytes, or null if the length was
+ * invalid.
+ */
+jspb.BinaryDecoder.prototype.readBytes = function(length) {
+ if (length < 0 ||
+ this.cursor_ + length > this.bytes_.length) {
+ this.error_ = true;
+ return null;
+ }
+
+ var result = this.bytes_.subarray(this.cursor_, this.cursor_ + length);
+
+ this.cursor_ += length;
+ goog.asserts.assert(this.cursor_ <= this.end_);
+ return result;
+};
+
+
+/**
+ * Reads a 64-bit varint from the stream and returns it as an 8-character
+ * Unicode string for use as a hash table key.
+ *
+ * @return {string} The hash value.
+ */
+jspb.BinaryDecoder.prototype.readVarintHash64 = function() {
+ this.readSplitVarint64_();
+ return jspb.utils.joinHash64(this.tempLow_, this.tempHigh_);
+};
+
+
+/**
+ * Reads a 64-bit fixed-width value from the stream and returns it as an
+ * 8-character Unicode string for use as a hash table key.
+ *
+ * @return {string} The hash value.
+ */
+jspb.BinaryDecoder.prototype.readFixedHash64 = function() {
+ var bytes = this.bytes_;
+ var cursor = this.cursor_;
+
+ var a = bytes[cursor + 0];
+ var b = bytes[cursor + 1];
+ var c = bytes[cursor + 2];
+ var d = bytes[cursor + 3];
+ var e = bytes[cursor + 4];
+ var f = bytes[cursor + 5];
+ var g = bytes[cursor + 6];
+ var h = bytes[cursor + 7];
+
+ this.cursor_ += 8;
+
+ return String.fromCharCode(a, b, c, d, e, f, g, h);
+};
diff --git a/js/binary/decoder_test.js b/js/binary/decoder_test.js
new file mode 100644
index 00000000..27342e49
--- /dev/null
+++ b/js/binary/decoder_test.js
@@ -0,0 +1,327 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's binary protocol buffer decoder.
+ *
+ * There are two particular magic numbers that need to be pointed out -
+ * 2^64-1025 is the largest number representable as both a double and an
+ * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
+ * both a double and a signed 64-bit integer.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryDecoder');
+goog.require('jspb.BinaryWriter');
+
+
+/**
+ * Tests raw encoding and decoding of unsigned types.
+ * @param {Function} readValue
+ * @param {Function} writeValue
+ * @param {number} epsilon
+ * @param {number} upperLimit
+ * @param {Function} filter
+ * @suppress {missingProperties|visibility}
+ */
+function doTestUnsignedValue(readValue,
+ writeValue, epsilon, upperLimit, filter) {
+ var writer = new jspb.BinaryWriter();
+
+ // Encode zero and limits.
+ writeValue.call(writer, filter(0));
+ writeValue.call(writer, filter(epsilon));
+ writeValue.call(writer, filter(upperLimit));
+
+ // Encode positive values.
+ for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+ writeValue.call(writer, filter(cursor));
+ }
+
+ var reader = jspb.BinaryDecoder.alloc(writer.getResultBuffer());
+
+ // Check zero and limits.
+ assertEquals(filter(0), readValue.call(reader));
+ assertEquals(filter(epsilon), readValue.call(reader));
+ assertEquals(filter(upperLimit), readValue.call(reader));
+
+ // Check positive values.
+ for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+ if (filter(cursor) != readValue.call(reader)) throw 'fail!';
+ }
+}
+
+
+/**
+ * Tests raw encoding and decoding of signed types.
+ * @param {Function} readValue
+ * @param {Function} writeValue
+ * @param {number} epsilon
+ * @param {number} lowerLimit
+ * @param {number} upperLimit
+ * @param {Function} filter
+ * @suppress {missingProperties}
+ */
+function doTestSignedValue(readValue,
+ writeValue, epsilon, lowerLimit, upperLimit, filter) {
+ var writer = new jspb.BinaryWriter();
+
+ // Encode zero and limits.
+ writeValue.call(writer, filter(lowerLimit));
+ writeValue.call(writer, filter(-epsilon));
+ writeValue.call(writer, filter(0));
+ writeValue.call(writer, filter(epsilon));
+ writeValue.call(writer, filter(upperLimit));
+
+ var inputValues = [];
+
+ // Encode negative values.
+ for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
+ var val = filter(cursor);
+ writeValue.call(writer, val);
+ inputValues.push(val);
+ }
+
+ // Encode positive values.
+ for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+ var val = filter(cursor);
+ writeValue.call(writer, val);
+ inputValues.push(val);
+ }
+
+ var reader = jspb.BinaryDecoder.alloc(writer.getResultBuffer());
+
+ // Check zero and limits.
+ assertEquals(filter(lowerLimit), readValue.call(reader));
+ assertEquals(filter(-epsilon), readValue.call(reader));
+ assertEquals(filter(0), readValue.call(reader));
+ assertEquals(filter(epsilon), readValue.call(reader));
+ assertEquals(filter(upperLimit), readValue.call(reader));
+
+ // Verify decoded values.
+ for (var i = 0; i < inputValues.length; i++) {
+ assertEquals(inputValues[i], readValue.call(reader));
+ }
+}
+
+describe('binaryDecoderTest', function() {
+ /**
+ * Tests the decoder instance cache.
+ * @suppress {visibility}
+ */
+ it('testInstanceCache', function() {
+ // Empty the instance caches.
+ jspb.BinaryDecoder.instanceCache_ = [];
+
+ // Allocating and then freeing a decoder should put it in the instance
+ // cache.
+ jspb.BinaryDecoder.alloc().free();
+
+ assertEquals(1, jspb.BinaryDecoder.instanceCache_.length);
+
+ // Allocating and then freeing three decoders should leave us with three in
+ // the cache.
+
+ var decoder1 = jspb.BinaryDecoder.alloc();
+ var decoder2 = jspb.BinaryDecoder.alloc();
+ var decoder3 = jspb.BinaryDecoder.alloc();
+ decoder1.free();
+ decoder2.free();
+ decoder3.free();
+
+ assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
+ });
+
+
+ /**
+ * Tests reading 64-bit integers as hash strings.
+ */
+ it('testHashStrings', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00);
+ var hashB = String.fromCharCode(0x12, 0x34, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00);
+ var hashC = String.fromCharCode(0x12, 0x34, 0x56, 0x78,
+ 0x87, 0x65, 0x43, 0x21);
+ var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF);
+
+ writer.rawWriteVarintHash64(hashA);
+ writer.rawWriteVarintHash64(hashB);
+ writer.rawWriteVarintHash64(hashC);
+ writer.rawWriteVarintHash64(hashD);
+
+ writer.rawWriteFixedHash64(hashA);
+ writer.rawWriteFixedHash64(hashB);
+ writer.rawWriteFixedHash64(hashC);
+ writer.rawWriteFixedHash64(hashD);
+
+ var decoder = jspb.BinaryDecoder.alloc(writer.getResultBuffer());
+
+ assertEquals(hashA, decoder.readVarintHash64());
+ assertEquals(hashB, decoder.readVarintHash64());
+ assertEquals(hashC, decoder.readVarintHash64());
+ assertEquals(hashD, decoder.readVarintHash64());
+
+ assertEquals(hashA, decoder.readFixedHash64());
+ assertEquals(hashB, decoder.readFixedHash64());
+ assertEquals(hashC, decoder.readFixedHash64());
+ assertEquals(hashD, decoder.readFixedHash64());
+ });
+
+
+ /**
+ * Verifies that misuse of the decoder class triggers assertions.
+ * @suppress {checkTypes|visibility}
+ */
+ it('testDecodeErrors', function() {
+ // Reading a value past the end of the stream should trigger an assertion.
+ var decoder = jspb.BinaryDecoder.alloc([0, 1, 2]);
+ assertThrows(function() {decoder.readUint64()});
+
+ // Overlong varints should trigger assertions.
+ decoder.setBlock(
+ [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0]);
+ assertThrows(function() {decoder.readUnsignedVarint64()});
+ decoder.reset();
+ assertThrows(function() {decoder.readSignedVarint64()});
+ decoder.reset();
+ assertThrows(function() {decoder.readZigzagVarint64()});
+
+ // Positive 32-bit varints encoded with 1 bits in positions 33 through 35
+ // should trigger assertions.
+ decoder.setBlock([255, 255, 255, 255, 0x1F]);
+ assertThrows(function() {decoder.readUnsignedVarint32()});
+
+ decoder.setBlock([255, 255, 255, 255, 0x2F]);
+ assertThrows(function() {decoder.readUnsignedVarint32()});
+
+ decoder.setBlock([255, 255, 255, 255, 0x4F]);
+ assertThrows(function() {decoder.readUnsignedVarint32()});
+
+ // Negative 32-bit varints encoded with non-1 bits in the high dword should
+ // trigger assertions.
+ decoder.setBlock([255, 255, 255, 255, 255, 255, 0, 255, 255, 1]);
+ assertThrows(function() {decoder.readUnsignedVarint32()});
+
+ decoder.setBlock([255, 255, 255, 255, 255, 255, 255, 255, 255, 0]);
+ assertThrows(function() {decoder.readUnsignedVarint32()});
+ });
+
+
+ /**
+ * Tests raw encoding and decoding of unsigned integers.
+ */
+ it('testRawUnsigned', function() {
+ doTestUnsignedValue(
+ jspb.BinaryDecoder.prototype.readUint8,
+ jspb.BinaryWriter.prototype.rawWriteUint8,
+ 1, 0xFF, Math.round);
+
+ doTestUnsignedValue(
+ jspb.BinaryDecoder.prototype.readUint16,
+ jspb.BinaryWriter.prototype.rawWriteUint16,
+ 1, 0xFFFF, Math.round);
+
+ doTestUnsignedValue(
+ jspb.BinaryDecoder.prototype.readUint32,
+ jspb.BinaryWriter.prototype.rawWriteUint32,
+ 1, 0xFFFFFFFF, Math.round);
+
+ doTestUnsignedValue(
+ jspb.BinaryDecoder.prototype.readUint64,
+ jspb.BinaryWriter.prototype.rawWriteUint64,
+ 1, Math.pow(2, 64) - 1025, Math.round);
+ });
+
+
+ /**
+ * Tests raw encoding and decoding of signed integers.
+ */
+ it('testRawSigned', function() {
+ doTestSignedValue(
+ jspb.BinaryDecoder.prototype.readInt8,
+ jspb.BinaryWriter.prototype.rawWriteInt8,
+ 1, -0x80, 0x7F, Math.round);
+
+ doTestSignedValue(
+ jspb.BinaryDecoder.prototype.readInt16,
+ jspb.BinaryWriter.prototype.rawWriteInt16,
+ 1, -0x8000, 0x7FFF, Math.round);
+
+ doTestSignedValue(
+ jspb.BinaryDecoder.prototype.readInt32,
+ jspb.BinaryWriter.prototype.rawWriteInt32,
+ 1, -0x80000000, 0x7FFFFFFF, Math.round);
+
+ doTestSignedValue(
+ jspb.BinaryDecoder.prototype.readInt64,
+ jspb.BinaryWriter.prototype.rawWriteInt64,
+ 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+ });
+
+
+ /**
+ * Tests raw encoding and decoding of floats.
+ */
+ it('testRawFloats', function() {
+ /**
+ * @param {number} x
+ * @return {number}
+ */
+ function truncate(x) {
+ var temp = new Float32Array(1);
+ temp[0] = x;
+ return temp[0];
+ }
+ doTestSignedValue(
+ jspb.BinaryDecoder.prototype.readFloat,
+ jspb.BinaryWriter.prototype.rawWriteFloat,
+ jspb.BinaryConstants.FLOAT32_EPS,
+ -jspb.BinaryConstants.FLOAT32_MAX,
+ jspb.BinaryConstants.FLOAT32_MAX,
+ truncate);
+
+ doTestSignedValue(
+ jspb.BinaryDecoder.prototype.readDouble,
+ jspb.BinaryWriter.prototype.rawWriteDouble,
+ jspb.BinaryConstants.FLOAT64_EPS * 10,
+ -jspb.BinaryConstants.FLOAT64_MAX,
+ jspb.BinaryConstants.FLOAT64_MAX,
+ function(x) { return x; });
+ });
+});
diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js
new file mode 100644
index 00000000..1cb7ff0e
--- /dev/null
+++ b/js/binary/proto_test.js
@@ -0,0 +1,487 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test suite is written using Jasmine -- see http://jasmine.github.io/
+
+goog.require('goog.testing.asserts');
+goog.require('proto.jspb.test.ExtendsWithMessage');
+goog.require('proto.jspb.test.ForeignEnum');
+goog.require('proto.jspb.test.ForeignMessage');
+goog.require('proto.jspb.test.TestAllTypes');
+goog.require('proto.jspb.test.TestExtendable');
+
+var suite = {};
+
+/**
+ * Helper: fill all fields on a TestAllTypes message.
+ * @param {proto.jspb.test.TestAllTypes} msg
+ */
+function fillAllFields(msg) {
+ msg.setOptionalInt32(-42);
+ // can be exactly represented by JS number (64-bit double, i.e., 52-bit
+ // mantissa).
+ msg.setOptionalInt64(-0x7fffffff00000000);
+ msg.setOptionalUint32(0x80000000);
+ msg.setOptionalUint64(0xf000000000000000);
+ msg.setOptionalSint32(-100);
+ msg.setOptionalSint64(-0x8000000000000000);
+ msg.setOptionalFixed32(1234);
+ msg.setOptionalFixed64(0x1234567800000000);
+ msg.setOptionalSfixed32(-1234);
+ msg.setOptionalSfixed64(-0x1234567800000000);
+ msg.setOptionalFloat(1.5);
+ msg.setOptionalDouble(-1.5);
+ msg.setOptionalBool(true);
+ msg.setOptionalString('hello world');
+ msg.setOptionalBytes('bytes');
+ msg.setOptionalGroup(new proto.jspb.test.TestAllTypes.OptionalGroup());
+ msg.getOptionalGroup().setA(100);
+ var submsg = new proto.jspb.test.ForeignMessage();
+ submsg.setC(16);
+ msg.setOptionalForeignMessage(submsg);
+ msg.setOptionalForeignEnum(proto.jspb.test.ForeignEnum.FOREIGN_FOO);
+ msg.setOneofString('oneof');
+
+ msg.setRepeatedInt32List([-42]);
+ msg.setRepeatedInt64List([-0x7fffffff00000000]);
+ msg.setRepeatedUint32List([0x80000000]);
+ msg.setRepeatedUint64List([0xf000000000000000]);
+ msg.setRepeatedSint32List([-100]);
+ msg.setRepeatedSint64List([-0x8000000000000000]);
+ msg.setRepeatedFixed32List([1234]);
+ msg.setRepeatedFixed64List([0x1234567800000000]);
+ msg.setRepeatedSfixed32List([-1234]);
+ msg.setRepeatedSfixed64List([-0x1234567800000000]);
+ msg.setRepeatedFloatList([1.5]);
+ msg.setRepeatedDoubleList([-1.5]);
+ msg.setRepeatedBoolList([true]);
+ msg.setRepeatedStringList(['hello world']);
+ msg.setRepeatedBytesList(['bytes']);
+ msg.setRepeatedGroupList([new proto.jspb.test.TestAllTypes.RepeatedGroup()]);
+ msg.getRepeatedGroupList()[0].setA(100);
+ submsg = new proto.jspb.test.ForeignMessage();
+ submsg.setC(1000);
+ msg.setRepeatedForeignMessageList([submsg]);
+ msg.setRepeatedForeignEnumList([proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+ msg.setPackedRepeatedInt32List([-42]);
+ msg.setPackedRepeatedInt64List([-0x7fffffff00000000]);
+ msg.setPackedRepeatedUint32List([0x80000000]);
+ msg.setPackedRepeatedUint64List([0xf000000000000000]);
+ msg.setPackedRepeatedSint32List([-100]);
+ msg.setPackedRepeatedSint64List([-0x8000000000000000]);
+ msg.setPackedRepeatedFixed32List([1234]);
+ msg.setPackedRepeatedFixed64List([0x1234567800000000]);
+ msg.setPackedRepeatedSfixed32List([-1234]);
+ msg.setPackedRepeatedSfixed64List([-0x1234567800000000]);
+ msg.setPackedRepeatedFloatList([1.5]);
+ msg.setPackedRepeatedDoubleList([-1.5]);
+ msg.setPackedRepeatedBoolList([true]);
+}
+
+
+/**
+ * Helper: compare a bytes field to a string with codepoints 0--255.
+ * @param {Uint8Array|string} arr
+ * @param {string} str
+ * @return {boolean}
+ */
+function bytesCompare(arr, str) {
+ if (arr.length != str.length) {
+ return false;
+ }
+ if (typeof arr == 'string') {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr.charCodeAt(i) != str.charCodeAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] != str.charCodeAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+
+/**
+ * Helper: verify contents of given TestAllTypes message as set by
+ * fillAllFields().
+ * @param {proto.jspb.test.TestAllTypes} msg
+ */
+function checkAllFields(msg) {
+ assertEquals(msg.getOptionalInt32(), -42);
+ assertEquals(msg.getOptionalInt64(), -0x7fffffff00000000);
+ assertEquals(msg.getOptionalUint32(), 0x80000000);
+ assertEquals(msg.getOptionalUint64(), 0xf000000000000000);
+ assertEquals(msg.getOptionalSint32(), -100);
+ assertEquals(msg.getOptionalSint64(), -0x8000000000000000);
+ assertEquals(msg.getOptionalFixed32(), 1234);
+ assertEquals(msg.getOptionalFixed64(), 0x1234567800000000);
+ assertEquals(msg.getOptionalSfixed32(), -1234);
+ assertEquals(msg.getOptionalSfixed64(), -0x1234567800000000);
+ assertEquals(msg.getOptionalFloat(), 1.5);
+ assertEquals(msg.getOptionalDouble(), -1.5);
+ assertEquals(msg.getOptionalBool(), true);
+ assertEquals(msg.getOptionalString(), 'hello world');
+ assertEquals(true, bytesCompare(msg.getOptionalBytes(), 'bytes'));
+ assertEquals(msg.getOptionalGroup().getA(), 100);
+ assertEquals(msg.getOptionalForeignMessage().getC(), 16);
+ assertEquals(msg.getOptionalForeignEnum(),
+ proto.jspb.test.ForeignEnum.FOREIGN_FOO);
+ assertEquals(msg.getOneofString(), 'oneof');
+ assertEquals(msg.getOneofFieldCase(),
+ proto.jspb.test.TestAllTypes.OneofFieldCase.ONEOF_STRING);
+
+ assertElementsEquals(msg.getRepeatedInt32List(), [-42]);
+ assertElementsEquals(msg.getRepeatedInt64List(), [-0x7fffffff00000000]);
+ assertElementsEquals(msg.getRepeatedUint32List(), [0x80000000]);
+ assertElementsEquals(msg.getRepeatedUint64List(), [0xf000000000000000]);
+ assertElementsEquals(msg.getRepeatedSint32List(), [-100]);
+ assertElementsEquals(msg.getRepeatedSint64List(), [-0x8000000000000000]);
+ assertElementsEquals(msg.getRepeatedFixed32List(), [1234]);
+ assertElementsEquals(msg.getRepeatedFixed64List(), [0x1234567800000000]);
+ assertElementsEquals(msg.getRepeatedSfixed32List(), [-1234]);
+ assertElementsEquals(msg.getRepeatedSfixed64List(), [-0x1234567800000000]);
+ assertElementsEquals(msg.getRepeatedFloatList(), [1.5]);
+ assertElementsEquals(msg.getRepeatedDoubleList(), [-1.5]);
+ assertElementsEquals(msg.getRepeatedBoolList(), [true]);
+ assertElementsEquals(msg.getRepeatedStringList(), ['hello world']);
+ assertEquals(msg.getRepeatedBytesList().length, 1);
+ assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], 'bytes'));
+ assertEquals(msg.getRepeatedGroupList().length, 1);
+ assertEquals(msg.getRepeatedGroupList()[0].getA(), 100);
+ assertEquals(msg.getRepeatedForeignMessageList().length, 1);
+ assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000);
+ assertElementsEquals(msg.getRepeatedForeignEnumList(),
+ [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+ assertElementsEquals(msg.getPackedRepeatedInt32List(), [-42]);
+ assertElementsEquals(msg.getPackedRepeatedInt64List(),
+ [-0x7fffffff00000000]);
+ assertElementsEquals(msg.getPackedRepeatedUint32List(), [0x80000000]);
+ assertElementsEquals(msg.getPackedRepeatedUint64List(), [0xf000000000000000]);
+ assertElementsEquals(msg.getPackedRepeatedSint32List(), [-100]);
+ assertElementsEquals(msg.getPackedRepeatedSint64List(),
+ [-0x8000000000000000]);
+ assertElementsEquals(msg.getPackedRepeatedFixed32List(), [1234]);
+ assertElementsEquals(msg.getPackedRepeatedFixed64List(),
+ [0x1234567800000000]);
+ assertElementsEquals(msg.getPackedRepeatedSfixed32List(), [-1234]);
+ assertElementsEquals(msg.getPackedRepeatedSfixed64List(),
+ [-0x1234567800000000]);
+ assertElementsEquals(msg.getPackedRepeatedFloatList(), [1.5]);
+ assertElementsEquals(msg.getPackedRepeatedDoubleList(), [-1.5]);
+ assertElementsEquals(msg.getPackedRepeatedBoolList(), [true]);
+}
+
+
+/**
+ * Helper: verify that all expected extensions are present.
+ * @param {!proto.jspb.test.TestExtendable} msg
+ */
+function checkExtensions(msg) {
+ assertEquals(-42,
+ msg.getExtension(proto.jspb.test.extendOptionalInt32));
+ assertEquals(-0x7fffffff00000000,
+ msg.getExtension(proto.jspb.test.extendOptionalInt64));
+ assertEquals(0x80000000,
+ msg.getExtension(proto.jspb.test.extendOptionalUint32));
+ assertEquals(0xf000000000000000,
+ msg.getExtension(proto.jspb.test.extendOptionalUint64));
+ assertEquals(-100,
+ msg.getExtension(proto.jspb.test.extendOptionalSint32));
+ assertEquals(-0x8000000000000000,
+ msg.getExtension(proto.jspb.test.extendOptionalSint64));
+ assertEquals(1234,
+ msg.getExtension(proto.jspb.test.extendOptionalFixed32));
+ assertEquals(0x1234567800000000,
+ msg.getExtension(proto.jspb.test.extendOptionalFixed64));
+ assertEquals(-1234,
+ msg.getExtension(proto.jspb.test.extendOptionalSfixed32));
+ assertEquals(-0x1234567800000000,
+ msg.getExtension(proto.jspb.test.extendOptionalSfixed64));
+ assertEquals(1.5,
+ msg.getExtension(proto.jspb.test.extendOptionalFloat));
+ assertEquals(-1.5,
+ msg.getExtension(proto.jspb.test.extendOptionalDouble));
+ assertEquals(true,
+ msg.getExtension(proto.jspb.test.extendOptionalBool));
+ assertEquals('hello world',
+ msg.getExtension(proto.jspb.test.extendOptionalString));
+ assertEquals(true,
+ bytesCompare(msg.getExtension(proto.jspb.test.extendOptionalBytes),
+ 'bytes'));
+ assertEquals(16,
+ msg.getExtension(
+ proto.jspb.test.ExtendsWithMessage.optionalExtension).getFoo());
+ assertEquals(proto.jspb.test.ForeignEnum.FOREIGN_FOO,
+ msg.getExtension(proto.jspb.test.extendOptionalForeignEnum));
+
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedInt32List),
+ [-42]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedInt64List),
+ [-0x7fffffff00000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedUint32List),
+ [0x80000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedUint64List),
+ [0xf000000000000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedSint32List),
+ [-100]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedSint64List),
+ [-0x8000000000000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedFixed32List),
+ [1234]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedFixed64List),
+ [0x1234567800000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedSfixed32List),
+ [-1234]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedSfixed64List),
+ [-0x1234567800000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedFloatList),
+ [1.5]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedDoubleList),
+ [-1.5]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedBoolList),
+ [true]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedStringList),
+ ['hello world']);
+ assertEquals(true,
+ bytesCompare(
+ msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0],
+ 'bytes'));
+ assertEquals(1000,
+ msg.getExtension(
+ proto.jspb.test.ExtendsWithMessage.repeatedExtensionList)[0]
+ .getFoo());
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendRepeatedForeignEnumList),
+ [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedInt32List),
+ [-42]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedInt64List),
+ [-0x7fffffff00000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedUint32List),
+ [0x80000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedUint64List),
+ [0xf000000000000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedSint32List),
+ [-100]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedSint64List),
+ [-0x8000000000000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedFixed32List),
+ [1234]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedFixed64List),
+ [0x1234567800000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedSfixed32List),
+ [-1234]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedSfixed64List),
+ [-0x1234567800000000]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedFloatList),
+ [1.5]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedDoubleList),
+ [-1.5]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedBoolList),
+ [true]);
+ assertElementsEquals(
+ msg.getExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList),
+ [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+}
+
+
+describe('protoBinaryTest', function() {
+ /**
+ * Tests a basic serialization-deserializaton round-trip with all supported
+ * field types (on the TestAllTypes message type).
+ */
+ it('testRoundTrip', function() {
+ var msg = new proto.jspb.test.TestAllTypes();
+ fillAllFields(msg);
+ var encoded = msg.serializeBinary();
+ var decoded = proto.jspb.test.TestAllTypes.deserializeBinary(encoded);
+ checkAllFields(decoded);
+ });
+
+
+ /**
+ * Helper: fill all extension values.
+ * @param {proto.jspb.test.TestExtendable} msg
+ */
+ function fillExtensions(msg) {
+ msg.setExtension(
+ proto.jspb.test.extendOptionalInt32, -42);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalInt64, -0x7fffffff00000000);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalUint32, 0x80000000);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalUint64, 0xf000000000000000);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalSint32, -100);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalSint64, -0x8000000000000000);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalFixed32, 1234);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalFixed64, 0x1234567800000000);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalSfixed32, -1234);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalSfixed64, -0x1234567800000000);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalFloat, 1.5);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalDouble, -1.5);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalBool, true);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalString, 'hello world');
+ msg.setExtension(
+ proto.jspb.test.extendOptionalBytes, 'bytes');
+ var submsg = new proto.jspb.test.ExtendsWithMessage();
+ submsg.setFoo(16);
+ msg.setExtension(
+ proto.jspb.test.ExtendsWithMessage.optionalExtension, submsg);
+ msg.setExtension(
+ proto.jspb.test.extendOptionalForeignEnum,
+ proto.jspb.test.ForeignEnum.FOREIGN_FOO);
+
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedInt32List, [-42]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedInt64List, [-0x7fffffff00000000]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedUint32List, [0x80000000]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedUint64List, [0xf000000000000000]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedSint32List, [-100]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedSint64List, [-0x8000000000000000]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedFixed32List, [1234]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedFixed64List, [0x1234567800000000]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedSfixed32List, [-1234]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedSfixed64List, [-0x1234567800000000]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedFloatList, [1.5]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedDoubleList, [-1.5]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedBoolList, [true]);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedStringList, ['hello world']);
+ msg.setExtension(
+ proto.jspb.test.extendRepeatedBytesList, ['bytes']);
+ submsg = new proto.jspb.test.ExtendsWithMessage();
+ submsg.setFoo(1000);
+ msg.setExtension(
+ proto.jspb.test.ExtendsWithMessage.repeatedExtensionList, [submsg]);
+ msg.setExtension(proto.jspb.test.extendRepeatedForeignEnumList,
+ [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedInt32List, [-42]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedInt64List, [-0x7fffffff00000000]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedUint32List, [0x80000000]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedUint64List, [0xf000000000000000]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedSint32List, [-100]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedSint64List, [-0x8000000000000000]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedFixed32List, [1234]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedFixed64List, [0x1234567800000000]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedSfixed32List, [-1234]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedSfixed64List,
+ [-0x1234567800000000]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedFloatList, [1.5]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedDoubleList, [-1.5]);
+ msg.setExtension(
+ proto.jspb.test.extendPackedRepeatedBoolList, [true]);
+ msg.setExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList,
+ [proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
+ }
+
+
+ /**
+ * Tests extension serialization and deserialization.
+ */
+ it('testExtensions', function() {
+ var msg = new proto.jspb.test.TestExtendable();
+ fillExtensions(msg);
+ var encoded = msg.serializeBinary();
+ var decoded = proto.jspb.test.TestExtendable.deserializeBinary(encoded);
+ checkExtensions(decoded);
+ });
+});
diff --git a/js/binary/reader.js b/js/binary/reader.js
new file mode 100644
index 00000000..abcd1660
--- /dev/null
+++ b/js/binary/reader.js
@@ -0,0 +1,1127 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview This file contains utilities for converting binary,
+ * wire-format protocol buffers into Javascript data structures.
+ *
+ * jspb's BinaryReader class wraps the BinaryDecoder class to add methods
+ * that understand the protocol buffer syntax and can do the type checking and
+ * bookkeeping necessary to parse trees of nested messages.
+ *
+ * Major caveat - Users of this library _must_ keep their Javascript proto
+ * parsing code in sync with the original .proto file - presumably you'll be
+ * using the typed jspb code generator, but if you bypass that you'll need
+ * to keep things in sync by hand.
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.provide('jspb.BinaryReader');
+
+goog.require('goog.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryDecoder');
+
+
+
+/**
+ * BinaryReader implements the decoders for all the wire types specified in
+ * https://developers.google.com/protocol-buffers/docs/encoding.
+ *
+ * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from.
+ * @param {number=} opt_start The optional offset to start reading at.
+ * @param {number=} opt_length The optional length of the block to read -
+ * we'll throw an assertion if we go off the end of the block.
+ * @constructor
+ * @struct
+ */
+jspb.BinaryReader = function(opt_bytes, opt_start, opt_length) {
+ /**
+ * Wire-format decoder.
+ * @private {!jspb.BinaryDecoder}
+ */
+ this.decoder_ = jspb.BinaryDecoder.alloc(opt_bytes, opt_start, opt_length);
+
+ /**
+ * Cursor immediately before the field tag.
+ * @private {number}
+ */
+ this.fieldCursor_ = this.decoder_.getCursor();
+
+ /**
+ * Field number of the next field in the buffer, filled in by nextField().
+ * @private {number}
+ */
+ this.nextField_ = jspb.BinaryConstants.INVALID_FIELD_NUMBER;
+
+ /**
+ * Wire type of the next proto field in the buffer, filled in by
+ * nextField().
+ * @private {jspb.BinaryConstants.WireType}
+ */
+ this.nextWireType_ = jspb.BinaryConstants.WireType.INVALID;
+
+ /**
+ * Set to true if this reader encountered an error due to corrupt data.
+ * @private {boolean}
+ */
+ this.error_ = false;
+
+ /**
+ * User-defined reader callbacks.
+ * @private {Object.<string, function(!jspb.BinaryReader):*>}
+ */
+ this.readCallbacks_ = null;
+};
+
+
+/**
+ * Global pool of BinaryReader instances.
+ * @private {!Array.<!jspb.BinaryReader>}
+ */
+jspb.BinaryReader.instanceCache_ = [];
+
+
+/**
+ * Pops an instance off the instance cache, or creates one if the cache is
+ * empty.
+ * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from.
+ * @param {number=} opt_start The optional offset to start reading at.
+ * @param {number=} opt_length The optional length of the block to read -
+ * we'll throw an assertion if we go off the end of the block.
+ * @return {!jspb.BinaryReader}
+ */
+jspb.BinaryReader.alloc =
+ function(opt_bytes, opt_start, opt_length) {
+ if (jspb.BinaryReader.instanceCache_.length) {
+ var newReader = jspb.BinaryReader.instanceCache_.pop();
+ if (opt_bytes) {
+ newReader.decoder_.setBlock(opt_bytes, opt_start, opt_length);
+ }
+ return newReader;
+ } else {
+ return new jspb.BinaryReader(opt_bytes, opt_start, opt_length);
+ }
+};
+
+
+/**
+ * Alias for the above method.
+ * @param {jspb.ByteSource=} opt_bytes The bytes we're reading from.
+ * @param {number=} opt_start The optional offset to start reading at.
+ * @param {number=} opt_length The optional length of the block to read -
+ * we'll throw an assertion if we go off the end of the block.
+ * @return {!jspb.BinaryReader}
+ */
+jspb.BinaryReader.prototype.alloc = jspb.BinaryReader.alloc;
+
+
+/**
+ * Puts this instance back in the instance cache.
+ */
+jspb.BinaryReader.prototype.free = function() {
+ this.decoder_.clear();
+ this.nextField_ = jspb.BinaryConstants.INVALID_FIELD_NUMBER;
+ this.nextWireType_ = jspb.BinaryConstants.WireType.INVALID;
+ this.error_ = false;
+ this.readCallbacks_ = null;
+
+ if (jspb.BinaryReader.instanceCache_.length < 100) {
+ jspb.BinaryReader.instanceCache_.push(this);
+ }
+};
+
+
+/**
+ * Returns the cursor immediately before the current field's tag.
+ * @return {number} The internal read cursor.
+ */
+jspb.BinaryReader.prototype.getFieldCursor = function() {
+ return this.fieldCursor_;
+};
+
+
+/**
+ * Returns the internal read cursor.
+ * @return {number} The internal read cursor.
+ */
+jspb.BinaryReader.prototype.getCursor = function() {
+ return this.decoder_.getCursor();
+};
+
+
+/**
+ * Returns the raw buffer.
+ * @return {Uint8Array} The raw buffer.
+ */
+jspb.BinaryReader.prototype.getBuffer = function() {
+ return this.decoder_.getBuffer();
+};
+
+
+/**
+ * @return {number} The field number of the next field in the buffer, or
+ * INVALID_FIELD_NUMBER if there is no next field.
+ */
+jspb.BinaryReader.prototype.getFieldNumber = function() {
+ return this.nextField_;
+};
+
+
+/**
+ * @return {jspb.BinaryConstants.WireType} The wire type of the next field
+ * in the stream, or WireType.INVALID if there is no next field.
+ */
+jspb.BinaryReader.prototype.getWireType = function() {
+ return this.nextWireType_;
+};
+
+
+/**
+ * @return {boolean} Whether the current wire type is an end-group tag. Used as
+ * an exit condition in decoder loops in generated code.
+ */
+jspb.BinaryReader.prototype.isEndGroup = function() {
+ return this.nextWireType_ == jspb.BinaryConstants.WireType.END_GROUP;
+};
+
+
+/**
+ * Returns true if this reader hit an error due to corrupt data.
+ * @return {boolean}
+ */
+jspb.BinaryReader.prototype.getError = function() {
+ return this.error_ || this.decoder_.getError();
+};
+
+
+/**
+ * Points this reader at a new block of bytes.
+ * @param {!Uint8Array} bytes The block of bytes we're reading from.
+ * @param {number} start The offset to start reading at.
+ * @param {number} length The length of the block to read.
+ */
+jspb.BinaryReader.prototype.setBlock = function(bytes, start, length) {
+ this.decoder_.setBlock(bytes, start, length);
+ this.nextField_ = jspb.BinaryConstants.INVALID_FIELD_NUMBER;
+ this.nextWireType_ = jspb.BinaryConstants.WireType.INVALID;
+};
+
+
+/**
+ * Rewinds the stream cursor to the beginning of the buffer and resets all
+ * internal state.
+ */
+jspb.BinaryReader.prototype.reset = function() {
+ this.decoder_.reset();
+ this.nextField_ = jspb.BinaryConstants.INVALID_FIELD_NUMBER;
+ this.nextWireType_ = jspb.BinaryConstants.WireType.INVALID;
+};
+
+
+/**
+ * Advances the stream cursor by the given number of bytes.
+ * @param {number} count The number of bytes to advance by.
+ */
+jspb.BinaryReader.prototype.advance = function(count) {
+ this.decoder_.advance(count);
+};
+
+
+/**
+ * Reads the next field header in the stream if there is one, returns true if
+ * we saw a valid field header or false if we've read the whole stream.
+ * Throws an error if we encountered a deprecated START_GROUP/END_GROUP field.
+ * @return {boolean} True if the stream contains more fields.
+ */
+jspb.BinaryReader.prototype.nextField = function() {
+ // If we're at the end of the block, there are no more fields.
+ if (this.decoder_.atEnd()) {
+ return false;
+ }
+
+ // If we hit an error decoding the previous field, stop now before we
+ // try to decode anything else
+ if (this.getError()) {
+ goog.asserts.fail('Decoder hit an error');
+ return false;
+ }
+
+ // Otherwise just read the header of the next field.
+ this.fieldCursor_ = this.decoder_.getCursor();
+ var header = this.decoder_.readUnsignedVarint32();
+
+ var nextField = header >>> 3;
+ var nextWireType = /** @type {jspb.BinaryConstants.WireType} */
+ (header & 0x7);
+
+ // If the wire type isn't one of the valid ones, something's broken.
+ if (nextWireType != jspb.BinaryConstants.WireType.VARINT &&
+ nextWireType != jspb.BinaryConstants.WireType.FIXED32 &&
+ nextWireType != jspb.BinaryConstants.WireType.FIXED64 &&
+ nextWireType != jspb.BinaryConstants.WireType.DELIMITED &&
+ nextWireType != jspb.BinaryConstants.WireType.START_GROUP &&
+ nextWireType != jspb.BinaryConstants.WireType.END_GROUP) {
+ goog.asserts.fail('Invalid wire type');
+ this.error_ = true;
+ return false;
+ }
+
+ this.nextField_ = nextField;
+ this.nextWireType_ = nextWireType;
+
+ return true;
+};
+
+
+/**
+ * Winds the reader back to just before this field's header.
+ */
+jspb.BinaryReader.prototype.unskipHeader = function() {
+ this.decoder_.unskipVarint((this.nextField_ << 3) | this.nextWireType_);
+};
+
+
+/**
+ * Skips all contiguous fields whose header matches the one we just read.
+ */
+jspb.BinaryReader.prototype.skipMatchingFields = function() {
+ var field = this.nextField_;
+ this.unskipHeader();
+
+ while (this.nextField() && (this.getFieldNumber() == field)) {
+ this.skipField();
+ }
+
+ if (!this.decoder_.atEnd()) {
+ this.unskipHeader();
+ }
+};
+
+
+/**
+ * Skips over the next varint field in the binary stream.
+ */
+jspb.BinaryReader.prototype.skipVarintField = function() {
+ if (this.nextWireType_ != jspb.BinaryConstants.WireType.VARINT) {
+ goog.asserts.fail('Invalid wire type for skipVarintField');
+ this.skipField();
+ return;
+ }
+
+ this.decoder_.skipVarint();
+};
+
+
+/**
+ * Skips over the next delimited field in the binary stream.
+ */
+jspb.BinaryReader.prototype.skipDelimitedField = function() {
+ if (this.nextWireType_ != jspb.BinaryConstants.WireType.DELIMITED) {
+ goog.asserts.fail('Invalid wire type for skipDelimitedField');
+ this.skipField();
+ return;
+ }
+
+ var length = this.decoder_.readUnsignedVarint32();
+ this.decoder_.advance(length);
+};
+
+
+/**
+ * Skips over the next fixed32 field in the binary stream.
+ */
+jspb.BinaryReader.prototype.skipFixed32Field = function() {
+ if (this.nextWireType_ != jspb.BinaryConstants.WireType.FIXED32) {
+ goog.asserts.fail('Invalid wire type for skipFixed32Field');
+ this.skipField();
+ return;
+ }
+
+ this.decoder_.advance(4);
+};
+
+
+/**
+ * Skips over the next fixed64 field in the binary stream.
+ */
+jspb.BinaryReader.prototype.skipFixed64Field = function() {
+ if (this.nextWireType_ != jspb.BinaryConstants.WireType.FIXED64) {
+ goog.asserts.fail('Invalid wire type for skipFixed64Field');
+ this.skipField();
+ return;
+ }
+
+ this.decoder_.advance(8);
+};
+
+
+/**
+ * Skips over the next group field in the binary stream.
+ */
+jspb.BinaryReader.prototype.skipGroup = function() {
+ // Keep a stack of start-group tags that must be matched by end-group tags.
+ var nestedGroups = [this.nextField_];
+ do {
+ if (!this.nextField()) {
+ goog.asserts.fail('Unmatched start-group tag: stream EOF');
+ this.error_ = true;
+ return;
+ }
+ if (this.nextWireType_ ==
+ jspb.BinaryConstants.WireType.START_GROUP) {
+ // Nested group start.
+ nestedGroups.push(this.nextField_);
+ } else if (this.nextWireType_ ==
+ jspb.BinaryConstants.WireType.END_GROUP) {
+ // Group end: check that it matches top-of-stack.
+ if (this.nextField_ != nestedGroups.pop()) {
+ goog.asserts.fail('Unmatched end-group tag');
+ this.error_ = true;
+ return;
+ }
+ }
+ } while (nestedGroups.length > 0);
+};
+
+
+/**
+ * Skips over the next field in the binary stream - this is useful if we're
+ * decoding a message that contain unknown fields.
+ */
+jspb.BinaryReader.prototype.skipField = function() {
+ switch (this.nextWireType_) {
+ case jspb.BinaryConstants.WireType.VARINT:
+ this.skipVarintField();
+ break;
+ case jspb.BinaryConstants.WireType.FIXED64:
+ this.skipFixed64Field();
+ break;
+ case jspb.BinaryConstants.WireType.DELIMITED:
+ this.skipDelimitedField();
+ break;
+ case jspb.BinaryConstants.WireType.FIXED32:
+ this.skipFixed32Field();
+ break;
+ case jspb.BinaryConstants.WireType.START_GROUP:
+ this.skipGroup();
+ break;
+ default:
+ goog.asserts.fail('Invalid wire encoding for field.');
+ }
+};
+
+
+/**
+ * Registers a user-defined read callback.
+ * @param {string} callbackName
+ * @param {function(!jspb.BinaryReader):*} callback
+ */
+jspb.BinaryReader.prototype.registerReadCallback =
+ function(callbackName, callback) {
+ if (goog.isNull(this.readCallbacks_)) {
+ this.readCallbacks_ = {};
+ }
+ goog.asserts.assert(!this.readCallbacks_[callbackName]);
+ this.readCallbacks_[callbackName] = callback;
+};
+
+
+/**
+ * Runs a registered read callback.
+ * @param {string} callbackName The name the callback is registered under.
+ * @return {*} The value returned by the callback.
+ */
+jspb.BinaryReader.prototype.runReadCallback = function(callbackName) {
+ goog.asserts.assert(!goog.isNull(this.readCallbacks_));
+ var callback = this.readCallbacks_[callbackName];
+ goog.asserts.assert(callback);
+ return callback(this);
+};
+
+
+/**
+ * Reads a field of any valid non-message type from the binary stream.
+ * @param {jspb.BinaryConstants.FieldType} fieldType
+ * @return {jspb.AnyFieldType}
+ */
+jspb.BinaryReader.prototype.readAny = function(fieldType) {
+ this.nextWireType_ = jspb.BinaryConstants.FieldTypeToWireType(fieldType);
+ var fieldTypes = jspb.BinaryConstants.FieldType;
+ switch (fieldType) {
+ case fieldTypes.DOUBLE:
+ return this.readDouble();
+ case fieldTypes.FLOAT:
+ return this.readFloat();
+ case fieldTypes.INT64:
+ return this.readInt64();
+ case fieldTypes.UINT64:
+ return this.readUint64();
+ case fieldTypes.INT32:
+ return this.readInt32();
+ case fieldTypes.FIXED64:
+ return this.readFixed64();
+ case fieldTypes.FIXED32:
+ return this.readFixed32();
+ case fieldTypes.BOOL:
+ return this.readBool();
+ case fieldTypes.STRING:
+ return this.readString();
+ case fieldTypes.GROUP:
+ goog.asserts.fail('Group field type not supported in readAny()');
+ case fieldTypes.MESSAGE:
+ goog.asserts.fail('Message field type not supported in readAny()');
+ case fieldTypes.BYTES:
+ return this.readBytes();
+ case fieldTypes.UINT32:
+ return this.readUint32();
+ case fieldTypes.ENUM:
+ return this.readEnum();
+ case fieldTypes.SFIXED32:
+ return this.readSfixed32();
+ case fieldTypes.SFIXED64:
+ return this.readSfixed64();
+ case fieldTypes.SINT32:
+ return this.readSint32();
+ case fieldTypes.SINT64:
+ return this.readSint64();
+ case fieldTypes.FHASH64:
+ return this.readFixedHash64();
+ case fieldTypes.VHASH64:
+ return this.readVarintHash64();
+ default:
+ goog.asserts.fail('Invalid field type in readAny()');
+ }
+ return 0;
+};
+
+
+/**
+ * Deserialize a proto into the provided message object using the provided
+ * reader function. This function is templated as we currently have one client
+ * who is using manual deserialization instead of the code-generated versions.
+ * @template T
+ * @param {T} message
+ * @param {function(T, !jspb.BinaryReader)} reader
+ */
+jspb.BinaryReader.prototype.readMessage = function(message, reader) {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.DELIMITED);
+
+ // Save the current endpoint of the decoder and move it to the end of the
+ // embedded message.
+ var oldEnd = this.decoder_.getEnd();
+ var length = this.decoder_.readUnsignedVarint32();
+ var newEnd = this.decoder_.getCursor() + length;
+ this.decoder_.setEnd(newEnd);
+
+ // Deserialize the embedded message.
+ reader(message, this);
+
+ // Advance the decoder past the embedded message and restore the endpoint.
+ this.decoder_.setCursor(newEnd);
+ this.decoder_.setEnd(oldEnd);
+};
+
+
+/**
+ * Deserialize a proto into the provided message object using the provided
+ * reader function, assuming that the message is serialized as a group
+ * with the given tag.
+ * @template T
+ * @param {number} field
+ * @param {T} message
+ * @param {function(T, !jspb.BinaryReader)} reader
+ */
+jspb.BinaryReader.prototype.readGroup =
+ function(field, message, reader) {
+ // Ensure that the wire type is correct.
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.START_GROUP);
+ // Ensure that the field number is correct.
+ goog.asserts.assert(this.nextField_ == field);
+
+ // Deserialize the message. The deserialization will stop at an END_GROUP tag.
+ reader(message, this);
+
+ if (!this.error_ &&
+ this.nextWireType_ != jspb.BinaryConstants.WireType.END_GROUP) {
+ goog.asserts.fail('Group submessage did not end with an END_GROUP tag');
+ this.error_ = true;
+ }
+};
+
+
+/**
+ * Return a decoder that wraps the current delimited field.
+ * @return {!jspb.BinaryDecoder}
+ */
+jspb.BinaryReader.prototype.getFieldDecoder = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.DELIMITED);
+
+ var length = this.decoder_.readUnsignedVarint32();
+ var start = this.decoder_.getCursor();
+ var end = start + length;
+
+ var innerDecoder = jspb.BinaryDecoder.alloc(this.decoder_.getBuffer(),
+ start, length);
+ this.decoder_.setCursor(end);
+ return innerDecoder;
+};
+
+
+/**
+ * Reads a signed 32-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the signed 32-bit integer field.
+ */
+jspb.BinaryReader.prototype.readInt32 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readSignedVarint32();
+};
+
+
+/**
+ * Reads a signed 32-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * Returns the value as a string.
+ *
+ * @return {string} The value of the signed 32-bit integer field as a decimal
+ * string.
+ */
+jspb.BinaryReader.prototype.readInt32String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readSignedVarint32String();
+};
+
+
+/**
+ * Reads a signed 64-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the signed 64-bit integer field.
+ */
+jspb.BinaryReader.prototype.readInt64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readSignedVarint64();
+};
+
+
+/**
+ * Reads a signed 64-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * Returns the value as a string.
+ *
+ * @return {string} The value of the signed 64-bit integer field as a decimal
+ * string.
+ */
+jspb.BinaryReader.prototype.readInt64String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readSignedVarint64String();
+};
+
+
+/**
+ * Reads an unsigned 32-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the unsigned 32-bit integer field.
+ */
+jspb.BinaryReader.prototype.readUint32 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readUnsignedVarint32();
+};
+
+
+/**
+ * Reads an unsigned 32-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * Returns the value as a string.
+ *
+ * @return {string} The value of the unsigned 32-bit integer field as a decimal
+ * string.
+ */
+jspb.BinaryReader.prototype.readUint32String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readUnsignedVarint32String();
+};
+
+
+/**
+ * Reads an unsigned 64-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the unsigned 64-bit integer field.
+ */
+jspb.BinaryReader.prototype.readUint64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readUnsignedVarint64();
+};
+
+
+/**
+ * Reads an unsigned 64-bit integer field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * Returns the value as a string.
+ *
+ * @return {string} The value of the unsigned 64-bit integer field as a decimal
+ * string.
+ */
+jspb.BinaryReader.prototype.readUint64String = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readUnsignedVarint64String();
+};
+
+
+/**
+ * Reads a signed zigzag-encoded 32-bit integer field from the binary stream,
+ * or throws an error if the next field in the stream is not of the correct
+ * wire type.
+ *
+ * @return {number} The value of the signed 32-bit integer field.
+ */
+jspb.BinaryReader.prototype.readSint32 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readZigzagVarint32();
+};
+
+
+/**
+ * Reads a signed zigzag-encoded 64-bit integer field from the binary stream,
+ * or throws an error if the next field in the stream is not of the correct
+ * wire type.
+ *
+ * @return {number} The value of the signed 64-bit integer field.
+ */
+jspb.BinaryReader.prototype.readSint64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readZigzagVarint64();
+};
+
+
+/**
+ * Reads an unsigned 32-bit fixed-length integer fiield from the binary stream,
+ * or throws an error if the next field in the stream is not of the correct
+ * wire type.
+ *
+ * @return {number} The value of the double field.
+ */
+jspb.BinaryReader.prototype.readFixed32 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED32);
+ return this.decoder_.readUint32();
+};
+
+
+/**
+ * Reads an unsigned 64-bit fixed-length integer fiield from the binary stream,
+ * or throws an error if the next field in the stream is not of the correct
+ * wire type.
+ *
+ * @return {number} The value of the float field.
+ */
+jspb.BinaryReader.prototype.readFixed64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64);
+ return this.decoder_.readUint64();
+};
+
+
+/**
+ * Reads a signed 32-bit fixed-length integer fiield from the binary stream, or
+ * throws an error if the next field in the stream is not of the correct wire
+ * type.
+ *
+ * @return {number} The value of the double field.
+ */
+jspb.BinaryReader.prototype.readSfixed32 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED32);
+ return this.decoder_.readInt32();
+};
+
+
+/**
+ * Reads a signed 64-bit fixed-length integer fiield from the binary stream, or
+ * throws an error if the next field in the stream is not of the correct wire
+ * type.
+ *
+ * @return {number} The value of the float field.
+ */
+jspb.BinaryReader.prototype.readSfixed64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64);
+ return this.decoder_.readInt64();
+};
+
+
+/**
+ * Reads a 32-bit floating-point field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the float field.
+ */
+jspb.BinaryReader.prototype.readFloat = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED32);
+ return this.decoder_.readFloat();
+};
+
+
+/**
+ * Reads a 64-bit floating-point field from the binary stream, or throws an
+ * error if the next field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the double field.
+ */
+jspb.BinaryReader.prototype.readDouble = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64);
+ return this.decoder_.readDouble();
+};
+
+
+/**
+ * Reads a boolean field from the binary stream, or throws an error if the next
+ * field in the stream is not of the correct wire type.
+ *
+ * @return {boolean} The value of the boolean field.
+ */
+jspb.BinaryReader.prototype.readBool = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return !!this.decoder_.readUnsignedVarint32();
+};
+
+
+/**
+ * Reads an enum field from the binary stream, or throws an error if the next
+ * field in the stream is not of the correct wire type.
+ *
+ * @return {number} The value of the enum field.
+ */
+jspb.BinaryReader.prototype.readEnum = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readSignedVarint64();
+};
+
+
+/**
+ * Reads a string field from the binary stream, or throws an error if the next
+ * field in the stream is not of the correct wire type.
+ *
+ * @return {string} The value of the string field.
+ */
+jspb.BinaryReader.prototype.readString = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.DELIMITED);
+ var length = this.decoder_.readUnsignedVarint32();
+ return this.decoder_.readString(length);
+};
+
+
+/**
+ * Reads a length-prefixed block of bytes from the binary stream, or returns
+ * null if the next field in the stream has an invalid length value.
+ *
+ * @return {Uint8Array} The block of bytes.
+ */
+jspb.BinaryReader.prototype.readBytes = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.DELIMITED);
+ var length = this.decoder_.readUnsignedVarint32();
+ return this.decoder_.readBytes(length);
+};
+
+
+/**
+ * Reads a 64-bit varint or fixed64 field from the stream and returns it as a
+ * 8-character Unicode string for use as a hash table key, or throws an error
+ * if the next field in the stream is not of the correct wire type.
+ *
+ * @return {string} The hash value.
+ */
+jspb.BinaryReader.prototype.readVarintHash64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.VARINT);
+ return this.decoder_.readVarintHash64();
+};
+
+
+/**
+ * Reads a 64-bit varint or fixed64 field from the stream and returns it as a
+ * 8-character Unicode string for use as a hash table key, or throws an error
+ * if the next field in the stream is not of the correct wire type.
+ *
+ * @return {string} The hash value.
+ */
+jspb.BinaryReader.prototype.readFixedHash64 = function() {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.FIXED64);
+ return this.decoder_.readFixedHash64();
+};
+
+
+/**
+ * Reads a packed scalar field using the supplied raw reader function.
+ * @param {function()} decodeMethod
+ * @return {!Array}
+ * @private
+ */
+jspb.BinaryReader.prototype.readPackedField_ = function(decodeMethod) {
+ goog.asserts.assert(
+ this.nextWireType_ == jspb.BinaryConstants.WireType.DELIMITED);
+ var length = this.decoder_.readUnsignedVarint32();
+ var end = this.decoder_.getCursor() + length;
+ var result = [];
+ while (this.decoder_.getCursor() < end) {
+ // TODO(aappleby): .call is slow
+ result.push(decodeMethod.call(this.decoder_));
+ }
+ return result;
+};
+
+
+/**
+ * Reads a packed int32 field, which consists of a length header and a list of
+ * signed varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedInt32 = function() {
+ return this.readPackedField_(this.decoder_.readSignedVarint32);
+};
+
+
+/**
+ * Reads a packed int32 field, which consists of a length header and a list of
+ * signed varints. Returns a list of strings.
+ * @return {!Array.<string>}
+ */
+jspb.BinaryReader.prototype.readPackedInt32String = function() {
+ return this.readPackedField_(this.decoder_.readSignedVarint32String);
+};
+
+
+/**
+ * Reads a packed int64 field, which consists of a length header and a list of
+ * signed varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedInt64 = function() {
+ return this.readPackedField_(this.decoder_.readSignedVarint64);
+};
+
+
+/**
+ * Reads a packed int64 field, which consists of a length header and a list of
+ * signed varints. Returns a list of strings.
+ * @return {!Array.<string>}
+ */
+jspb.BinaryReader.prototype.readPackedInt64String = function() {
+ return this.readPackedField_(this.decoder_.readSignedVarint64String);
+};
+
+
+/**
+ * Reads a packed uint32 field, which consists of a length header and a list of
+ * unsigned varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedUint32 = function() {
+ return this.readPackedField_(this.decoder_.readUnsignedVarint32);
+};
+
+
+/**
+ * Reads a packed uint32 field, which consists of a length header and a list of
+ * unsigned varints. Returns a list of strings.
+ * @return {!Array.<string>}
+ */
+jspb.BinaryReader.prototype.readPackedUint32String = function() {
+ return this.readPackedField_(this.decoder_.readUnsignedVarint32String);
+};
+
+
+/**
+ * Reads a packed uint64 field, which consists of a length header and a list of
+ * unsigned varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedUint64 = function() {
+ return this.readPackedField_(this.decoder_.readUnsignedVarint64);
+};
+
+
+/**
+ * Reads a packed uint64 field, which consists of a length header and a list of
+ * unsigned varints. Returns a list of strings.
+ * @return {!Array.<string>}
+ */
+jspb.BinaryReader.prototype.readPackedUint64String = function() {
+ return this.readPackedField_(this.decoder_.readUnsignedVarint64String);
+};
+
+
+/**
+ * Reads a packed sint32 field, which consists of a length header and a list of
+ * zigzag varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedSint32 = function() {
+ return this.readPackedField_(this.decoder_.readZigzagVarint32);
+};
+
+
+/**
+ * Reads a packed sint64 field, which consists of a length header and a list of
+ * zigzag varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedSint64 = function() {
+ return this.readPackedField_(this.decoder_.readZigzagVarint64);
+};
+
+
+/**
+ * Reads a packed fixed32 field, which consists of a length header and a list
+ * of unsigned 32-bit ints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedFixed32 = function() {
+ return this.readPackedField_(this.decoder_.readUint32);
+};
+
+
+/**
+ * Reads a packed fixed64 field, which consists of a length header and a list
+ * of unsigned 64-bit ints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedFixed64 = function() {
+ return this.readPackedField_(this.decoder_.readUint64);
+};
+
+
+/**
+ * Reads a packed sfixed32 field, which consists of a length header and a list
+ * of 32-bit ints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedSfixed32 = function() {
+ return this.readPackedField_(this.decoder_.readInt32);
+};
+
+
+/**
+ * Reads a packed sfixed64 field, which consists of a length header and a list
+ * of 64-bit ints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedSfixed64 = function() {
+ return this.readPackedField_(this.decoder_.readInt64);
+};
+
+
+/**
+ * Reads a packed float field, which consists of a length header and a list of
+ * floats.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedFloat = function() {
+ return this.readPackedField_(this.decoder_.readFloat);
+};
+
+
+/**
+ * Reads a packed double field, which consists of a length header and a list of
+ * doubles.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedDouble = function() {
+ return this.readPackedField_(this.decoder_.readDouble);
+};
+
+
+/**
+ * Reads a packed bool field, which consists of a length header and a list of
+ * unsigned varints.
+ * @return {!Array.<boolean>}
+ */
+jspb.BinaryReader.prototype.readPackedBool = function() {
+ return this.readPackedField_(this.decoder_.readBool);
+};
+
+
+/**
+ * Reads a packed enum field, which consists of a length header and a list of
+ * unsigned varints.
+ * @return {!Array.<number>}
+ */
+jspb.BinaryReader.prototype.readPackedEnum = function() {
+ return this.readPackedField_(this.decoder_.readEnum);
+};
+
+
+/**
+ * Reads a packed varint hash64 field, which consists of a length header and a
+ * list of varint hash64s.
+ * @return {!Array.<string>}
+ */
+jspb.BinaryReader.prototype.readPackedVarintHash64 = function() {
+ return this.readPackedField_(this.decoder_.readVarintHash64);
+};
+
+
+/**
+ * Reads a packed fixed hash64 field, which consists of a length header and a
+ * list of fixed hash64s.
+ * @return {!Array.<string>}
+ */
+jspb.BinaryReader.prototype.readPackedFixedHash64 = function() {
+ return this.readPackedField_(this.decoder_.readFixedHash64);
+};
diff --git a/js/binary/reader_test.js b/js/binary/reader_test.js
new file mode 100644
index 00000000..a6482610
--- /dev/null
+++ b/js/binary/reader_test.js
@@ -0,0 +1,889 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's binary protocol buffer reader.
+ *
+ * There are two particular magic numbers that need to be pointed out -
+ * 2^64-1025 is the largest number representable as both a double and an
+ * unsigned 64-bit integer, and 2^63-513 is the largest number representable as
+ * both a double and a signed 64-bit integer.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryDecoder');
+goog.require('jspb.BinaryReader');
+goog.require('jspb.BinaryWriter');
+
+
+
+describe('binaryReaderTest', function() {
+ /**
+ * Tests the reader instance cache.
+ * @suppress {visibility}
+ */
+ it('testInstanceCaches', function() {
+ var writer = new jspb.BinaryWriter();
+ var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+ writer.writeMessage(1, dummyMessage, goog.nullFunction);
+ writer.writeMessage(2, dummyMessage, goog.nullFunction);
+
+ var buffer = writer.getResultBuffer();
+
+ // Empty the instance caches.
+ jspb.BinaryReader.instanceCache_ = [];
+
+ // Allocating and then freeing three decoders should leave us with three in
+ // the cache.
+
+ var decoder1 = jspb.BinaryDecoder.alloc();
+ var decoder2 = jspb.BinaryDecoder.alloc();
+ var decoder3 = jspb.BinaryDecoder.alloc();
+ decoder1.free();
+ decoder2.free();
+ decoder3.free();
+
+ assertEquals(3, jspb.BinaryDecoder.instanceCache_.length);
+ assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+
+ // Allocating and then freeing a reader should remove one decoder from its
+ // cache, but it should stay stuck to the reader afterwards since we can't
+ // have a reader without a decoder.
+ jspb.BinaryReader.alloc().free();
+
+ assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+ assertEquals(1, jspb.BinaryReader.instanceCache_.length);
+
+ // Allocating a reader should remove a reader from the cache.
+ var reader = jspb.BinaryReader.alloc(buffer);
+
+ assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+ assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+
+ // Processing the message reuses the current reader.
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ reader.readMessage(dummyMessage, function() {
+ assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+ });
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ reader.readMessage(dummyMessage, function() {
+ assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+ });
+
+ assertEquals(false, reader.nextField());
+
+ assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+ assertEquals(0, jspb.BinaryReader.instanceCache_.length);
+
+ // Freeing the reader should put it back into the cache.
+ reader.free();
+
+ assertEquals(2, jspb.BinaryDecoder.instanceCache_.length);
+ assertEquals(1, jspb.BinaryReader.instanceCache_.length);
+ });
+
+
+ /**
+ * @param {number} x
+ * @return {number}
+ */
+ function truncate(x) {
+ var temp = new Float32Array(1);
+ temp[0] = x;
+ return temp[0];
+ }
+
+
+ /**
+ * Verifies that misuse of the reader class triggers assertions.
+ * @suppress {checkTypes|visibility}
+ */
+ it('testReadErrors', function() {
+ // Calling readMessage on a non-delimited field should trigger an
+ // assertion.
+ var reader = jspb.BinaryReader.alloc([8, 1]);
+ var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+ reader.nextField();
+ assertThrows(function() {
+ reader.readMessage(dummyMessage, goog.nullFunction);
+ });
+
+ // Reading past the end of the stream should trigger an assertion.
+ reader = jspb.BinaryReader.alloc([9, 1]);
+ reader.nextField();
+ assertThrows(function() {reader.readFixed64()});
+
+ // Reading past the end of a submessage should trigger an assertion.
+ reader = jspb.BinaryReader.alloc([10, 4, 13, 1, 1, 1]);
+ reader.nextField();
+ reader.readMessage(dummyMessage, function() {
+ reader.nextField();
+ assertThrows(function() {reader.readFixed32()});
+ });
+
+ // Skipping an invalid field should trigger an assertion.
+ reader = jspb.BinaryReader.alloc([12, 1]);
+ reader.nextWireType_ = 1000;
+ assertThrows(function() {reader.skipField()});
+
+ // Reading fields with the wrong wire type should assert.
+ reader = jspb.BinaryReader.alloc([9, 0, 0, 0, 0, 0, 0, 0, 0]);
+ reader.nextField();
+ assertThrows(function() {reader.readInt32()});
+ assertThrows(function() {reader.readInt32String()});
+ assertThrows(function() {reader.readInt64()});
+ assertThrows(function() {reader.readInt64String()});
+ assertThrows(function() {reader.readUint32()});
+ assertThrows(function() {reader.readUint32String()});
+ assertThrows(function() {reader.readUint64()});
+ assertThrows(function() {reader.readUint64String()});
+ assertThrows(function() {reader.readSint32()});
+ assertThrows(function() {reader.readBool()});
+ assertThrows(function() {reader.readEnum()});
+
+ reader = jspb.BinaryReader.alloc([8, 1]);
+ reader.nextField();
+ assertThrows(function() {reader.readFixed32()});
+ assertThrows(function() {reader.readFixed64()});
+ assertThrows(function() {reader.readSfixed32()});
+ assertThrows(function() {reader.readSfixed64()});
+ assertThrows(function() {reader.readFloat()});
+ assertThrows(function() {reader.readDouble()});
+
+ assertThrows(function() {reader.readString()});
+ assertThrows(function() {reader.readBytes()});
+ });
+
+
+ /**
+ * Tests encoding and decoding of unsigned field types.
+ * @param {Function} readField
+ * @param {Function} writeField
+ * @param {number} epsilon
+ * @param {number} upperLimit
+ * @param {Function} filter
+ * @private
+ * @suppress {missingProperties}
+ */
+ function doTestUnsignedField_(readField,
+ writeField, epsilon, upperLimit, filter) {
+ assertNotNull(readField);
+ assertNotNull(writeField);
+
+ var writer = new jspb.BinaryWriter();
+
+ // Encode zero and limits.
+ writeField.call(writer, 1, filter(0));
+ writeField.call(writer, 2, filter(epsilon));
+ writeField.call(writer, 3, filter(upperLimit));
+
+ // Encode positive values.
+ for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+ writeField.call(writer, 4, filter(cursor));
+ }
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ // Check zero and limits.
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ assertEquals(filter(0), readField.call(reader));
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ assertEquals(filter(epsilon), readField.call(reader));
+
+ reader.nextField();
+ assertEquals(3, reader.getFieldNumber());
+ assertEquals(filter(upperLimit), readField.call(reader));
+
+ // Check positive values.
+ for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+ reader.nextField();
+ if (4 != reader.getFieldNumber()) throw 'fail!';
+ if (filter(cursor) != readField.call(reader)) throw 'fail!';
+ }
+ };
+
+
+ /**
+ * Tests encoding and decoding of signed field types.
+ * @param {Function} readField
+ * @param {Function} writeField
+ * @param {number} epsilon
+ * @param {number} lowerLimit
+ * @param {number} upperLimit
+ * @param {Function} filter
+ * @private
+ * @suppress {missingProperties}
+ */
+ function doTestSignedField_(readField,
+ writeField, epsilon, lowerLimit, upperLimit, filter) {
+ var writer = new jspb.BinaryWriter();
+
+ // Encode zero and limits.
+ writeField.call(writer, 1, filter(lowerLimit));
+ writeField.call(writer, 2, filter(-epsilon));
+ writeField.call(writer, 3, filter(0));
+ writeField.call(writer, 4, filter(epsilon));
+ writeField.call(writer, 5, filter(upperLimit));
+
+ var inputValues = [];
+
+ // Encode negative values.
+ for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
+ var val = filter(cursor);
+ writeField.call(writer, 6, val);
+ inputValues.push({
+ fieldNumber: 6,
+ value: val
+ });
+ }
+
+ // Encode positive values.
+ for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
+ var val = filter(cursor);
+ writeField.call(writer, 7, val);
+ inputValues.push({
+ fieldNumber: 7,
+ value: val
+ });
+ }
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ // Check zero and limits.
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ assertEquals(filter(lowerLimit), readField.call(reader));
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ assertEquals(filter(-epsilon), readField.call(reader));
+
+ reader.nextField();
+ assertEquals(3, reader.getFieldNumber());
+ assertEquals(filter(0), readField.call(reader));
+
+ reader.nextField();
+ assertEquals(4, reader.getFieldNumber());
+ assertEquals(filter(epsilon), readField.call(reader));
+
+ reader.nextField();
+ assertEquals(5, reader.getFieldNumber());
+ assertEquals(filter(upperLimit), readField.call(reader));
+
+ for (var i = 0; i < inputValues.length; i++) {
+ var expected = inputValues[i];
+ reader.nextField();
+ assertEquals(expected.fieldNumber, reader.getFieldNumber());
+ assertEquals(expected.value, readField.call(reader));
+ }
+ };
+
+
+ /**
+ * Tests fields that use varint encoding.
+ */
+ it('testVarintFields', function() {
+ assertNotNull(jspb.BinaryReader.prototype.readUint32);
+ assertNotNull(jspb.BinaryReader.prototype.writeUint32);
+ assertNotNull(jspb.BinaryReader.prototype.readUint64);
+ assertNotNull(jspb.BinaryReader.prototype.writeUint64);
+ assertNotNull(jspb.BinaryReader.prototype.readBool);
+ assertNotNull(jspb.BinaryReader.prototype.writeBool);
+ doTestUnsignedField_(
+ jspb.BinaryReader.prototype.readUint32,
+ jspb.BinaryWriter.prototype.writeUint32,
+ 1, Math.pow(2, 32) - 1, Math.round);
+
+ doTestUnsignedField_(
+ jspb.BinaryReader.prototype.readUint64,
+ jspb.BinaryWriter.prototype.writeUint64,
+ 1, Math.pow(2, 64) - 1025, Math.round);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readInt32,
+ jspb.BinaryWriter.prototype.writeInt32,
+ 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readInt64,
+ jspb.BinaryWriter.prototype.writeInt64,
+ 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readEnum,
+ jspb.BinaryWriter.prototype.writeEnum,
+ 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+
+ doTestUnsignedField_(
+ jspb.BinaryReader.prototype.readBool,
+ jspb.BinaryWriter.prototype.writeBool,
+ 1, 1, function(x) { return !!x; });
+ });
+
+
+ /**
+ * Tests 64-bit fields that are handled as strings.
+ */
+ it('testStringInt64Fields', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var testSignedData = [
+ '2730538252207801776',
+ '-2688470994844604560',
+ '3398529779486536359',
+ '3568577411627971000',
+ '272477188847484900',
+ '-6649058714086158188',
+ '-7695254765712060806',
+ '-4525541438037104029',
+ '-4993706538836508568',
+ '4990160321893729138'
+ ];
+ var testUnsignedData = [
+ '7822732630241694882',
+ '6753602971916687352',
+ '2399935075244442116',
+ '8724292567325338867',
+ '16948784802625696584',
+ '4136275908516066934',
+ '3575388346793700364',
+ '5167142028379259461',
+ '1557573948689737699',
+ '17100725280812548567'
+ ];
+
+ for (var i = 0; i < testSignedData.length; i++) {
+ writer.writeInt64String(2 * i + 1, testSignedData[i]);
+ writer.writeUint64String(2 * i + 2, testUnsignedData[i]);
+ }
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ for (var i = 0; i < testSignedData.length; i++) {
+ reader.nextField();
+ assertEquals(2 * i + 1, reader.getFieldNumber());
+ assertEquals(testSignedData[i], reader.readInt64String());
+ reader.nextField();
+ assertEquals(2 * i + 2, reader.getFieldNumber());
+ assertEquals(testUnsignedData[i], reader.readUint64String());
+ }
+ });
+
+
+ /**
+ * Tests fields that use zigzag encoding.
+ */
+ it('testZigzagFields', function() {
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readSint32,
+ jspb.BinaryWriter.prototype.writeSint32,
+ 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readSint64,
+ jspb.BinaryWriter.prototype.writeSint64,
+ 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+ });
+
+
+ /**
+ * Tests fields that use fixed-length encoding.
+ */
+ it('testFixedFields', function() {
+ doTestUnsignedField_(
+ jspb.BinaryReader.prototype.readFixed32,
+ jspb.BinaryWriter.prototype.writeFixed32,
+ 1, Math.pow(2, 32) - 1, Math.round);
+
+ doTestUnsignedField_(
+ jspb.BinaryReader.prototype.readFixed64,
+ jspb.BinaryWriter.prototype.writeFixed64,
+ 1, Math.pow(2, 64) - 1025, Math.round);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readSfixed32,
+ jspb.BinaryWriter.prototype.writeSfixed32,
+ 1, -Math.pow(2, 31), Math.pow(2, 31) - 1, Math.round);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readSfixed64,
+ jspb.BinaryWriter.prototype.writeSfixed64,
+ 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
+ });
+
+
+ /**
+ * Tests floating point fields.
+ */
+ it('testFloatFields', function() {
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readFloat,
+ jspb.BinaryWriter.prototype.writeFloat,
+ jspb.BinaryConstants.FLOAT32_MIN,
+ -jspb.BinaryConstants.FLOAT32_MAX,
+ jspb.BinaryConstants.FLOAT32_MAX,
+ truncate);
+
+ doTestSignedField_(
+ jspb.BinaryReader.prototype.readDouble,
+ jspb.BinaryWriter.prototype.writeDouble,
+ jspb.BinaryConstants.FLOAT64_EPS * 10,
+ -jspb.BinaryConstants.FLOAT64_MIN,
+ jspb.BinaryConstants.FLOAT64_MIN,
+ function(x) { return x; });
+ });
+
+
+ /**
+ * Tests length-delimited string fields.
+ */
+ it('testStringFields', function() {
+ var s1 = 'The quick brown fox jumps over the lazy dog.';
+ var s2 = '人人生而自由,在尊嚴和權利上一律平等。';
+
+ var writer = new jspb.BinaryWriter();
+
+ writer.writeString(1, s1);
+ writer.writeString(2, s2);
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ assertEquals(s1, reader.readString());
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ assertEquals(s2, reader.readString());
+ });
+
+
+ /**
+ * Tests length-delimited byte fields.
+ */
+ it('testByteFields', function() {
+ var message = [];
+ var lowerLimit = 1;
+ var upperLimit = 256;
+ var scale = 1.1;
+
+ var writer = new jspb.BinaryWriter();
+
+ for (var cursor = lowerLimit; cursor < upperLimit; cursor *= 1.1) {
+ var len = Math.round(cursor);
+ var bytes = [];
+ for (var i = 0; i < len; i++) bytes.push(i % 256);
+
+ writer.writeBytes(len, bytes);
+ }
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ for (var cursor = lowerLimit; reader.nextField(); cursor *= 1.1) {
+ var len = Math.round(cursor);
+ if (len != reader.getFieldNumber()) throw 'fail!';
+
+ var bytes = reader.readBytes();
+ if (len != bytes.length) throw 'fail!';
+ for (var i = 0; i < bytes.length; i++) {
+ if (i % 256 != bytes[i]) throw 'fail!';
+ }
+ }
+ });
+
+
+ /**
+ * Tests nested messages.
+ */
+ it('testNesting', function() {
+ var writer = new jspb.BinaryWriter();
+ var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+ writer.writeInt32(1, 100);
+
+ // Add one message with 3 int fields.
+ writer.writeMessage(2, dummyMessage, function() {
+ writer.writeInt32(3, 300);
+ writer.writeInt32(4, 400);
+ writer.writeInt32(5, 500);
+ });
+
+ // Add one empty message.
+ writer.writeMessage(6, dummyMessage, goog.nullFunction);
+
+ writer.writeInt32(7, 700);
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ // Validate outermost message.
+
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ assertEquals(100, reader.readInt32());
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ reader.readMessage(dummyMessage, function() {
+ // Validate embedded message 1.
+ reader.nextField();
+ assertEquals(3, reader.getFieldNumber());
+ assertEquals(300, reader.readInt32());
+
+ reader.nextField();
+ assertEquals(4, reader.getFieldNumber());
+ assertEquals(400, reader.readInt32());
+
+ reader.nextField();
+ assertEquals(5, reader.getFieldNumber());
+ assertEquals(500, reader.readInt32());
+
+ assertEquals(false, reader.nextField());
+ });
+
+ reader.nextField();
+ assertEquals(6, reader.getFieldNumber());
+ reader.readMessage(dummyMessage, function() {
+ // Validate embedded message 2.
+
+ assertEquals(false, reader.nextField());
+ });
+
+ reader.nextField();
+ assertEquals(7, reader.getFieldNumber());
+ assertEquals(700, reader.readInt32());
+
+ assertEquals(false, reader.nextField());
+ });
+
+ /**
+ * Tests skipping fields of each type by interleaving them with sentinel
+ * values and skipping everything that's not a sentinel.
+ */
+ it('testSkipField', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var sentinel = 123456789;
+
+ // Write varint fields of different sizes.
+ writer.writeInt32(1, sentinel);
+ writer.writeInt32(1, 1);
+ writer.writeInt32(1, 1000);
+ writer.writeInt32(1, 1000000);
+ writer.writeInt32(1, 1000000000);
+
+ // Write fixed 64-bit encoded fields.
+ writer.writeInt32(2, sentinel);
+ writer.writeDouble(2, 1);
+ writer.writeFixed64(2, 1);
+ writer.writeSfixed64(2, 1);
+
+ // Write fixed 32-bit encoded fields.
+ writer.writeInt32(3, sentinel);
+ writer.writeFloat(3, 1);
+ writer.writeFixed32(3, 1);
+ writer.writeSfixed32(3, 1);
+
+ // Write delimited fields.
+ writer.writeInt32(4, sentinel);
+ writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ writer.writeString(4, 'The quick brown fox jumps over the lazy dog');
+
+ // Write a group with a nested group inside. We use the internal
+ // .rawWriteVarint() to ensure the tested wire data is what we want,
+ // independently of any serialization logic.
+ writer.writeInt32(5, sentinel);
+ // Start group, field 5.
+ writer.rawWriteVarint(
+ (5 << 3) + jspb.BinaryConstants.WireType.START_GROUP);
+ // Varint, field 42.
+ writer.rawWriteVarint(
+ (42 << 3) + jspb.BinaryConstants.WireType.VARINT);
+ // Varint data.
+ writer.rawWriteVarint(42);
+ // Start group, field 6.
+ writer.rawWriteVarint(
+ (6 << 3) + jspb.BinaryConstants.WireType.START_GROUP);
+ // Varint, field 84.
+ writer.rawWriteVarint(
+ (84 << 3) + jspb.BinaryConstants.WireType.VARINT);
+ writer.rawWriteVarint(42);
+ // End group, field 6.
+ writer.rawWriteVarint(
+ (6 << 3) + jspb.BinaryConstants.WireType.END_GROUP);
+ // End group, field 5.
+ writer.rawWriteVarint(
+ (5 << 3) + jspb.BinaryConstants.WireType.END_GROUP);
+
+ // Write final sentinel.
+ writer.writeInt32(6, sentinel);
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ function skip(field, count) {
+ for (var i = 0; i < count; i++) {
+ reader.nextField();
+ if (field != reader.getFieldNumber()) throw 'fail!';
+ reader.skipField();
+ }
+ }
+
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ assertEquals(sentinel, reader.readInt32());
+ skip(1, 4);
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ assertEquals(sentinel, reader.readInt32());
+ skip(2, 3);
+
+ reader.nextField();
+ assertEquals(3, reader.getFieldNumber());
+ assertEquals(sentinel, reader.readInt32());
+ skip(3, 3);
+
+ reader.nextField();
+ assertEquals(4, reader.getFieldNumber());
+ assertEquals(sentinel, reader.readInt32());
+ skip(4, 2);
+
+ reader.nextField();
+ assertEquals(5, reader.getFieldNumber());
+ assertEquals(sentinel, reader.readInt32());
+ skip(5, 1);
+
+ reader.nextField();
+ assertEquals(6, reader.getFieldNumber());
+ assertEquals(sentinel, reader.readInt32());
+ });
+
+
+ /**
+ * Tests packed fields.
+ */
+ it('testPackedFields', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var sentinel = 123456789;
+
+ var unsignedData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ var signedData = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10];
+ var floatData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
+ var doubleData = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10];
+ var boolData = [true, false, true, true, false, false, true, false];
+
+ for (var i = 0; i < floatData.length; i++) {
+ floatData[i] = truncate(floatData[i]);
+ }
+
+ writer.writeInt32(1, sentinel);
+
+ writer.writePackedInt32(2, signedData);
+ writer.writePackedInt64(2, signedData);
+ writer.writePackedUint32(2, unsignedData);
+ writer.writePackedUint64(2, unsignedData);
+ writer.writePackedSint32(2, signedData);
+ writer.writePackedSint64(2, signedData);
+ writer.writePackedFixed32(2, unsignedData);
+ writer.writePackedFixed64(2, unsignedData);
+ writer.writePackedSfixed32(2, signedData);
+ writer.writePackedSfixed64(2, signedData);
+ writer.writePackedFloat(2, floatData);
+ writer.writePackedDouble(2, doubleData);
+ writer.writePackedBool(2, boolData);
+ writer.writePackedEnum(2, unsignedData);
+
+ writer.writeInt32(3, sentinel);
+
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ reader.nextField();
+ assertEquals(sentinel, reader.readInt32());
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedInt32(), signedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedInt64(), signedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedUint32(), unsignedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedUint64(), unsignedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedSint32(), signedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedSint64(), signedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedFixed32(), unsignedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedFixed64(), unsignedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedSfixed32(), signedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedSfixed64(), signedData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedFloat(), floatData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedDouble(), doubleData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedBool(), boolData);
+
+ reader.nextField();
+ assertElementsEquals(reader.readPackedEnum(), unsignedData);
+
+ reader.nextField();
+ assertEquals(sentinel, reader.readInt32());
+ });
+
+
+ /**
+ * Byte blobs inside nested messages should always have their byte offset set
+ * relative to the start of the outermost blob, not the start of their parent
+ * blob.
+ */
+ it('testNestedBlobs', function() {
+ // Create a proto consisting of two nested messages, with the inner one
+ // containing a blob of bytes.
+
+ var fieldTag = (1 << 3) | jspb.BinaryConstants.WireType.DELIMITED;
+ var blob = [1, 2, 3, 4, 5];
+ var writer = new jspb.BinaryWriter();
+ var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+ writer.writeMessage(1, dummyMessage, function() {
+ writer.writeMessage(1, dummyMessage, function() {
+ writer.writeBytes(1, blob);
+ });
+ });
+
+ // Peel off the outer two message layers. Each layer should have two bytes
+ // of overhead, one for the field tag and one for the length of the inner
+ // blob.
+
+ var decoder1 = new jspb.BinaryDecoder(writer.getResultBuffer());
+ assertEquals(fieldTag, decoder1.readUnsignedVarint32());
+ assertEquals(blob.length + 4, decoder1.readUnsignedVarint32());
+
+ var decoder2 = new jspb.BinaryDecoder(decoder1.readBytes(blob.length + 4));
+ assertEquals(fieldTag, decoder2.readUnsignedVarint32());
+ assertEquals(blob.length + 2, decoder2.readUnsignedVarint32());
+
+ assertEquals(fieldTag, decoder2.readUnsignedVarint32());
+ assertEquals(blob.length, decoder2.readUnsignedVarint32());
+ var bytes = decoder2.readBytes(blob.length);
+
+ assertElementsEquals(bytes, blob);
+ });
+
+
+ /**
+ * Tests read callbacks.
+ */
+ it('testReadCallbacks', function() {
+ var writer = new jspb.BinaryWriter();
+ var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+ // Add an int, a submessage, and another int.
+ writer.writeInt32(1, 100);
+
+ writer.writeMessage(2, dummyMessage, function() {
+ writer.writeInt32(3, 300);
+ writer.writeInt32(4, 400);
+ writer.writeInt32(5, 500);
+ });
+
+ writer.writeInt32(7, 700);
+
+ // Create the reader and register a custom read callback.
+ var reader = jspb.BinaryReader.alloc(writer.getResultBuffer());
+
+ /**
+ * @param {!jspb.BinaryReader} reader
+ * @return {*}
+ */
+ function readCallback(reader) {
+ reader.nextField();
+ assertEquals(3, reader.getFieldNumber());
+ assertEquals(300, reader.readInt32());
+
+ reader.nextField();
+ assertEquals(4, reader.getFieldNumber());
+ assertEquals(400, reader.readInt32());
+
+ reader.nextField();
+ assertEquals(5, reader.getFieldNumber());
+ assertEquals(500, reader.readInt32());
+
+ assertEquals(false, reader.nextField());
+ };
+
+ reader.registerReadCallback('readCallback', readCallback);
+
+ // Read the container message.
+ reader.nextField();
+ assertEquals(1, reader.getFieldNumber());
+ assertEquals(100, reader.readInt32());
+
+ reader.nextField();
+ assertEquals(2, reader.getFieldNumber());
+ reader.readMessage(dummyMessage, function() {
+ // Decode the embedded message using the registered callback.
+ reader.runReadCallback('readCallback');
+ });
+
+ reader.nextField();
+ assertEquals(7, reader.getFieldNumber());
+ assertEquals(700, reader.readInt32());
+
+ assertEquals(false, reader.nextField());
+ });
+});
diff --git a/js/binary/utils.js b/js/binary/utils.js
new file mode 100644
index 00000000..92600389
--- /dev/null
+++ b/js/binary/utils.js
@@ -0,0 +1,979 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview This file contains helper code used by jspb.BinaryReader
+ * and BinaryWriter.
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.provide('jspb.utils');
+
+goog.require('goog.asserts');
+goog.require('goog.crypt.base64');
+goog.require('goog.string');
+goog.require('jspb.BinaryConstants');
+
+
+/**
+ * Javascript can't natively handle 64-bit data types, so to manipulate them we
+ * have to split them into two 32-bit halves and do the math manually.
+ *
+ * Instead of instantiating and passing small structures around to do this, we
+ * instead just use two global temporary values. This one stores the low 32
+ * bits of a split value - for example, if the original value was a 64-bit
+ * integer, this temporary value will contain the low 32 bits of that integer.
+ * If the original value was a double, this temporary value will contain the
+ * low 32 bits of the binary representation of that double, etcetera.
+ * @type {number}
+ */
+jspb.utils.split64Low = 0;
+
+
+/**
+ * And correspondingly, this temporary variable will contain the high 32 bits
+ * of whatever value was split.
+ * @type {number}
+ */
+jspb.utils.split64High = 0;
+
+
+/**
+ * Splits an unsigned Javascript integer into two 32-bit halves and stores it
+ * in the temp values above.
+ * @param {number} value The number to split.
+ */
+jspb.utils.splitUint64 = function(value) {
+ // Extract low 32 bits and high 32 bits as unsigned integers.
+ var lowBits = value >>> 0;
+ var highBits = Math.floor((value - lowBits) /
+ jspb.BinaryConstants.TWO_TO_32) >>> 0;
+
+ jspb.utils.split64Low = lowBits;
+ jspb.utils.split64High = highBits;
+};
+
+
+/**
+ * Splits a signed Javascript integer into two 32-bit halves and stores it in
+ * the temp values above.
+ * @param {number} value The number to split.
+ */
+jspb.utils.splitInt64 = function(value) {
+ // Convert to sign-magnitude representation.
+ var sign = (value < 0);
+ value = Math.abs(value);
+
+ // Extract low 32 bits and high 32 bits as unsigned integers.
+ var lowBits = value >>> 0;
+ var highBits = Math.floor((value - lowBits) /
+ jspb.BinaryConstants.TWO_TO_32);
+ highBits = highBits >>> 0;
+
+ // Perform two's complement conversion if the sign bit was set.
+ if (sign) {
+ highBits = ~highBits >>> 0;
+ lowBits = ~lowBits >>> 0;
+ lowBits += 1;
+ if (lowBits > 0xFFFFFFFF) {
+ lowBits = 0;
+ highBits++;
+ if (highBits > 0xFFFFFFFF) highBits = 0;
+ }
+ }
+
+ jspb.utils.split64Low = lowBits;
+ jspb.utils.split64High = highBits;
+};
+
+
+/**
+ * Convers a signed Javascript integer into zigzag format, splits it into two
+ * 32-bit halves, and stores it in the temp values above.
+ * @param {number} value The number to split.
+ */
+jspb.utils.splitZigzag64 = function(value) {
+ // Convert to sign-magnitude and scale by 2 before we split the value.
+ var sign = (value < 0);
+ value = Math.abs(value) * 2;
+
+ jspb.utils.splitUint64(value);
+ var lowBits = jspb.utils.split64Low;
+ var highBits = jspb.utils.split64High;
+
+ // If the value is negative, subtract 1 from the split representation so we
+ // don't lose the sign bit due to precision issues.
+ if (sign) {
+ if (lowBits == 0) {
+ if (highBits == 0) {
+ lowBits = 0xFFFFFFFF;
+ highBits = 0xFFFFFFFF;
+ } else {
+ highBits--;
+ lowBits = 0xFFFFFFFF;
+ }
+ } else {
+ lowBits--;
+ }
+ }
+
+ jspb.utils.split64Low = lowBits;
+ jspb.utils.split64High = highBits;
+};
+
+
+/**
+ * Converts a floating-point number into 32-bit IEEE representation and stores
+ * it in the temp values above.
+ * @param {number} value
+ */
+jspb.utils.splitFloat32 = function(value) {
+ var sign = (value < 0) ? 1 : 0;
+ value = sign ? -value : value;
+ var exp;
+ var mant;
+
+ // Handle zeros.
+ if (value === 0) {
+ if ((1 / value) > 0) {
+ // Positive zero.
+ jspb.utils.split64High = 0;
+ jspb.utils.split64Low = 0x00000000;
+ } else {
+ // Negative zero.
+ jspb.utils.split64High = 0;
+ jspb.utils.split64Low = 0x80000000;
+ }
+ return;
+ }
+
+ // Handle nans.
+ if (isNaN(value)) {
+ jspb.utils.split64High = 0;
+ jspb.utils.split64Low = 0x7FFFFFFF;
+ return;
+ }
+
+ // Handle infinities.
+ if (value > jspb.BinaryConstants.FLOAT32_MAX) {
+ jspb.utils.split64High = 0;
+ jspb.utils.split64Low = ((sign << 31) | (0x7F800000)) >>> 0;
+ return;
+ }
+
+ // Handle denormals.
+ if (value < jspb.BinaryConstants.FLOAT32_MIN) {
+ // Number is a denormal.
+ mant = Math.round(value / Math.pow(2, -149));
+ jspb.utils.split64High = 0;
+ jspb.utils.split64Low = ((sign << 31) | mant) >>> 0;
+ return;
+ }
+
+ exp = Math.floor(Math.log(value) / Math.LN2);
+ mant = value * Math.pow(2, -exp);
+ mant = Math.round(mant * jspb.BinaryConstants.TWO_TO_23) & 0x7FFFFF;
+
+ jspb.utils.split64High = 0;
+ jspb.utils.split64Low = ((sign << 31) | ((exp + 127) << 23) | mant) >>> 0;
+};
+
+
+/**
+ * Converts a floating-point number into 64-bit IEEE representation and stores
+ * it in the temp values above.
+ * @param {number} value
+ */
+jspb.utils.splitFloat64 = function(value) {
+ var sign = (value < 0) ? 1 : 0;
+ value = sign ? -value : value;
+
+ // Handle zeros.
+ if (value === 0) {
+ if ((1 / value) > 0) {
+ // Positive zero.
+ jspb.utils.split64High = 0x00000000;
+ jspb.utils.split64Low = 0x00000000;
+ } else {
+ // Negative zero.
+ jspb.utils.split64High = 0x80000000;
+ jspb.utils.split64Low = 0x00000000;
+ }
+ return;
+ }
+
+ // Handle nans.
+ if (isNaN(value)) {
+ jspb.utils.split64High = 0x7FFFFFFF;
+ jspb.utils.split64Low = 0xFFFFFFFF;
+ return;
+ }
+
+ // Handle infinities.
+ if (value > jspb.BinaryConstants.FLOAT64_MAX) {
+ jspb.utils.split64High = ((sign << 31) | (0x7FF00000)) >>> 0;
+ jspb.utils.split64Low = 0;
+ return;
+ }
+
+ // Handle denormals.
+ if (value < jspb.BinaryConstants.FLOAT64_MIN) {
+ // Number is a denormal.
+ var mant = value / Math.pow(2, -1074);
+ var mantHigh = (mant / jspb.BinaryConstants.TWO_TO_32);
+ jspb.utils.split64High = ((sign << 31) | mantHigh) >>> 0;
+ jspb.utils.split64Low = (mant >>> 0);
+ return;
+ }
+
+ var exp = Math.floor(Math.log(value) / Math.LN2);
+ if (exp == 1024) exp = 1023;
+ var mant = value * Math.pow(2, -exp);
+
+ var mantHigh = (mant * jspb.BinaryConstants.TWO_TO_20) & 0xFFFFF;
+ var mantLow = (mant * jspb.BinaryConstants.TWO_TO_52) >>> 0;
+
+ jspb.utils.split64High =
+ ((sign << 31) | ((exp + 1023) << 20) | mantHigh) >>> 0;
+ jspb.utils.split64Low = mantLow;
+};
+
+
+/**
+ * Converts an 8-character hash string into two 32-bit numbers and stores them
+ * in the temp values above.
+ * @param {string} hash
+ */
+jspb.utils.splitHash64 = function(hash) {
+ var a = hash.charCodeAt(0);
+ var b = hash.charCodeAt(1);
+ var c = hash.charCodeAt(2);
+ var d = hash.charCodeAt(3);
+ var e = hash.charCodeAt(4);
+ var f = hash.charCodeAt(5);
+ var g = hash.charCodeAt(6);
+ var h = hash.charCodeAt(7);
+
+ jspb.utils.split64Low = (a + (b << 8) + (c << 16) + (d << 24)) >>> 0;
+ jspb.utils.split64High = (e + (f << 8) + (g << 16) + (h << 24)) >>> 0;
+};
+
+
+/**
+ * Joins two 32-bit values into a 64-bit unsigned integer. Precision will be
+ * lost if the result is greater than 2^52.
+ * @param {number} bitsLow
+ * @param {number} bitsHigh
+ * @return {number}
+ */
+jspb.utils.joinUint64 = function(bitsLow, bitsHigh) {
+ return bitsHigh * jspb.BinaryConstants.TWO_TO_32 + bitsLow;
+};
+
+
+/**
+ * Joins two 32-bit values into a 64-bit signed integer. Precision will be lost
+ * if the result is greater than 2^52.
+ * @param {number} bitsLow
+ * @param {number} bitsHigh
+ * @return {number}
+ */
+jspb.utils.joinInt64 = function(bitsLow, bitsHigh) {
+ // If the high bit is set, do a manual two's complement conversion.
+ var sign = (bitsHigh & 0x80000000);
+ if (sign) {
+ bitsLow = (~bitsLow + 1) >>> 0;
+ bitsHigh = ~bitsHigh >>> 0;
+ if (bitsLow == 0) {
+ bitsHigh = (bitsHigh + 1) >>> 0;
+ }
+ }
+
+ var result = jspb.utils.joinUint64(bitsLow, bitsHigh);
+ return sign ? -result : result;
+};
+
+
+/**
+ * Joins two 32-bit values into a 64-bit unsigned integer and applies zigzag
+ * decoding. Precision will be lost if the result is greater than 2^52.
+ * @param {number} bitsLow
+ * @param {number} bitsHigh
+ * @return {number}
+ */
+jspb.utils.joinZigzag64 = function(bitsLow, bitsHigh) {
+ // Extract the sign bit and shift right by one.
+ var sign = bitsLow & 1;
+ bitsLow = ((bitsLow >>> 1) | (bitsHigh << 31)) >>> 0;
+ bitsHigh = bitsHigh >>> 1;
+
+ // Increment the split value if the sign bit was set.
+ if (sign) {
+ bitsLow = (bitsLow + 1) >>> 0;
+ if (bitsLow == 0) {
+ bitsHigh = (bitsHigh + 1) >>> 0;
+ }
+ }
+
+ var result = jspb.utils.joinUint64(bitsLow, bitsHigh);
+ return sign ? -result : result;
+};
+
+
+/**
+ * Joins two 32-bit values into a 32-bit IEEE floating point number and
+ * converts it back into a Javascript number.
+ * @param {number} bitsLow The low 32 bits of the binary number;
+ * @param {number} bitsHigh The high 32 bits of the binary number.
+ * @return {number}
+ */
+jspb.utils.joinFloat32 = function(bitsLow, bitsHigh) {
+ var sign = ((bitsLow >> 31) * 2 + 1);
+ var exp = (bitsLow >>> 23) & 0xFF;
+ var mant = bitsLow & 0x7FFFFF;
+
+ if (exp == 0xFF) {
+ if (mant) {
+ return NaN;
+ } else {
+ return sign * Infinity;
+ }
+ }
+
+ if (exp == 0) {
+ // Denormal.
+ return sign * Math.pow(2, -149) * mant;
+ } else {
+ return sign * Math.pow(2, exp - 150) *
+ (mant + Math.pow(2, 23));
+ }
+};
+
+
+/**
+ * Joins two 32-bit values into a 64-bit IEEE floating point number and
+ * converts it back into a Javascript number.
+ * @param {number} bitsLow The low 32 bits of the binary number;
+ * @param {number} bitsHigh The high 32 bits of the binary number.
+ * @return {number}
+ */
+jspb.utils.joinFloat64 = function(bitsLow, bitsHigh) {
+ var sign = ((bitsHigh >> 31) * 2 + 1);
+ var exp = (bitsHigh >>> 20) & 0x7FF;
+ var mant = jspb.BinaryConstants.TWO_TO_32 * (bitsHigh & 0xFFFFF) + bitsLow;
+
+ if (exp == 0x7FF) {
+ if (mant) {
+ return NaN;
+ } else {
+ return sign * Infinity;
+ }
+ }
+
+ if (exp == 0) {
+ // Denormal.
+ return sign * Math.pow(2, -1074) * mant;
+ } else {
+ return sign * Math.pow(2, exp - 1075) *
+ (mant + jspb.BinaryConstants.TWO_TO_52);
+ }
+};
+
+
+/**
+ * Joins two 32-bit values into an 8-character hash string.
+ * @param {number} bitsLow
+ * @param {number} bitsHigh
+ * @return {string}
+ */
+jspb.utils.joinHash64 = function(bitsLow, bitsHigh) {
+ var a = (bitsLow >>> 0) & 0xFF;
+ var b = (bitsLow >>> 8) & 0xFF;
+ var c = (bitsLow >>> 16) & 0xFF;
+ var d = (bitsLow >>> 24) & 0xFF;
+ var e = (bitsHigh >>> 0) & 0xFF;
+ var f = (bitsHigh >>> 8) & 0xFF;
+ var g = (bitsHigh >>> 16) & 0xFF;
+ var h = (bitsHigh >>> 24) & 0xFF;
+
+ return String.fromCharCode(a, b, c, d, e, f, g, h);
+};
+
+
+/**
+ * Individual digits for number->string conversion.
+ * @const {!Array.<number>}
+ */
+jspb.utils.DIGITS = [
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+];
+
+
+/**
+ * Losslessly converts a 64-bit unsigned integer in 32:32 split representation
+ * into a decimal string.
+ * @param {number} bitsLow The low 32 bits of the binary number;
+ * @param {number} bitsHigh The high 32 bits of the binary number.
+ * @return {string} The binary number represented as a string.
+ */
+jspb.utils.joinUnsignedDecimalString = function(bitsLow, bitsHigh) {
+ // Skip the expensive conversion if the number is small enough to use the
+ // built-in conversions.
+ if (bitsHigh <= 0x1FFFFF) {
+ return '' + (jspb.BinaryConstants.TWO_TO_32 * bitsHigh + bitsLow);
+ }
+
+ // What this code is doing is essentially converting the input number from
+ // base-2 to base-1e7, which allows us to represent the 64-bit range with
+ // only 3 (very large) digits. Those digits are then trivial to convert to
+ // a base-10 string.
+
+ // The magic numbers used here are -
+ // 2^24 = 16777216 = (1,6777216) in base-1e7.
+ // 2^48 = 281474976710656 = (2,8147497,6710656) in base-1e7.
+
+ // Split 32:32 representation into 16:24:24 representation so our
+ // intermediate digits don't overflow.
+ var low = bitsLow & 0xFFFFFF;
+ var mid = (((bitsLow >>> 24) | (bitsHigh << 8)) >>> 0) & 0xFFFFFF;
+ var high = (bitsHigh >> 16) & 0xFFFF;
+
+ // Assemble our three base-1e7 digits, ignoring carries. The maximum
+ // value in a digit at this step is representable as a 48-bit integer, which
+ // can be stored in a 64-bit floating point number.
+ var digitA = low + (mid * 6777216) + (high * 6710656);
+ var digitB = mid + (high * 8147497);
+ var digitC = (high * 2);
+
+ // Apply carries from A to B and from B to C.
+ var base = 10000000;
+ if (digitA >= base) {
+ digitB += Math.floor(digitA / base);
+ digitA %= base;
+ }
+
+ if (digitB >= base) {
+ digitC += Math.floor(digitB / base);
+ digitB %= base;
+ }
+
+ // Convert base-1e7 digits to base-10, omitting leading zeroes.
+ var table = jspb.utils.DIGITS;
+ var start = false;
+ var result = '';
+
+ function emit(digit) {
+ var temp = base;
+ for (var i = 0; i < 7; i++) {
+ temp /= 10;
+ var decimalDigit = ((digit / temp) % 10) >>> 0;
+ if ((decimalDigit == 0) && !start) continue;
+ start = true;
+ result += table[decimalDigit];
+ }
+ }
+
+ if (digitC || start) emit(digitC);
+ if (digitB || start) emit(digitB);
+ if (digitA || start) emit(digitA);
+
+ return result;
+};
+
+
+/**
+ * Losslessly converts a 64-bit signed integer in 32:32 split representation
+ * into a decimal string.
+ * @param {number} bitsLow The low 32 bits of the binary number;
+ * @param {number} bitsHigh The high 32 bits of the binary number.
+ * @return {string} The binary number represented as a string.
+ */
+jspb.utils.joinSignedDecimalString = function(bitsLow, bitsHigh) {
+ // If we're treating the input as a signed value and the high bit is set, do
+ // a manual two's complement conversion before the decimal conversion.
+ var negative = (bitsHigh & 0x80000000);
+ if (negative) {
+ bitsLow = (~bitsLow + 1) >>> 0;
+ var carry = (bitsLow == 0) ? 1 : 0;
+ bitsHigh = (~bitsHigh + carry) >>> 0;
+ }
+
+ var result = jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
+ return negative ? '-' + result : result;
+};
+
+
+/**
+ * Convert an 8-character hash string representing either a signed or unsigned
+ * 64-bit integer into its decimal representation without losing accuracy.
+ * @param {string} hash The hash string to convert.
+ * @param {boolean} signed True if we should treat the hash string as encoding
+ * a signed integer.
+ * @return {string}
+ */
+jspb.utils.hash64ToDecimalString = function(hash, signed) {
+ jspb.utils.splitHash64(hash);
+ var bitsLow = jspb.utils.split64Low;
+ var bitsHigh = jspb.utils.split64High;
+ return signed ?
+ jspb.utils.joinSignedDecimalString(bitsLow, bitsHigh) :
+ jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
+};
+
+
+/**
+ * Converts an array of 8-character hash strings into their decimal
+ * representations.
+ * @param {!Array.<string>} hashes The array of hash strings to convert.
+ * @param {boolean} signed True if we should treat the hash string as encoding
+ * a signed integer.
+ * @return {!Array.<string>}
+ */
+jspb.utils.hash64ArrayToDecimalStrings = function(hashes, signed) {
+ var result = new Array(hashes.length);
+ for (var i = 0; i < hashes.length; i++) {
+ result[i] = jspb.utils.hash64ToDecimalString(hashes[i], signed);
+ }
+ return result;
+};
+
+
+/**
+ * Converts an 8-character hash string into its hexadecimal representation.
+ * @param {string} hash
+ * @return {string}
+ */
+jspb.utils.hash64ToHexString = function(hash) {
+ var temp = new Array(18);
+ temp[0] = '0';
+ temp[1] = 'x';
+
+ for (var i = 0; i < 8; i++) {
+ var c = hash.charCodeAt(7 - i);
+ temp[i * 2 + 2] = jspb.utils.DIGITS[c >> 4];
+ temp[i * 2 + 3] = jspb.utils.DIGITS[c & 0xF];
+ }
+
+ var result = temp.join('');
+ return result;
+};
+
+
+/**
+ * Converts a '0x<16 digits>' hex string into its hash string representation.
+ * @param {string} hex
+ * @return {string}
+ */
+jspb.utils.hexStringToHash64 = function(hex) {
+ hex = hex.toLowerCase();
+ goog.asserts.assert(hex.length == 18);
+ goog.asserts.assert(hex[0] == '0');
+ goog.asserts.assert(hex[1] == 'x');
+
+ var result = '';
+ for (var i = 0; i < 8; i++) {
+ var hi = jspb.utils.DIGITS.indexOf(hex[i * 2 + 2]);
+ var lo = jspb.utils.DIGITS.indexOf(hex[i * 2 + 3]);
+ result = String.fromCharCode(hi * 16 + lo) + result;
+ }
+
+ return result;
+};
+
+
+/**
+ * Convert an 8-character hash string representing either a signed or unsigned
+ * 64-bit integer into a Javascript number. Will lose accuracy if the result is
+ * larger than 2^52.
+ * @param {string} hash The hash string to convert.
+ * @param {boolean} signed True if the has should be interpreted as a signed
+ * number.
+ * @return {number}
+ */
+jspb.utils.hash64ToNumber = function(hash, signed) {
+ jspb.utils.splitHash64(hash);
+ var bitsLow = jspb.utils.split64Low;
+ var bitsHigh = jspb.utils.split64High;
+ return signed ? jspb.utils.joinInt64(bitsLow, bitsHigh) :
+ jspb.utils.joinUint64(bitsLow, bitsHigh);
+};
+
+
+/**
+ * Convert a Javascript number into an 8-character hash string. Will lose
+ * precision if the value is non-integral or greater than 2^64.
+ * @param {number} value The integer to convert.
+ * @return {string}
+ */
+jspb.utils.numberToHash64 = function(value) {
+ jspb.utils.splitInt64(value);
+ return jspb.utils.joinHash64(jspb.utils.split64Low,
+ jspb.utils.split64High);
+};
+
+
+/**
+ * Counts the number of contiguous varints in a buffer.
+ * @param {!Uint8Array} buffer The buffer to scan.
+ * @param {number} start The starting point in the buffer to scan.
+ * @param {number} end The end point in the buffer to scan.
+ * @return {number} The number of varints in the buffer.
+ */
+jspb.utils.countVarints = function(buffer, start, end) {
+ // Count how many high bits of each byte were set in the buffer.
+ var count = 0;
+ for (var i = start; i < end; i++) {
+ count += buffer[i] >> 7;
+ }
+
+ // The number of varints in the buffer equals the size of the buffer minus
+ // the number of non-terminal bytes in the buffer (those with the high bit
+ // set).
+ return (end - start) - count;
+};
+
+
+/**
+ * Counts the number of contiguous varint fields with the given field number in
+ * the buffer.
+ * @param {!Uint8Array} buffer The buffer to scan.
+ * @param {number} start The starting point in the buffer to scan.
+ * @param {number} end The end point in the buffer to scan.
+ * @param {number} field The field number to count.
+ * @return {number} The number of matching fields in the buffer.
+ */
+jspb.utils.countVarintFields = function(buffer, start, end, field) {
+ var count = 0;
+ var cursor = start;
+ var tag = field * 8 + jspb.BinaryConstants.WireType.VARINT;
+
+ if (tag < 128) {
+ // Single-byte field tag, we can use a slightly quicker count.
+ while (cursor < end) {
+ // Skip the field tag, or exit if we find a non-matching tag.
+ if (buffer[cursor++] != tag) return count;
+
+ // Field tag matches, we've found a valid field.
+ count++;
+
+ // Skip the varint.
+ while (1) {
+ var x = buffer[cursor++];
+ if ((x & 0x80) == 0) break;
+ }
+ }
+ } else {
+ while (cursor < end) {
+ // Skip the field tag, or exit if we find a non-matching tag.
+ var temp = tag;
+ while (temp > 128) {
+ if (buffer[cursor] != ((temp & 0x7F) | 0x80)) return count;
+ cursor++;
+ temp >>= 7;
+ }
+ if (buffer[cursor++] != temp) return count;
+
+ // Field tag matches, we've found a valid field.
+ count++;
+
+ // Skip the varint.
+ while (1) {
+ var x = buffer[cursor++];
+ if ((x & 0x80) == 0) break;
+ }
+ }
+ }
+ return count;
+};
+
+
+/**
+ * Counts the number of contiguous fixed32 fields with the given tag in the
+ * buffer.
+ * @param {!Uint8Array} buffer The buffer to scan.
+ * @param {number} start The starting point in the buffer to scan.
+ * @param {number} end The end point in the buffer to scan.
+ * @param {number} tag The tag value to count.
+ * @param {number} stride The number of bytes to skip per field.
+ * @return {number} The number of fields with a matching tag in the buffer.
+ * @private
+ */
+jspb.utils.countFixedFields_ =
+ function(buffer, start, end, tag, stride) {
+ var count = 0;
+ var cursor = start;
+
+ if (tag < 128) {
+ // Single-byte field tag, we can use a slightly quicker count.
+ while (cursor < end) {
+ // Skip the field tag, or exit if we find a non-matching tag.
+ if (buffer[cursor++] != tag) return count;
+
+ // Field tag matches, we've found a valid field.
+ count++;
+
+ // Skip the value.
+ cursor += stride;
+ }
+ } else {
+ while (cursor < end) {
+ // Skip the field tag, or exit if we find a non-matching tag.
+ var temp = tag;
+ while (temp > 128) {
+ if (buffer[cursor++] != ((temp & 0x7F) | 0x80)) return count;
+ temp >>= 7;
+ }
+ if (buffer[cursor++] != temp) return count;
+
+ // Field tag matches, we've found a valid field.
+ count++;
+
+ // Skip the value.
+ cursor += stride;
+ }
+ }
+ return count;
+};
+
+
+/**
+ * Counts the number of contiguous fixed32 fields with the given field number
+ * in the buffer.
+ * @param {!Uint8Array} buffer The buffer to scan.
+ * @param {number} start The starting point in the buffer to scan.
+ * @param {number} end The end point in the buffer to scan.
+ * @param {number} field The field number to count.
+ * @return {number} The number of matching fields in the buffer.
+ */
+jspb.utils.countFixed32Fields = function(buffer, start, end, field) {
+ var tag = field * 8 + jspb.BinaryConstants.WireType.FIXED32;
+ return jspb.utils.countFixedFields_(buffer, start, end, tag, 4);
+};
+
+
+/**
+ * Counts the number of contiguous fixed64 fields with the given field number
+ * in the buffer.
+ * @param {!Uint8Array} buffer The buffer to scan.
+ * @param {number} start The starting point in the buffer to scan.
+ * @param {number} end The end point in the buffer to scan.
+ * @param {number} field The field number to count
+ * @return {number} The number of matching fields in the buffer.
+ */
+jspb.utils.countFixed64Fields = function(buffer, start, end, field) {
+ var tag = field * 8 + jspb.BinaryConstants.WireType.FIXED64;
+ return jspb.utils.countFixedFields_(buffer, start, end, tag, 8);
+};
+
+
+/**
+ * Counts the number of contiguous delimited fields with the given field number
+ * in the buffer.
+ * @param {!Uint8Array} buffer The buffer to scan.
+ * @param {number} start The starting point in the buffer to scan.
+ * @param {number} end The end point in the buffer to scan.
+ * @param {number} field The field number to count.
+ * @return {number} The number of matching fields in the buffer.
+ */
+jspb.utils.countDelimitedFields = function(buffer, start, end, field) {
+ var count = 0;
+ var cursor = start;
+ var tag = field * 8 + jspb.BinaryConstants.WireType.DELIMITED;
+
+ while (cursor < end) {
+ // Skip the field tag, or exit if we find a non-matching tag.
+ var temp = tag;
+ while (temp > 128) {
+ if (buffer[cursor++] != ((temp & 0x7F) | 0x80)) return count;
+ temp >>= 7;
+ }
+ if (buffer[cursor++] != temp) return count;
+
+ // Field tag matches, we've found a valid field.
+ count++;
+
+ // Decode the length prefix.
+ var length = 0;
+ var shift = 1;
+ while (1) {
+ temp = buffer[cursor++];
+ length += (temp & 0x7f) * shift;
+ shift *= 128;
+ if ((temp & 0x80) == 0) break;
+ }
+
+ // Advance the cursor past the blob.
+ cursor += length;
+ }
+ return count;
+};
+
+
+/**
+ * Clones a scalar field. Pulling this out to a helper method saves us a few
+ * bytes of generated code.
+ * @param {Array} array
+ * @return {Array}
+ */
+jspb.utils.cloneRepeatedScalarField = function(array) {
+ return array ? array.slice() : null;
+};
+
+
+/**
+ * Clones an array of messages using the provided cloner function.
+ * @param {Array.<jspb.BinaryMessage>} messages
+ * @param {jspb.ClonerFunction} cloner
+ * @return {Array.<jspb.BinaryMessage>}
+ */
+jspb.utils.cloneRepeatedMessageField = function(messages, cloner) {
+ if (messages === null) return null;
+ var result = [];
+ for (var i = 0; i < messages.length; i++) {
+ result.push(cloner(messages[i]));
+ }
+ return result;
+};
+
+
+/**
+ * Clones an array of byte blobs.
+ * @param {Array.<Uint8Array>} blobs
+ * @return {Array.<Uint8Array>}
+ */
+jspb.utils.cloneRepeatedBlobField = function(blobs) {
+ if (blobs === null) return null;
+ var result = [];
+ for (var i = 0; i < blobs.length; i++) {
+ result.push(new Uint8Array(blobs[i]));
+ }
+ return result;
+};
+
+
+/**
+ * String-ify bytes for text format. Should be optimized away in non-debug.
+ * The returned string uses \xXX escapes for all values and is itself quoted.
+ * [1, 31] serializes to '"\x01\x1f"'.
+ * @param {jspb.ByteSource} byteSource The bytes to serialize.
+ * @param {boolean=} opt_stringIsRawBytes The string is interpreted as a series
+ * of raw bytes rather than base64 data.
+ * @return {string} Stringified bytes for text format.
+ */
+jspb.utils.debugBytesToTextFormat = function(byteSource,
+ opt_stringIsRawBytes) {
+ var s = '"';
+ if (byteSource) {
+ var bytes =
+ jspb.utils.byteSourceToUint8Array(byteSource, opt_stringIsRawBytes);
+ for (var i = 0; i < bytes.length; i++) {
+ s += '\\x';
+ if (bytes[i] < 16) s += '0';
+ s += bytes[i].toString(16);
+ }
+ }
+ return s + '"';
+};
+
+
+/**
+ * String-ify a scalar for text format. Should be optimized away in non-debug.
+ * @param {string|number|boolean} scalar The scalar to stringify.
+ * @return {string} Stringified scalar for text format.
+ */
+jspb.utils.debugScalarToTextFormat = function(scalar) {
+ if (goog.isString(scalar)) {
+ return goog.string.quote(scalar);
+ } else {
+ return scalar.toString();
+ }
+};
+
+
+/**
+ * Utility function: convert a string with codepoints 0--255 inclusive to a
+ * Uint8Array. If any codepoints greater than 255 exist in the string, throws an
+ * exception.
+ * @param {string} str
+ * @return {!Uint8Array}
+ * @private
+ */
+jspb.utils.stringToByteArray_ = function(str) {
+ var arr = new Uint8Array(str.length);
+ for (var i = 0; i < str.length; i++) {
+ var codepoint = str.charCodeAt(i);
+ if (codepoint > 255) {
+ throw new Error('Conversion error: string contains codepoint ' +
+ 'outside of byte range');
+ }
+ arr[i] = codepoint;
+ }
+ return arr;
+};
+
+
+/**
+ * Converts any type defined in jspb.ByteSource into a Uint8Array.
+ * @param {!jspb.ByteSource} data
+ * @param {boolean=} opt_stringIsRawBytes Interpret a string as a series of raw
+ * bytes (encoded as codepoints 0--255 inclusive) rather than base64 data
+ * (default behavior).
+ * @return {!Uint8Array}
+ * @suppress {invalidCasts}
+ */
+jspb.utils.byteSourceToUint8Array = function(data, opt_stringIsRawBytes) {
+ if (data.constructor === Uint8Array) {
+ return /** @type {!Uint8Array} */(data);
+ }
+
+ if (data.constructor === ArrayBuffer) {
+ data = /** @type {!ArrayBuffer} */(data);
+ return /** @type {!Uint8Array} */(new Uint8Array(data));
+ }
+
+ if (data.constructor === Array) {
+ data = /** @type {!Array.<number>} */(data);
+ return /** @type {!Uint8Array} */(new Uint8Array(data));
+ }
+
+ if (data.constructor === String) {
+ data = /** @type {string} */(data);
+ if (opt_stringIsRawBytes) {
+ return jspb.utils.stringToByteArray_(data);
+ } else {
+ return goog.crypt.base64.decodeStringToUint8Array(data);
+ }
+ }
+
+ goog.asserts.fail('Type not convertible to Uint8Array.');
+ return /** @type {!Uint8Array} */(new Uint8Array(0));
+};
diff --git a/js/binary/utils_test.js b/js/binary/utils_test.js
new file mode 100644
index 00000000..5c330791
--- /dev/null
+++ b/js/binary/utils_test.js
@@ -0,0 +1,632 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's helper functions.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.crypt.base64');
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.BinaryWriter');
+goog.require('jspb.utils');
+
+
+/**
+ * @param {number} x
+ * @return {number}
+ */
+function truncate(x) {
+ var temp = new Float32Array(1);
+ temp[0] = x;
+ return temp[0];
+}
+
+
+/**
+ * Converts an 64-bit integer in split representation to a 64-bit hash string
+ * (8 bits encoded per character).
+ * @param {number} bitsLow The low 32 bits of the split 64-bit integer.
+ * @param {number} bitsHigh The high 32 bits of the split 64-bit integer.
+ * @return {string} The encoded hash string, 8 bits per character.
+ */
+function toHashString(bitsLow, bitsHigh) {
+ return String.fromCharCode((bitsLow >>> 0) & 0xFF,
+ (bitsLow >>> 8) & 0xFF,
+ (bitsLow >>> 16) & 0xFF,
+ (bitsLow >>> 24) & 0xFF,
+ (bitsHigh >>> 0) & 0xFF,
+ (bitsHigh >>> 8) & 0xFF,
+ (bitsHigh >>> 16) & 0xFF,
+ (bitsHigh >>> 24) & 0xFF);
+}
+
+
+describe('binaryUtilsTest', function() {
+ /**
+ * Tests lossless binary-to-decimal conversion.
+ */
+ it('testDecimalConversion', function() {
+ // Check some magic numbers.
+ var result =
+ jspb.utils.joinUnsignedDecimalString(0x89e80001, 0x8ac72304);
+ assertEquals('10000000000000000001', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0xacd05f15, 0x1b69b4b);
+ assertEquals('123456789123456789', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0xeb1f0ad2, 0xab54a98c);
+ assertEquals('12345678901234567890', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0xe3b70cb1, 0x891087b8);
+ assertEquals('9876543210987654321', result);
+
+ // Check limits.
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00000000);
+ assertEquals('0', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0xFFFFFFFF, 0xFFFFFFFF);
+ assertEquals('18446744073709551615', result);
+
+ // Check each bit of the low dword.
+ for (var i = 0; i < 32; i++) {
+ var low = (1 << i) >>> 0;
+ result = jspb.utils.joinUnsignedDecimalString(low, 0);
+ assertEquals('' + Math.pow(2, i), result);
+ }
+
+ // Check the first 20 bits of the high dword.
+ for (var i = 0; i < 20; i++) {
+ var high = (1 << i) >>> 0;
+ result = jspb.utils.joinUnsignedDecimalString(0, high);
+ assertEquals('' + Math.pow(2, 32 + i), result);
+ }
+
+ // V8's internal double-to-string conversion is inaccurate for values above
+ // 2^52, even if they're representable integers - check the rest of the bits
+ // manually against the correct string representations of 2^N.
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00100000);
+ assertEquals('4503599627370496', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00200000);
+ assertEquals('9007199254740992', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00400000);
+ assertEquals('18014398509481984', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x00800000);
+ assertEquals('36028797018963968', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x01000000);
+ assertEquals('72057594037927936', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x02000000);
+ assertEquals('144115188075855872', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x04000000);
+ assertEquals('288230376151711744', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x08000000);
+ assertEquals('576460752303423488', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x10000000);
+ assertEquals('1152921504606846976', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x20000000);
+ assertEquals('2305843009213693952', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x40000000);
+ assertEquals('4611686018427387904', result);
+
+ result = jspb.utils.joinUnsignedDecimalString(0x00000000, 0x80000000);
+ assertEquals('9223372036854775808', result);
+ });
+
+
+ /**
+ * Going from hash strings to decimal strings should also be lossless.
+ */
+ it('testHashToDecimalConversion', function() {
+ var result;
+ var convert = jspb.utils.hash64ToDecimalString;
+
+ result = convert(toHashString(0x00000000, 0x00000000), false);
+ assertEquals('0', result);
+
+ result = convert(toHashString(0x00000000, 0x00000000), true);
+ assertEquals('0', result);
+
+ result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), false);
+ assertEquals('18446744073709551615', result);
+
+ result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF), true);
+ assertEquals('-1', result);
+
+ result = convert(toHashString(0x00000000, 0x80000000), false);
+ assertEquals('9223372036854775808', result);
+
+ result = convert(toHashString(0x00000000, 0x80000000), true);
+ assertEquals('-9223372036854775808', result);
+
+ result = convert(toHashString(0xacd05f15, 0x01b69b4b), false);
+ assertEquals('123456789123456789', result);
+
+ result = convert(toHashString(~0xacd05f15 + 1, ~0x01b69b4b), true);
+ assertEquals('-123456789123456789', result);
+
+ // And converting arrays of hashes should work the same way.
+ result = jspb.utils.hash64ArrayToDecimalStrings([
+ toHashString(0xFFFFFFFF, 0xFFFFFFFF),
+ toHashString(0x00000000, 0x80000000),
+ toHashString(0xacd05f15, 0x01b69b4b)], false);
+ assertEquals(3, result.length);
+ assertEquals('18446744073709551615', result[0]);
+ assertEquals('9223372036854775808', result[1]);
+ assertEquals('123456789123456789', result[2]);
+ });
+
+
+ /**
+ * Going from hash strings to hex strings should be lossless.
+ */
+ it('testHashToHexConversion', function() {
+ var result;
+ var convert = jspb.utils.hash64ToHexString;
+
+ result = convert(toHashString(0x00000000, 0x00000000));
+ assertEquals('0x0000000000000000', result);
+
+ result = convert(toHashString(0xFFFFFFFF, 0xFFFFFFFF));
+ assertEquals('0xffffffffffffffff', result);
+
+ result = convert(toHashString(0x12345678, 0x9ABCDEF0));
+ assertEquals('0x9abcdef012345678', result);
+ });
+
+
+ /**
+ * Going from hex strings to hash strings should be lossless.
+ */
+ it('testHexToHashConversion', function() {
+ var result;
+ var convert = jspb.utils.hexStringToHash64;
+
+ result = convert('0x0000000000000000');
+ assertEquals(String.fromCharCode.apply(null,
+ [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result);
+
+ result = convert('0xffffffffffffffff');
+ assertEquals(String.fromCharCode.apply(null,
+ [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result);
+
+ // Hex string is big-endian, hash string is little-endian.
+ result = convert('0x123456789ABCDEF0');
+ assertEquals(String.fromCharCode.apply(null,
+ [0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]), result);
+
+ // Capitalization should not matter.
+ result = convert('0x0000abcdefABCDEF');
+ assertEquals(String.fromCharCode.apply(null,
+ [0xEF, 0xCD, 0xAB, 0xEF, 0xCD, 0xAB, 0x00, 0x00]), result);
+ });
+
+
+ /**
+ * Going from numbers to hash strings should be lossless for up to 53 bits of
+ * precision.
+ */
+ it('testNumberToHashConversion', function() {
+ var result;
+ var convert = jspb.utils.numberToHash64;
+
+ result = convert(0x0000000000000);
+ assertEquals('0x0000000000000000', jspb.utils.hash64ToHexString(result));
+
+ result = convert(0xFFFFFFFFFFFFF);
+ assertEquals('0x000fffffffffffff', jspb.utils.hash64ToHexString(result));
+
+ result = convert(0x123456789ABCD);
+ assertEquals('0x000123456789abcd', jspb.utils.hash64ToHexString(result));
+
+ result = convert(0xDCBA987654321);
+ assertEquals('0x000dcba987654321', jspb.utils.hash64ToHexString(result));
+
+ // 53 bits of precision should not be truncated.
+ result = convert(0x10000000000001);
+ assertEquals('0x0010000000000001', jspb.utils.hash64ToHexString(result));
+
+ // 54 bits of precision should be truncated.
+ result = convert(0x20000000000001);
+ assertNotEquals(
+ '0x0020000000000001', jspb.utils.hash64ToHexString(result));
+ });
+
+
+ /**
+ * Sanity check the behavior of Javascript's strings when doing funny things
+ * with unicode characters.
+ */
+ it('sanityCheckUnicodeStrings', function() {
+ var strings = new Array(65536);
+
+ // All possible unsigned 16-bit values should be storable in a string, they
+ // shouldn't do weird things with the length of the string, and they should
+ // come back out of the string unchanged.
+ for (var i = 0; i < 65536; i++) {
+ strings[i] = 'a' + String.fromCharCode(i) + 'a';
+ if (3 != strings[i].length) throw 'fail!';
+ if (i != strings[i].charCodeAt(1)) throw 'fail!';
+ }
+
+ // Each unicode character should compare equal to itself and not equal to a
+ // different unicode character.
+ for (var i = 0; i < 65536; i++) {
+ if (strings[i] != strings[i]) throw 'fail!';
+ if (strings[i] == strings[(i + 1) % 65536]) throw 'fail!';
+ }
+ });
+
+
+ /**
+ * Tests conversion from 32-bit floating point numbers to split64 numbers.
+ */
+ it('testFloat32ToSplit64', function() {
+ var f32_eps = jspb.BinaryConstants.FLOAT32_EPS;
+ var f32_min = jspb.BinaryConstants.FLOAT32_MIN;
+ var f32_max = jspb.BinaryConstants.FLOAT32_MAX;
+
+ // NaN.
+ jspb.utils.splitFloat32(NaN);
+ if (!isNaN(jspb.utils.joinFloat32(jspb.utils.split64Low,
+ jspb.utils.split64High))) {
+ throw 'fail!';
+ }
+
+ /**
+ * @param {number} x
+ * @param {number=} opt_bits
+ */
+ function test(x, opt_bits) {
+ jspb.utils.splitFloat32(x);
+ if (goog.isDef(opt_bits)) {
+ if (opt_bits != jspb.utils.split64Low) throw 'fail!';
+ }
+ if (truncate(x) != jspb.utils.joinFloat32(jspb.utils.split64Low,
+ jspb.utils.split64High)) {
+ throw 'fail!';
+ }
+ }
+
+ // Positive and negative infinity.
+ test(Infinity, 0x7f800000);
+ test(-Infinity, 0xff800000);
+
+ // Positive and negative zero.
+ test(0, 0x00000000);
+ test(-0, 0x80000000);
+
+ // Positive and negative epsilon.
+ test(f32_eps, 0x00000001);
+ test(-f32_eps, 0x80000001);
+
+ // Positive and negative min.
+ test(f32_min, 0x00800000);
+ test(-f32_min, 0x80800000);
+
+ // Positive and negative max.
+ test(f32_max, 0x7F7FFFFF);
+ test(-f32_max, 0xFF7FFFFF);
+
+ // Various positive values.
+ var cursor = f32_eps * 10;
+ while (cursor != Infinity) {
+ test(cursor);
+ cursor *= 1.1;
+ }
+
+ // Various negative values.
+ cursor = -f32_eps * 10;
+ while (cursor != -Infinity) {
+ test(cursor);
+ cursor *= 1.1;
+ }
+ });
+
+
+ /**
+ * Tests conversion from 64-bit floating point numbers to split64 numbers.
+ */
+ it('testFloat64ToSplit64', function() {
+ var f64_eps = jspb.BinaryConstants.FLOAT64_EPS;
+ var f64_min = jspb.BinaryConstants.FLOAT64_MIN;
+ var f64_max = jspb.BinaryConstants.FLOAT64_MAX;
+
+ // NaN.
+ jspb.utils.splitFloat64(NaN);
+ if (!isNaN(jspb.utils.joinFloat64(jspb.utils.split64Low,
+ jspb.utils.split64High))) {
+ throw 'fail!';
+ }
+
+ /**
+ * @param {number} x
+ * @param {number=} opt_highBits
+ * @param {number=} opt_lowBits
+ */
+ function test(x, opt_highBits, opt_lowBits) {
+ jspb.utils.splitFloat64(x);
+ if (goog.isDef(opt_highBits)) {
+ if (opt_highBits != jspb.utils.split64High) throw 'fail!';
+ }
+ if (goog.isDef(opt_lowBits)) {
+ if (opt_lowBits != jspb.utils.split64Low) throw 'fail!';
+ }
+ if (x != jspb.utils.joinFloat64(jspb.utils.split64Low,
+ jspb.utils.split64High)) {
+ throw 'fail!';
+ }
+ }
+
+ // Positive and negative infinity.
+ test(Infinity, 0x7ff00000, 0x00000000);
+ test(-Infinity, 0xfff00000, 0x00000000);
+
+ // Positive and negative zero.
+ test(0, 0x00000000, 0x00000000);
+ test(-0, 0x80000000, 0x00000000);
+
+ // Positive and negative epsilon.
+ test(f64_eps, 0x00000000, 0x00000001);
+ test(-f64_eps, 0x80000000, 0x00000001);
+
+ // Positive and negative min.
+ test(f64_min, 0x00100000, 0x00000000);
+ test(-f64_min, 0x80100000, 0x00000000);
+
+ // Positive and negative max.
+ test(f64_max, 0x7FEFFFFF, 0xFFFFFFFF);
+ test(-f64_max, 0xFFEFFFFF, 0xFFFFFFFF);
+
+ // Various positive values.
+ var cursor = f64_eps * 10;
+ while (cursor != Infinity) {
+ test(cursor);
+ cursor *= 1.1;
+ }
+
+ // Various negative values.
+ cursor = -f64_eps * 10;
+ while (cursor != -Infinity) {
+ test(cursor);
+ cursor *= 1.1;
+ }
+ });
+
+
+ /**
+ * Tests counting packed varints.
+ */
+ it('testCountVarints', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.rawWriteVarint(Math.floor(i));
+ count++;
+ }
+
+ var buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count, jspb.utils.countVarints(buffer, 0, buffer.length));
+ });
+
+
+ /**
+ * Tests counting matching varint fields.
+ */
+ it('testCountVarintFields', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.writeUint64(1, Math.floor(i));
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ var buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countVarintFields(buffer, 0, buffer.length, 1));
+
+ writer = new jspb.BinaryWriter();
+
+ count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.writeUint64(123456789, Math.floor(i));
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countVarintFields(buffer, 0, buffer.length, 123456789));
+ });
+
+
+ /**
+ * Tests counting matching fixed32 fields.
+ */
+ it('testCountFixed32Fields', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.writeFixed32(1, Math.floor(i));
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ var buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 1));
+
+ writer = new jspb.BinaryWriter();
+
+ count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.writeFixed32(123456789, Math.floor(i));
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countFixed32Fields(buffer, 0, buffer.length, 123456789));
+ });
+
+
+ /**
+ * Tests counting matching fixed64 fields.
+ */
+ it('testCountFixed64Fields', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.writeDouble(1, i);
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ var buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 1));
+
+ writer = new jspb.BinaryWriter();
+
+ count = 0;
+ for (var i = 1; i < 1000000000; i *= 1.1) {
+ writer.writeDouble(123456789, i);
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countFixed64Fields(buffer, 0, buffer.length, 123456789));
+ });
+
+
+ /**
+ * Tests counting matching delimited fields.
+ */
+ it('testCountDelimitedFields', function() {
+ var writer = new jspb.BinaryWriter();
+
+ var count = 0;
+ for (var i = 1; i < 1000; i *= 1.1) {
+ writer.writeBytes(1, [Math.floor(i)]);
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ var buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 1));
+
+ writer = new jspb.BinaryWriter();
+
+ count = 0;
+ for (var i = 1; i < 1000; i *= 1.1) {
+ writer.writeBytes(123456789, [Math.floor(i)]);
+ count++;
+ }
+ writer.writeString(2, 'terminator');
+
+ buffer = new Uint8Array(writer.getResultBuffer());
+ assertEquals(count,
+ jspb.utils.countDelimitedFields(buffer, 0, buffer.length, 123456789));
+ });
+
+
+ /**
+ * Tests byte format for debug strings.
+ */
+ it('testDebugBytesToTextFormat', function() {
+ assertEquals('""', jspb.utils.debugBytesToTextFormat(null));
+ assertEquals('"\\x00\\x10\\xff"',
+ jspb.utils.debugBytesToTextFormat([0, 16, 255]));
+ });
+
+
+ /**
+ * Tests converting byte blob sources into byte blobs.
+ */
+ it('testByteSourceToUint8Array', function() {
+ var convert = jspb.utils.byteSourceToUint8Array;
+
+ var sourceData = [];
+ for (var i = 0; i < 256; i++) {
+ sourceData.push(i);
+ }
+
+ var sourceBytes = new Uint8Array(sourceData);
+ var sourceBuffer = sourceBytes.buffer;
+ var sourceBase64 = goog.crypt.base64.encodeByteArray(sourceData);
+ var sourceString = String.fromCharCode.apply(null, sourceData);
+
+ function check(result) {
+ assertEquals(Uint8Array, result.constructor);
+ assertEquals(sourceData.length, result.length);
+ for (var i = 0; i < result.length; i++) {
+ assertEquals(sourceData[i], result[i]);
+ }
+ }
+
+ // Converting Uint8Arrays into Uint8Arrays should be a no-op.
+ assertEquals(sourceBytes, convert(sourceBytes));
+
+ // Converting Array.<numbers> into Uint8Arrays should work.
+ check(convert(sourceData));
+
+ // Converting ArrayBuffers into Uint8Arrays should work.
+ check(convert(sourceBuffer));
+
+ // Converting base64-encoded strings into Uint8Arrays should work.
+ check(convert(sourceBase64));
+
+ // Converting binary-data strings into Uint8Arrays should work.
+ check(convert(sourceString, /* opt_stringIsRawBytes = */ true));
+ });
+});
diff --git a/js/binary/writer.js b/js/binary/writer.js
new file mode 100644
index 00000000..a1849457
--- /dev/null
+++ b/js/binary/writer.js
@@ -0,0 +1,2124 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview This file contains utilities for encoding Javascript objects
+ * into binary, wire-format protocol buffers (in the form of Uint8Arrays) that
+ * a server can consume directly.
+ *
+ * jspb's BinaryWriter class defines methods for efficiently encoding
+ * Javascript objects into binary, wire-format protocol buffers and supports
+ * all the fundamental field types used in protocol buffers.
+ *
+ * Major caveat 1 - Users of this library _must_ keep their Javascript proto
+ * parsing code in sync with the original .proto file - presumably you'll be
+ * using the typed jspb code generator, but if you bypass that you'll need
+ * to keep things in sync by hand.
+ *
+ * Major caveat 2 - Javascript is unable to accurately represent integers
+ * larger than 2^53 due to its use of a double-precision floating point format
+ * for all numbers. BinaryWriter does not make any special effort to preserve
+ * precision for values above this limit - if you need to pass 64-bit integers
+ * (hash codes, for example) between the client and server without precision
+ * loss, do _not_ use this library.
+ *
+ * Major caveat 3 - This class uses typed arrays and must not be used on older
+ * browsers that do not support them.
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.provide('jspb.BinaryWriter');
+
+goog.require('goog.asserts');
+goog.require('goog.crypt.base64');
+goog.require('jspb.BinaryConstants');
+goog.require('jspb.arith.Int64');
+goog.require('jspb.arith.UInt64');
+goog.require('jspb.utils');
+
+goog.forwardDeclare('jspb.Message');
+
+
+
+/**
+ * BinaryWriter implements encoders for all the wire types specified in
+ * https://developers.google.com/protocol-buffers/docs/encoding.
+ *
+ * @constructor
+ * @struct
+ */
+jspb.BinaryWriter = function() {
+ /**
+ * Blocks of serialized data that will be concatenated once all messages have
+ * been written.
+ * @private {!Array<!Uint8Array|!Array<number>>}
+ */
+ this.blocks_ = [];
+
+ /**
+ * Total number of bytes in the blocks_ array. Does _not_ include the temp
+ * buffer.
+ * @private {number}
+ */
+ this.totalLength_ = 0;
+
+ /**
+ * Temporary buffer holding a message that we're still serializing. When we
+ * get to a stopping point (either the start of a new submessage, or when we
+ * need to append a raw Uint8Array), the temp buffer will be added to the
+ * block array above and a new temp buffer will be created.
+ * @private {!Array.<number>}
+ */
+ this.temp_ = [];
+
+ /**
+ * A stack of bookmarks containing the parent blocks for each message started
+ * via beginSubMessage(), needed as bookkeeping for endSubMessage().
+ * TODO(aappleby): Deprecated, users should be calling writeMessage().
+ * @private {!Array.<!jspb.BinaryWriter.Bookmark_>}
+ */
+ this.bookmarks_ = [];
+};
+
+
+/**
+ * @typedef {{block: !Array.<number>, length: number}}
+ * @private
+ */
+jspb.BinaryWriter.Bookmark_;
+
+
+/**
+ * Saves the current temp buffer in the blocks_ array and starts a new one.
+ * @return {!Array.<number>} Returns a reference to the old temp buffer.
+ * @private
+ */
+jspb.BinaryWriter.prototype.saveTempBuffer_ = function() {
+ var oldTemp = this.temp_;
+ this.blocks_.push(this.temp_);
+ this.totalLength_ += this.temp_.length;
+ this.temp_ = [];
+ return oldTemp;
+};
+
+
+/**
+ * Append a typed array of bytes onto the buffer.
+ *
+ * @param {!Uint8Array} arr The byte array to append.
+ * @private
+ */
+jspb.BinaryWriter.prototype.appendUint8Array_ = function(arr) {
+ if (this.temp_.length) {
+ this.saveTempBuffer_();
+ }
+ this.blocks_.push(arr);
+ this.totalLength_ += arr.length;
+};
+
+
+/**
+ * Append an untyped array of bytes onto the buffer.
+ *
+ * @param {!Array.<number>} arr The byte array to append.
+ * @private
+ */
+jspb.BinaryWriter.prototype.appendArray_ = function(arr) {
+ if (this.temp_.length) {
+ this.saveTempBuffer_();
+ }
+ this.temp_ = arr;
+};
+
+
+/**
+ * Begins a length-delimited section by writing the field header to the current
+ * temp buffer and then saving it in the block array. Returns the saved block,
+ * which we will append the length to in endDelimited_ below.
+ * @param {number} field
+ * @return {!jspb.BinaryWriter.Bookmark_}
+ * @private
+ */
+jspb.BinaryWriter.prototype.beginDelimited_ = function(field) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ return {block: this.saveTempBuffer_(), length: this.totalLength_};
+};
+
+
+/**
+ * Ends a length-delimited block by encoding the _change_ in length of the
+ * buffer to the parent block and adds the number of bytes needed to encode
+ * that length to the total byte length. Note that 'parentLength' _must_ be the
+ * total length _after_ the field header was written in beginDelimited_ above.
+ * @param {!jspb.BinaryWriter.Bookmark_} bookmark
+ * @private
+ */
+jspb.BinaryWriter.prototype.endDelimited_ = function(bookmark) {
+ var messageLength = this.totalLength_ + this.temp_.length - bookmark.length;
+ goog.asserts.assert(messageLength >= 0);
+
+ var bytes = 1;
+ while (messageLength > 127) {
+ bookmark.block.push((messageLength & 0x7f) | 0x80);
+ messageLength = messageLength >>> 7;
+ bytes++;
+ }
+
+ bookmark.block.push(messageLength);
+ this.totalLength_ += bytes;
+};
+
+
+/**
+ * Resets the writer, throwing away any accumulated buffers.
+ */
+jspb.BinaryWriter.prototype.reset = function() {
+ this.blocks_ = [];
+ this.temp_ = [];
+ this.totalLength_ = 0;
+ this.bookmarks_ = [];
+};
+
+
+/**
+ * Converts the encoded data into a Uint8Array.
+ * @return {!Uint8Array}
+ */
+jspb.BinaryWriter.prototype.getResultBuffer = function() {
+ goog.asserts.assert(this.bookmarks_.length == 0);
+
+ var flat = new Uint8Array(this.totalLength_ + this.temp_.length);
+
+ var blocks = this.blocks_;
+ var blockCount = blocks.length;
+ var offset = 0;
+
+ for (var i = 0; i < blockCount; i++) {
+ var block = blocks[i];
+ flat.set(block, offset);
+ offset += block.length;
+ }
+
+ flat.set(this.temp_, offset);
+ offset += this.temp_.length;
+
+ // Post condition: `flattened` must have had every byte written.
+ goog.asserts.assert(offset == flat.length);
+
+ // Replace our block list with the flattened block, which lets GC reclaim
+ // the temp blocks sooner.
+ this.blocks_ = [flat];
+ this.temp_ = [];
+
+ return flat;
+};
+
+
+/**
+ * Converts the encoded data into a bas64-encoded string.
+ * @return {string}
+ */
+jspb.BinaryWriter.prototype.getResultBase64String = function() {
+ return goog.crypt.base64.encodeByteArray(this.getResultBuffer());
+};
+
+
+/**
+ * Begins a new sub-message. The client must call endSubMessage() when they're
+ * done.
+ * TODO(aappleby): Deprecated. Move callers to writeMessage().
+ * @param {number} field The field number of the sub-message.
+ */
+jspb.BinaryWriter.prototype.beginSubMessage = function(field) {
+ this.bookmarks_.push(this.beginDelimited_(field));
+};
+
+
+/**
+ * Finishes a sub-message and packs it into the parent messages' buffer.
+ * TODO(aappleby): Deprecated. Move callers to writeMessage().
+ */
+jspb.BinaryWriter.prototype.endSubMessage = function() {
+ goog.asserts.assert(this.bookmarks_.length >= 0);
+ this.endDelimited_(this.bookmarks_.pop());
+};
+
+
+/**
+ * Encodes a 32-bit unsigned integer into its wire-format varint representation
+ * and stores it in the buffer.
+ * @param {number} value The integer to convert.
+ */
+jspb.BinaryWriter.prototype.rawWriteUnsignedVarint32 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+
+ while (value > 127) {
+ this.temp_.push((value & 0x7f) | 0x80);
+ value = value >>> 7;
+ }
+
+ this.temp_.push(value);
+};
+
+
+/**
+ * Encodes a 32-bit signed integer into its wire-format varint representation
+ * and stores it in the buffer.
+ * @param {number} value The integer to convert.
+ */
+jspb.BinaryWriter.prototype.rawWriteSignedVarint32 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ if (value >= 0) {
+ this.rawWriteUnsignedVarint32(value);
+ return;
+ }
+
+ // Write nine bytes with a _signed_ right shift so we preserve the sign bit.
+ for (var i = 0; i < 9; i++) {
+ this.temp_.push((value & 0x7f) | 0x80);
+ value = value >> 7;
+ }
+
+ // The above loop writes out 63 bits, so the last byte is always the sign bit
+ // which is always set for negative numbers.
+ this.temp_.push(1);
+};
+
+
+/**
+ * Encodes an unsigned 64-bit integer in 32:32 split representation into its
+ * wire-format varint representation and stores it in the buffer.
+ * @param {number} lowBits The low 32 bits of the int.
+ * @param {number} highBits The high 32 bits of the int.
+ */
+jspb.BinaryWriter.prototype.rawWriteSplitVarint =
+ function(lowBits, highBits) {
+ // Break the binary representation into chunks of 7 bits, set the 8th bit
+ // in each chunk if it's not the final chunk, and append to the result.
+ while (highBits > 0 || lowBits > 127) {
+ this.temp_.push((lowBits & 0x7f) | 0x80);
+ lowBits = ((lowBits >>> 7) | (highBits << 25)) >>> 0;
+ highBits = highBits >>> 7;
+ }
+ this.temp_.push(lowBits);
+};
+
+
+/**
+ * Encodes a JavaScript integer into its wire-format varint representation and
+ * stores it in the buffer. Due to the way the varint encoding works this
+ * behaves correctly for both signed and unsigned integers, though integers
+ * that are not representable in 64 bits will still be truncated.
+ * @param {number} value The integer to convert.
+ */
+jspb.BinaryWriter.prototype.rawWriteVarint = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ jspb.utils.splitInt64(value);
+ this.rawWriteSplitVarint(jspb.utils.split64Low,
+ jspb.utils.split64High);
+};
+
+
+/**
+ * Encodes a jspb.arith.{Int64,UInt64} instance into its wire-format
+ * varint representation and stores it in the buffer. Due to the way the varint
+ * encoding works this behaves correctly for both signed and unsigned integers,
+ * though integers that are not representable in 64 bits will still be
+ * truncated.
+ * @param {jspb.arith.Int64|jspb.arith.UInt64} value
+ */
+jspb.BinaryWriter.prototype.rawWriteVarintFromNum = function(value) {
+ this.rawWriteSplitVarint(value.lo, value.hi);
+};
+
+
+/**
+ * Encodes a JavaScript integer into its wire-format, zigzag-encoded varint
+ * representation and stores it in the buffer.
+ * @param {number} value The integer to convert.
+ */
+jspb.BinaryWriter.prototype.rawWriteZigzagVarint32 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ this.rawWriteUnsignedVarint32(((value << 1) ^ (value >> 31)) >>> 0);
+};
+
+
+/**
+ * Encodes a JavaScript integer into its wire-format, zigzag-encoded varint
+ * representation and stores it in the buffer. Integers not representable in 64
+ * bits will be truncated.
+ * @param {number} value The integer to convert.
+ */
+jspb.BinaryWriter.prototype.rawWriteZigzagVarint = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ jspb.utils.splitZigzag64(value);
+ this.rawWriteSplitVarint(jspb.utils.split64Low,
+ jspb.utils.split64High);
+};
+
+
+/**
+ * Writes a raw 8-bit unsigned integer to the buffer. Numbers outside the range
+ * [0,2^8) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteUint8 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= 0) && (value < 256));
+ this.temp_.push((value >>> 0) & 0xFF);
+};
+
+
+/**
+ * Writes a raw 16-bit unsigned integer to the buffer. Numbers outside the
+ * range [0,2^16) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteUint16 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= 0) && (value < 65536));
+ this.temp_.push((value >>> 0) & 0xFF);
+ this.temp_.push((value >>> 8) & 0xFF);
+};
+
+
+/**
+ * Writes a raw 32-bit unsigned integer to the buffer. Numbers outside the
+ * range [0,2^32) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteUint32 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= 0) &&
+ (value < jspb.BinaryConstants.TWO_TO_32));
+ this.temp_.push((value >>> 0) & 0xFF);
+ this.temp_.push((value >>> 8) & 0xFF);
+ this.temp_.push((value >>> 16) & 0xFF);
+ this.temp_.push((value >>> 24) & 0xFF);
+};
+
+
+/**
+ * Writes a raw 64-bit unsigned integer to the buffer. Numbers outside the
+ * range [0,2^64) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteUint64 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= 0) &&
+ (value < jspb.BinaryConstants.TWO_TO_64));
+ jspb.utils.splitUint64(value);
+ this.rawWriteUint32(jspb.utils.split64Low);
+ this.rawWriteUint32(jspb.utils.split64High);
+};
+
+
+/**
+ * Writes a raw 8-bit integer to the buffer. Numbers outside the range
+ * [-2^7,2^7) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteInt8 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= -128) && (value < 128));
+ this.temp_.push((value >>> 0) & 0xFF);
+};
+
+
+/**
+ * Writes a raw 16-bit integer to the buffer. Numbers outside the range
+ * [-2^15,2^15) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteInt16 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= -32768) && (value < 32768));
+ this.temp_.push((value >>> 0) & 0xFF);
+ this.temp_.push((value >>> 8) & 0xFF);
+};
+
+
+/**
+ * Writes a raw 32-bit integer to the buffer. Numbers outside the range
+ * [-2^31,2^31) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteInt32 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (value < jspb.BinaryConstants.TWO_TO_31));
+ this.temp_.push((value >>> 0) & 0xFF);
+ this.temp_.push((value >>> 8) & 0xFF);
+ this.temp_.push((value >>> 16) & 0xFF);
+ this.temp_.push((value >>> 24) & 0xFF);
+};
+
+
+/**
+ * Writes a raw 64-bit integer to the buffer. Numbers outside the range
+ * [-2^63,2^63) will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteInt64 = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (value < jspb.BinaryConstants.TWO_TO_63));
+ jspb.utils.splitInt64(value);
+ this.rawWriteUint32(jspb.utils.split64Low);
+ this.rawWriteUint32(jspb.utils.split64High);
+};
+
+
+/**
+ * Writes a raw single-precision floating point value to the buffer. Numbers
+ * requiring more than 32 bits of precision will be truncated.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteFloat = function(value) {
+ jspb.utils.splitFloat32(value);
+ this.rawWriteUint32(jspb.utils.split64Low);
+};
+
+
+/**
+ * Writes a raw double-precision floating point value to the buffer. As this is
+ * the native format used by JavaScript, no precision will be lost.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteDouble = function(value) {
+ jspb.utils.splitFloat64(value);
+ this.rawWriteUint32(jspb.utils.split64Low);
+ this.rawWriteUint32(jspb.utils.split64High);
+};
+
+
+/**
+ * Writes a raw boolean value to the buffer as a varint.
+ * @param {boolean} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteBool = function(value) {
+ goog.asserts.assert(goog.isBoolean(value));
+ this.temp_.push(~~value);
+};
+
+
+/**
+ * Writes an raw enum value to the buffer as a varint.
+ * @param {number} value The value to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteEnum = function(value) {
+ goog.asserts.assert(value == Math.floor(value));
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (value < jspb.BinaryConstants.TWO_TO_31));
+ this.rawWriteSignedVarint32(value);
+};
+
+
+/**
+ * Writes a raw string value to the buffer.
+ * @param {string} string The string to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteUtf8String = function(string) {
+ for (var i = 0; i < string.length; i++) {
+ this.temp_.push(string.charCodeAt(i));
+ }
+};
+
+
+/**
+ * Writes an arbitrary raw byte array to the buffer.
+ * @param {!Uint8Array} bytes The array of bytes to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteBytes = function(bytes) {
+ this.appendUint8Array_(bytes);
+};
+
+
+/**
+ * Writes an arbitrary raw byte array to the buffer.
+ * @param {!Uint8Array} bytes The array of bytes to write.
+ * @param {number} start The start of the range to write.
+ * @param {number} end The end of the range to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteByteRange = function(bytes, start, end) {
+ this.appendUint8Array_(bytes.subarray(start, end));
+};
+
+
+/**
+ * Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the
+ * buffer as a varint.
+ * @param {string} hash The hash to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteVarintHash64 = function(hash) {
+ jspb.utils.splitHash64(hash);
+ this.rawWriteSplitVarint(jspb.utils.split64Low,
+ jspb.utils.split64High);
+};
+
+
+/**
+ * Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the
+ * buffer as a fixed64.
+ * @param {string} hash The hash to write.
+ */
+jspb.BinaryWriter.prototype.rawWriteFixedHash64 = function(hash) {
+ jspb.utils.splitHash64(hash);
+ this.rawWriteUint32(jspb.utils.split64Low);
+ this.rawWriteUint32(jspb.utils.split64High);
+};
+
+
+/**
+ * Encodes a (field number, wire type) tuple into a wire-format field header
+ * and stores it in the buffer as a varint.
+ * @param {number} field The field number.
+ * @param {number} wireType The wire-type of the field, as specified in the
+ * protocol buffer documentation.
+ * @private
+ */
+jspb.BinaryWriter.prototype.rawWriteFieldHeader_ =
+ function(field, wireType) {
+ goog.asserts.assert(field >= 1 && field == Math.floor(field));
+ var x = field * 8 + wireType;
+ this.rawWriteUnsignedVarint32(x);
+};
+
+
+/**
+ * Writes a field of any valid scalar type to the binary stream.
+ * @param {jspb.BinaryConstants.FieldType} fieldType
+ * @param {number} field
+ * @param {jspb.AnyFieldType} value
+ */
+jspb.BinaryWriter.prototype.writeAny = function(fieldType, field, value) {
+ var fieldTypes = jspb.BinaryConstants.FieldType;
+ switch (fieldType) {
+ case fieldTypes.DOUBLE:
+ this.writeDouble(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.FLOAT:
+ this.writeFloat(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.INT64:
+ this.writeInt64(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.UINT64:
+ this.writeUint64(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.INT32:
+ this.writeInt32(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.FIXED64:
+ this.writeFixed64(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.FIXED32:
+ this.writeFixed32(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.BOOL:
+ this.writeBool(field, /** @type {boolean} */(value));
+ return;
+ case fieldTypes.STRING:
+ this.writeString(field, /** @type {string} */(value));
+ return;
+ case fieldTypes.GROUP:
+ goog.asserts.fail('Group field type not supported in writeAny()');
+ return;
+ case fieldTypes.MESSAGE:
+ goog.asserts.fail('Message field type not supported in writeAny()');
+ return;
+ case fieldTypes.BYTES:
+ this.writeBytes(field, /** @type {?Uint8Array} */(value));
+ return;
+ case fieldTypes.UINT32:
+ this.writeUint32(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.ENUM:
+ this.writeEnum(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.SFIXED32:
+ this.writeSfixed32(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.SFIXED64:
+ this.writeSfixed64(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.SINT32:
+ this.writeSint32(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.SINT64:
+ this.writeSint64(field, /** @type {number} */(value));
+ return;
+ case fieldTypes.FHASH64:
+ this.writeFixedHash64(field, /** @type {string} */(value));
+ return;
+ case fieldTypes.VHASH64:
+ this.writeVarintHash64(field, /** @type {string} */(value));
+ return;
+ default:
+ goog.asserts.fail('Invalid field type in writeAny()');
+ return;
+ }
+};
+
+
+/**
+ * Writes a varint field to the buffer without range checking.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeUnsignedVarint32_ = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteSignedVarint32(value);
+};
+
+
+/**
+ * Writes a varint field to the buffer without range checking.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeSignedVarint32_ = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteSignedVarint32(value);
+};
+
+
+/**
+ * Writes a varint field to the buffer without range checking.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeVarint_ = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteVarint(value);
+};
+
+
+/**
+ * Writes a zigzag varint field to the buffer without range checking.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeZigzagVarint32_ = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteZigzagVarint32(value);
+};
+
+
+/**
+ * Writes a zigzag varint field to the buffer without range checking.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeZigzagVarint_ = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteZigzagVarint(value);
+};
+
+
+/**
+ * Writes an int32 field to the buffer. Numbers outside the range [-2^31,2^31)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeInt32 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (value < jspb.BinaryConstants.TWO_TO_31));
+ this.writeSignedVarint32_(field, value);
+};
+
+
+/**
+ * Writes an int32 field represented as a string to the buffer. Numbers outside
+ * the range [-2^31,2^31) will be truncated.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeInt32String = function(field, value) {
+ if (value == null) return;
+ var intValue = /** {number} */ parseInt(value, 10);
+ goog.asserts.assert((intValue >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (intValue < jspb.BinaryConstants.TWO_TO_31));
+ this.writeSignedVarint32_(field, intValue);
+};
+
+
+/**
+ * Writes an int64 field to the buffer. Numbers outside the range [-2^63,2^63)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeInt64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (value < jspb.BinaryConstants.TWO_TO_63));
+ this.writeVarint_(field, value);
+};
+
+
+/**
+ * Writes a int64 field (with value as a string) to the buffer.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeInt64String = function(field, value) {
+ if (value == null) return;
+ var num = jspb.arith.Int64.fromString(value);
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteVarintFromNum(num);
+};
+
+
+/**
+ * Writes a uint32 field to the buffer. Numbers outside the range [0,2^32)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeUint32 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= 0) &&
+ (value < jspb.BinaryConstants.TWO_TO_32));
+ this.writeUnsignedVarint32_(field, value);
+};
+
+
+/**
+ * Writes a uint32 field represented as a string to the buffer. Numbers outside
+ * the range [0,2^32) will be truncated.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeUint32String = function(field, value) {
+ if (value == null) return;
+ var intValue = /** {number} */ parseInt(value, 10);
+ goog.asserts.assert((intValue >= 0) &&
+ (intValue < jspb.BinaryConstants.TWO_TO_32));
+ this.writeUnsignedVarint32_(field, intValue);
+};
+
+
+/**
+ * Writes a uint64 field to the buffer. Numbers outside the range [0,2^64)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeUint64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= 0) &&
+ (value < jspb.BinaryConstants.TWO_TO_64));
+ this.writeVarint_(field, value);
+};
+
+
+/**
+ * Writes a uint64 field (with value as a string) to the buffer.
+ * @param {number} field The field number.
+ * @param {string?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeUint64String = function(field, value) {
+ if (value == null) return;
+ var num = jspb.arith.UInt64.fromString(value);
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteVarintFromNum(num);
+};
+
+
+/**
+ * Writes a sint32 field to the buffer. Numbers outside the range [-2^31,2^31)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeSint32 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (value < jspb.BinaryConstants.TWO_TO_31));
+ this.writeZigzagVarint32_(field, value);
+};
+
+
+/**
+ * Writes a sint64 field to the buffer. Numbers outside the range [-2^63,2^63)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeSint64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (value < jspb.BinaryConstants.TWO_TO_63));
+ this.writeZigzagVarint_(field, value);
+};
+
+
+/**
+ * Writes a fixed32 field to the buffer. Numbers outside the range [0,2^32)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeFixed32 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= 0) &&
+ (value < jspb.BinaryConstants.TWO_TO_32));
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32);
+ this.rawWriteUint32(value);
+};
+
+
+/**
+ * Writes a fixed64 field to the buffer. Numbers outside the range [0,2^64)
+ * will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeFixed64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= 0) &&
+ (value < jspb.BinaryConstants.TWO_TO_64));
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64);
+ this.rawWriteUint64(value);
+};
+
+
+/**
+ * Writes a sfixed32 field to the buffer. Numbers outside the range
+ * [-2^31,2^31) will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeSfixed32 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (value < jspb.BinaryConstants.TWO_TO_31));
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32);
+ this.rawWriteInt32(value);
+};
+
+
+/**
+ * Writes a sfixed64 field to the buffer. Numbers outside the range
+ * [-2^63,2^63) will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeSfixed64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
+ (value < jspb.BinaryConstants.TWO_TO_63));
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64);
+ this.rawWriteInt64(value);
+};
+
+
+/**
+ * Writes a single-precision floating point field to the buffer. Numbers
+ * requiring more than 32 bits of precision will be truncated.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeFloat = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32);
+ this.rawWriteFloat(value);
+};
+
+
+/**
+ * Writes a double-precision floating point field to the buffer. As this is the
+ * native format used by JavaScript, no precision will be lost.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeDouble = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64);
+ this.rawWriteDouble(value);
+};
+
+
+/**
+ * Writes a boolean field to the buffer.
+ * @param {number} field The field number.
+ * @param {boolean?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeBool = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert(goog.isBoolean(value));
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.temp_.push(~~value);
+};
+
+
+/**
+ * Writes an enum field to the buffer.
+ * @param {number} field The field number.
+ * @param {number?} value The value to write.
+ */
+jspb.BinaryWriter.prototype.writeEnum = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
+ (value < jspb.BinaryConstants.TWO_TO_31));
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteSignedVarint32(value);
+};
+
+
+/**
+ * Writes a string field to the buffer.
+ * @param {number} field The field number.
+ * @param {string?} value The string to write.
+ */
+jspb.BinaryWriter.prototype.writeString = function(field, value) {
+ if (value == null) return;
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+
+ // Conversion loop swiped from goog.crypt.stringToUtf8ByteArray. Note that
+ // 'bytes' will be at least as long as 'value', but could be longer if we
+ // need to unpack unicode characters.
+ var bytes = [];
+ for (var i = 0; i < value.length; i++) {
+ var c = value.charCodeAt(i);
+ if (c < 128) {
+ bytes.push(c);
+ } else if (c < 2048) {
+ bytes.push((c >> 6) | 192);
+ bytes.push((c & 63) | 128);
+ } else {
+ bytes.push((c >> 12) | 224);
+ bytes.push(((c >> 6) & 63) | 128);
+ bytes.push((c & 63) | 128);
+ }
+ }
+
+ this.rawWriteUnsignedVarint32(bytes.length);
+ this.appendArray_(bytes);
+};
+
+
+/**
+ * Writes an arbitrary byte field to the buffer. Note - to match the behavior
+ * of the C++ implementation, empty byte arrays _are_ serialized.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {jspb.ByteSource} value The array of bytes to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ * @param {boolean=} opt_stringIsRawBytes If `value` is a string, interpret it
+ * as a series of raw bytes (codepoints 0--255 inclusive) rather than base64
+ * data.
+ */
+jspb.BinaryWriter.prototype.writeBytes =
+ function(field, value, opt_buffer, opt_start, opt_end,
+ opt_stringIsRawBytes) {
+ if (value != null) {
+ this.rawWriteFieldHeader_(field,
+ jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length);
+ this.rawWriteBytes(
+ jspb.utils.byteSourceToUint8Array(value, opt_stringIsRawBytes));
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an arbitrary byte field to the buffer, with `opt_stringIsRawBytes`
+ * flag implicitly true.
+ * @param {number} field
+ * @param {jspb.ByteSource} value The array of bytes to write.
+ */
+jspb.BinaryWriter.prototype.writeBytesRawString = function(field, value) {
+ this.writeBytes(field, value, null, null, null, true);
+};
+
+
+/**
+ * Writes a message to the buffer.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @template MessageType
+ * @param {number} field The field number.
+ * @param {?MessageType} value The message to write.
+ * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
+ * to write and the writer to write it with.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writeMessage =
+ function(field, value, writerCallback, opt_buffer, opt_start, opt_end) {
+ if (value !== null) {
+ var bookmark = this.beginDelimited_(field);
+
+ writerCallback(value, this);
+
+ this.endDelimited_(bookmark);
+ } else if (opt_buffer && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes a group message to the buffer.
+ *
+ * @template MessageType
+ * @param {number} field The field number.
+ * @param {?MessageType} value The message to write, wrapped with START_GROUP /
+ * END_GROUP tags. Will be a no-op if 'value' is null.
+ * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
+ * to write and the writer to write it with.
+ */
+jspb.BinaryWriter.prototype.writeGroup =
+ function(field, value, writerCallback) {
+ if (value) {
+ this.rawWriteFieldHeader_(
+ field, jspb.BinaryConstants.WireType.START_GROUP);
+ writerCallback(value, this);
+ this.rawWriteFieldHeader_(
+ field, jspb.BinaryConstants.WireType.END_GROUP);
+ }
+};
+
+
+/**
+ * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to
+ * the buffer.
+ * @param {number} field The field number.
+ * @param {string?} value The hash string.
+ */
+jspb.BinaryWriter.prototype.writeFixedHash64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert(value.length == 8);
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64);
+ this.rawWriteFixedHash64(value);
+};
+
+
+/**
+ * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to
+ * the buffer.
+ * @param {number} field The field number.
+ * @param {string?} value The hash string.
+ */
+jspb.BinaryWriter.prototype.writeVarintHash64 = function(field, value) {
+ if (value == null) return;
+ goog.asserts.assert(value.length == 8);
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT);
+ this.rawWriteVarintHash64(value);
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated varint field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint32_ =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeUnsignedVarint32_(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated varint field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_ =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeSignedVarint32_(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated varint field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeRepeatedVarint_ = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeVarint_(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated zigzag field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeRepeatedZigzag32_ = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeZigzagVarint32_(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated zigzag field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writeRepeatedZigzag_ = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeZigzagVarint_(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated 32-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedInt32 =
+ jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_;
+
+
+/**
+ * Writes an array of numbers formatted as strings to the buffer as a repeated
+ * 32-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedInt32String =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeInt32String(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated 64-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedInt64 =
+ jspb.BinaryWriter.prototype.writeRepeatedVarint_;
+
+
+/**
+ * Writes an array of numbers formatted as strings to the buffer as a repeated
+ * 64-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedInt64String =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeInt64String(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array numbers to the buffer as a repeated unsigned 32-bit int
+ * field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedUint32 =
+ jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint32_;
+
+
+/**
+ * Writes an array of numbers formatted as strings to the buffer as a repeated
+ * unsigned 32-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedUint32String =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeUint32String(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array numbers to the buffer as a repeated unsigned 64-bit int
+ * field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedUint64 =
+ jspb.BinaryWriter.prototype.writeRepeatedVarint_;
+
+
+/**
+ * Writes an array of numbers formatted as strings to the buffer as a repeated
+ * unsigned 64-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedUint64String =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeUint64String(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array numbers to the buffer as a repeated signed 32-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedSint32 =
+ jspb.BinaryWriter.prototype.writeRepeatedZigzag32_;
+
+
+/**
+ * Writes an array numbers to the buffer as a repeated signed 64-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedSint64 =
+ jspb.BinaryWriter.prototype.writeRepeatedZigzag_;
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated fixed32 field. This
+ * works for both signed and unsigned fixed32s.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedFixed32 = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeFixed32(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated fixed64 field. This
+ * works for both signed and unsigned fixed64s.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedFixed64 = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeFixed64(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated sfixed32 field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedSfixed32 = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeSfixed32(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated sfixed64 field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedSfixed64 = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeSfixed64(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated float field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedFloat = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeFloat(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a repeated double field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedDouble = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeDouble(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of booleans to the buffer as a repeated bool field.
+ * @param {number} field The field number.
+ * @param {?Array.<boolean>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedBool = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeBool(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of enums to the buffer as a repeated enum field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedEnum = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeEnum(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of strings to the buffer as a repeated string field.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of strings to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedString = function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeString(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of arbitrary byte fields to the buffer.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<!Uint8Array|string>} value
+ * The arrays of arrays of bytes to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ * @param {boolean=} opt_stringIsRawBytes Any values that are strings are
+ * interpreted as raw bytes rather than base64 data.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedBytes =
+ function(field, value, opt_buffer, opt_start, opt_end,
+ opt_stringIsRawBytes) {
+ if (value != null) {
+ for (var i = 0; i < value.length; i++) {
+ this.writeBytes(field, value[i], null, null, null, opt_stringIsRawBytes);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of arbitrary byte fields to the buffer, with
+ * `opt_stringIsRawBytes` implicitly true.
+ * @param {number} field
+ * @param {?Array.<string>} value
+ */
+jspb.BinaryWriter.prototype.writeRepeatedBytesRawString =
+ function(field, value) {
+ this.writeRepeatedBytes(field, value, null, null, null, true);
+};
+
+
+/**
+ * Writes an array of messages to the buffer.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @template MessageType
+ * @param {number} field The field number.
+ * @param {?Array.<!MessageType>} value The array of messages to
+ * write.
+ * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
+ * to write and the writer to write it with.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedMessage =
+ function(field, value, writerCallback, opt_buffer, opt_start, opt_end) {
+ if (value) {
+ for (var i = 0; i < value.length; i++) {
+ var bookmark = this.beginDelimited_(field);
+
+ writerCallback(value[i], this);
+
+ this.endDelimited_(bookmark);
+ }
+ } else if (opt_buffer && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of group messages to the buffer.
+ *
+ * @template MessageType
+ * @param {number} field The field number.
+ * @param {?Array.<!MessageType>} value The array of messages to
+ * write.
+ * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value
+ * to write and the writer to write it with.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedGroup =
+ function(field, value, writerCallback) {
+ if (value) {
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteFieldHeader_(
+ field, jspb.BinaryConstants.WireType.START_GROUP);
+ writerCallback(value[i], this);
+ this.rawWriteFieldHeader_(
+ field, jspb.BinaryConstants.WireType.END_GROUP);
+ }
+ }
+};
+
+
+/**
+ * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to
+ * the buffer.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of hashes to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedFixedHash64 =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeFixedHash64(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes a repeated 64-bit hash string field (8 characters @ 8 bits of data
+ * each) to the buffer.
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of hashes to write.
+ */
+jspb.BinaryWriter.prototype.writeRepeatedVarintHash64 =
+ function(field, value) {
+ if (value == null) return;
+ for (var i = 0; i < value.length; i++) {
+ this.writeVarintHash64(field, value[i]);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed varint field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_ =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteUnsignedVarint32(value[i]);
+ }
+ this.endDelimited_(bookmark);
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed varint field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writePackedSignedVarint32_ =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteSignedVarint32(value[i]);
+ }
+ this.endDelimited_(bookmark);
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed varint field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writePackedVarint_ =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteVarint(value[i]);
+ }
+ this.endDelimited_(bookmark);
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed zigzag field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ * @private
+ */
+jspb.BinaryWriter.prototype.writePackedZigzag_ =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteZigzagVarint(value[i]);
+ }
+ this.endDelimited_(bookmark);
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed 32-bit int field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedInt32 =
+ jspb.BinaryWriter.prototype.writePackedSignedVarint32_;
+
+
+/**
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * 32-bit int field.
+ * @param {number} field
+ * @param {?Array.<string>} value
+ */
+jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) {
+ if (value == null || !value.length) return;
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteSignedVarint32(parseInt(value[i], 10));
+ }
+ this.endDelimited_(bookmark);
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed 64-bit int field.
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedInt64 =
+ jspb.BinaryWriter.prototype.writePackedVarint_;
+
+
+/**
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * 64-bit int field.
+ * @param {number} field
+ * @param {?Array.<string>} value
+ */
+jspb.BinaryWriter.prototype.writePackedInt64String =
+ function(field, value) {
+ if (value == null || !value.length) return;
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ var num = jspb.arith.Int64.fromString(value[i]);
+ this.rawWriteVarintFromNum(num);
+ }
+ this.endDelimited_(bookmark);
+};
+
+
+/**
+ * Writes an array numbers to the buffer as a packed unsigned 32-bit int field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedUint32 =
+ jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_;
+
+
+/**
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * unsigned 32-bit int field.
+ * @param {number} field
+ * @param {?Array.<string>} value
+ */
+jspb.BinaryWriter.prototype.writePackedUint32String =
+ function(field, value) {
+ if (value == null || !value.length) return;
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteUnsignedVarint32(parseInt(value[i], 10));
+ }
+ this.endDelimited_(bookmark);
+};
+
+
+/**
+ * Writes an array numbers to the buffer as a packed unsigned 64-bit int field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedUint64 =
+ jspb.BinaryWriter.prototype.writePackedVarint_;
+
+
+/**
+ * Writes an array of numbers represented as strings to the buffer as a packed
+ * unsigned 64-bit int field.
+ * @param {number} field
+ * @param {?Array.<string>} value
+ */
+jspb.BinaryWriter.prototype.writePackedUint64String =
+ function(field, value) {
+ if (value == null || !value.length) return;
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ var num = jspb.arith.UInt64.fromString(value[i]);
+ this.rawWriteVarintFromNum(num);
+ }
+ this.endDelimited_(bookmark);
+};
+
+
+/**
+ * Writes an array numbers to the buffer as a packed signed 32-bit int field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedSint32 =
+ jspb.BinaryWriter.prototype.writePackedZigzag_;
+
+
+/**
+ * Writes an array numbers to the buffer as a packed signed 64-bit int field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedSint64 =
+ jspb.BinaryWriter.prototype.writePackedZigzag_;
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed fixed32 field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedFixed32 =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 4);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteUint32(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed fixed64 field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedFixed64 =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 8);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteUint64(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed sfixed32 field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedSfixed32 =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 4);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteInt32(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed sfixed64 field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedSfixed64 =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 8);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteInt64(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed float field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedFloat =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 4);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteFloat(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of numbers to the buffer as a packed double field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedDouble =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 8);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteDouble(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of booleans to the buffer as a packed bool field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<boolean>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedBool =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteBool(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes an array of enums to the buffer as a packed enum field.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<number>} value The array of ints to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedEnum =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteEnum(value[i]);
+ }
+ this.endDelimited_(bookmark);
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to
+ * the buffer.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of hashes to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedFixedHash64 =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED);
+ this.rawWriteUnsignedVarint32(value.length * 8);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteFixedHash64(value[i]);
+ }
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
+
+
+/**
+ * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to
+ * the buffer.
+ *
+ * If 'value' is null, this method will try and copy the pre-serialized value
+ * in 'opt_buffer' if present.
+ *
+ * @param {number} field The field number.
+ * @param {?Array.<string>} value The array of hashes to write.
+ * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values.
+ * @param {?number=} opt_start The starting point in the above buffer.
+ * @param {?number=} opt_end The ending point in the above buffer.
+ */
+jspb.BinaryWriter.prototype.writePackedVarintHash64 =
+ function(field, value, opt_buffer, opt_start, opt_end) {
+ if (value != null && value.length) {
+ var bookmark = this.beginDelimited_(field);
+ for (var i = 0; i < value.length; i++) {
+ this.rawWriteVarintHash64(value[i]);
+ }
+ this.endDelimited_(bookmark);
+ } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) {
+ this.rawWriteByteRange(opt_buffer, opt_start, opt_end);
+ }
+};
diff --git a/js/binary/writer_test.js b/js/binary/writer_test.js
new file mode 100644
index 00000000..54d37a21
--- /dev/null
+++ b/js/binary/writer_test.js
@@ -0,0 +1,123 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test cases for jspb's binary protocol buffer writer. In
+ * practice BinaryWriter is used to drive the Decoder and Reader test cases,
+ * so only writer-specific tests are here.
+ *
+ * Test suite is written using Jasmine -- see http://jasmine.github.io/
+ *
+ * @author aappleby@google.com (Austin Appleby)
+ */
+
+goog.require('goog.crypt');
+goog.require('goog.testing.asserts');
+goog.require('jspb.BinaryWriter');
+
+
+/**
+ * @param {function()} func This function should throw an error when run.
+ */
+function assertFails(func) {
+ var e = assertThrows(func);
+ console.log(e);
+ //assertNotNull(e.toString().match(/Error/));
+}
+
+
+describe('binaryWriterTest', function() {
+ /**
+ * Verifies that misuse of the writer class triggers assertions.
+ */
+ it('testWriteErrors', function() {
+ // Submessages with invalid field indices should assert.
+ var writer = new jspb.BinaryWriter();
+ var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
+
+ assertFails(function() {
+ writer.writeMessage(-1, dummyMessage, goog.nullFunction);
+ });
+
+ // Writing invalid field indices should assert.
+ writer = new jspb.BinaryWriter();
+ assertFails(function() {writer.writeUint64(-1, 1);});
+
+ // Writing out-of-range field values should assert.
+ writer = new jspb.BinaryWriter();
+
+ assertFails(function() {writer.writeInt32(1, -Infinity);});
+ assertFails(function() {writer.writeInt32(1, Infinity);});
+
+ assertFails(function() {writer.writeInt64(1, -Infinity);});
+ assertFails(function() {writer.writeInt64(1, Infinity);});
+
+ assertFails(function() {writer.writeUint32(1, -1);});
+ assertFails(function() {writer.writeUint32(1, Infinity);});
+
+ assertFails(function() {writer.writeUint64(1, -1);});
+ assertFails(function() {writer.writeUint64(1, Infinity);});
+
+ assertFails(function() {writer.writeSint32(1, -Infinity);});
+ assertFails(function() {writer.writeSint32(1, Infinity);});
+
+ assertFails(function() {writer.writeSint64(1, -Infinity);});
+ assertFails(function() {writer.writeSint64(1, Infinity);});
+
+ assertFails(function() {writer.writeFixed32(1, -1);});
+ assertFails(function() {writer.writeFixed32(1, Infinity);});
+
+ assertFails(function() {writer.writeFixed64(1, -1);});
+ assertFails(function() {writer.writeFixed64(1, Infinity);});
+
+ assertFails(function() {writer.writeSfixed32(1, -Infinity);});
+ assertFails(function() {writer.writeSfixed32(1, Infinity);});
+
+ assertFails(function() {writer.writeSfixed64(1, -Infinity);});
+ assertFails(function() {writer.writeSfixed64(1, Infinity);});
+ });
+
+
+ /**
+ * Basic test of retrieving the result as a Uint8Array buffer
+ */
+ it('testGetResultBuffer', function() {
+ var expected = '0864120b48656c6c6f20776f726c641a0301020320c801';
+
+ var writer = new jspb.BinaryWriter();
+ writer.writeUint32(1, 100);
+ writer.writeString(2, 'Hello world');
+ writer.writeBytes(3, new Uint8Array([1, 2, 3]));
+ writer.writeUint32(4, 200);
+
+ var buffer = writer.getResultBuffer();
+ assertEquals(expected, goog.crypt.byteArrayToHex(buffer));
+ });
+});
diff --git a/js/data.proto b/js/data.proto
new file mode 100644
index 00000000..74a8a994
--- /dev/null
+++ b/js/data.proto
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: mwr@google.com (Mark Rawling)
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.test;
+
+// legacy data, must be nested
+message data {
+ message NestedData {
+ required string str = 1;
+ }
+}
+
+// new data, does not require nesting
+message UnnestedData {
+ required string str = 1;
+}
+
diff --git a/js/debug.js b/js/debug.js
new file mode 100644
index 00000000..48389118
--- /dev/null
+++ b/js/debug.js
@@ -0,0 +1,140 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Utilities to debug JSPB based proto objects.
+ */
+
+goog.provide('jspb.debug');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.object');
+goog.require('jspb.Message');
+
+
+/**
+ * Turns a proto into a human readable object that can i.e. be written to the
+ * console: {@code console.log(jspb.debug.dump(myProto))}.
+ * This function makes a best effort and may not work in all cases. It will not
+ * work in obfuscated and or optimized code.
+ * Use this in environments where {@see jspb.Message.prototype.toObject} is
+ * not available for code size reasons.
+ * @param {jspb.Message} message A jspb.Message.
+ * @return {Object}
+ */
+jspb.debug.dump = function(message) {
+ if (!goog.DEBUG) {
+ return null;
+ }
+ goog.asserts.assert(message instanceof jspb.Message,
+ 'jspb.Message instance expected');
+ /** @type {Object} */
+ var object = message;
+ goog.asserts.assert(object['getExtension'],
+ 'Only unobfuscated and unoptimized compilation modes supported.');
+ return /** @type {Object} */ (jspb.debug.dump_(message));
+};
+
+
+/**
+ * Recursively introspects a message and the values its getters return to
+ * make a best effort in creating a human readable representation of the
+ * message.
+ * @param {*} thing A jspb.Message, Array or primitive type to dump.
+ * @return {*}
+ * @private
+ */
+jspb.debug.dump_ = function(thing) {
+ var type = goog.typeOf(thing);
+ if (type == 'number' || type == 'string' || type == 'boolean' ||
+ type == 'null' || type == 'undefined') {
+ return thing;
+ }
+ if (type == 'array') {
+ goog.asserts.assertArray(thing);
+ return goog.array.map(thing, jspb.debug.dump_);
+ }
+ var message = thing; // Copy because we don't want type inference on thing.
+ goog.asserts.assert(message instanceof jspb.Message,
+ 'Only messages expected: ' + thing);
+ var ctor = message.constructor;
+ var messageName = ctor.name || ctor.displayName;
+ var object = {
+ '$name': messageName
+ };
+ for (var name in ctor.prototype) {
+ var match = /^get([A-Z]\w*)/.exec(name);
+ if (match && name != 'getExtension' &&
+ name != 'getJsPbMessageId') {
+ var val = thing[name]();
+ if (val != null) {
+ object[jspb.debug.formatFieldName_(match[1])] = jspb.debug.dump_(val);
+ }
+ }
+ }
+ if (COMPILED && thing['extensionObject_']) {
+ object['$extensions'] = 'Recursive dumping of extensions not supported ' +
+ 'in compiled code. Switch to uncompiled or dump extension object ' +
+ 'directly';
+ return object;
+ }
+ var extensionsObject;
+ for (var id in ctor['extensions']) {
+ if (/^\d+$/.test(id)) {
+ var ext = ctor['extensions'][id];
+ var extVal = thing.getExtension(ext);
+ var fieldName = goog.object.getKeys(ext.fieldName)[0];
+ if (extVal != null) {
+ if (!extensionsObject) {
+ extensionsObject = object['$extensions'] = {};
+ }
+ extensionsObject[jspb.debug.formatFieldName_(fieldName)] =
+ jspb.debug.dump_(extVal);
+ }
+ }
+ }
+ return object;
+};
+
+
+/**
+ * Formats a field name for output as camelCase.
+ *
+ * @param {string} name Name of the field.
+ * @return {string}
+ * @private
+ */
+jspb.debug.formatFieldName_ = function(name) {
+ // Name may be in TitleCase.
+ return name.replace(/^[A-Z]/, function(c) {
+ return c.toLowerCase();
+ });
+};
diff --git a/js/debug_test.js b/js/debug_test.js
new file mode 100644
index 00000000..615fc7c6
--- /dev/null
+++ b/js/debug_test.js
@@ -0,0 +1,101 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+goog.setTestOnly();
+
+goog.require('goog.testing.asserts');
+goog.require('jspb.debug');
+goog.require('proto.jspb.test.HasExtensions');
+goog.require('proto.jspb.test.IsExtension');
+goog.require('proto.jspb.test.Simple1');
+
+
+
+describe('debugTest', function() {
+ it('testSimple1', function() {
+ if (COMPILED) {
+ return;
+ }
+ var message = new proto.jspb.test.Simple1();
+ message.setAString('foo');
+ assertObjectEquals({
+ $name: 'proto.jspb.test.Simple1',
+ 'aString': 'foo',
+ 'aRepeatedStringList': []
+ }, jspb.debug.dump(message));
+
+ message.setABoolean(true);
+ message.setARepeatedStringList(['1', '2']);
+
+ assertObjectEquals({
+ $name: 'proto.jspb.test.Simple1',
+ 'aString': 'foo',
+ 'aRepeatedStringList': ['1', '2'],
+ 'aBoolean': true
+ }, jspb.debug.dump(message));
+
+ message.setAString(undefined);
+
+ assertObjectEquals({
+ $name: 'proto.jspb.test.Simple1',
+ 'aRepeatedStringList': ['1', '2'],
+ 'aBoolean': true
+ }, jspb.debug.dump(message));
+ });
+
+
+ it('testExtensions', function() {
+ if (COMPILED) {
+ return;
+ }
+ var extension = new proto.jspb.test.IsExtension();
+ extension.setExt1('ext1field');
+ var extendable = new proto.jspb.test.HasExtensions();
+ extendable.setStr1('v1');
+ extendable.setStr2('v2');
+ extendable.setStr3('v3');
+ extendable.setExtension(proto.jspb.test.IsExtension.extField, extension);
+
+ assertObjectEquals({
+ '$name': 'proto.jspb.test.HasExtensions',
+ 'str1': 'v1',
+ 'str2': 'v2',
+ 'str3': 'v3',
+ '$extensions': {
+ 'extField': {
+ '$name': 'proto.jspb.test.IsExtension',
+ 'ext1': 'ext1field'
+ },
+ 'repeatedSimpleList': []
+ }
+ }, jspb.debug.dump(extendable));
+ });
+
+});
diff --git a/js/gulpfile.js b/js/gulpfile.js
new file mode 100644
index 00000000..79095d65
--- /dev/null
+++ b/js/gulpfile.js
@@ -0,0 +1,29 @@
+var gulp = require('gulp');
+var exec = require('child_process').exec;
+
+gulp.task('genproto', function (cb) {
+ exec('../src/protoc --js_out=library=testproto_libs,binary:. -I ../src -I . *.proto ../src/google/protobuf/descriptor.proto',
+ function (err, stdout, stderr) {
+ console.log(stdout);
+ console.log(stderr);
+ cb(err);
+ });
+})
+
+gulp.task('deps', ['genproto'], function (cb) {
+ exec('./node_modules/google-closure-library/closure/bin/build/depswriter.py *.js binary/*.js > deps.js',
+ function (err, stdout, stderr) {
+ console.log(stdout);
+ console.log(stderr);
+ cb(err);
+ });
+})
+
+gulp.task('test', ['genproto', 'deps'], function (cb) {
+ exec('JASMINE_CONFIG_PATH=jasmine.json ./node_modules/.bin/jasmine',
+ function (err, stdout, stderr) {
+ console.log(stdout);
+ console.log(stderr);
+ cb(err);
+ });
+});
diff --git a/js/jasmine.json b/js/jasmine.json
new file mode 100644
index 00000000..f83c54c0
--- /dev/null
+++ b/js/jasmine.json
@@ -0,0 +1,12 @@
+{
+ "spec_dir": "",
+ "spec_files": [
+ "*_test.js",
+ "binary/*_test.js"
+ ],
+ "helpers": [
+ "node_modules/google-closure-library/closure/goog/bootstrap/nodejs.js",
+ "node_loader.js",
+ "deps.js"
+ ]
+}
diff --git a/js/message.js b/js/message.js
new file mode 100644
index 00000000..cef9aefd
--- /dev/null
+++ b/js/message.js
@@ -0,0 +1,1125 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Definition of jspb.Message.
+ *
+ * @author mwr@google.com (Mark Rawling)
+ */
+
+goog.provide('jspb.ExtensionFieldInfo');
+goog.provide('jspb.Message');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.json');
+goog.require('goog.object');
+
+// Not needed in compilation units that have no protos with xids.
+goog.forwardDeclare('xid.String');
+
+
+
+/**
+ * Stores information for a single extension field.
+ *
+ * For example, an extension field defined like so:
+ *
+ * extend BaseMessage {
+ * optional MyMessage my_field = 123;
+ * }
+ *
+ * will result in an ExtensionFieldInfo object with these properties:
+ *
+ * {
+ * fieldIndex: 123,
+ * fieldName: {my_field_renamed: 0},
+ * ctor: proto.example.MyMessage,
+ * toObjectFn: proto.example.MyMessage.toObject,
+ * isRepeated: 0
+ * }
+ *
+ * We include `toObjectFn` to allow the JSCompiler to perform dead-code removal
+ * on unused toObject() methods.
+ *
+ * If an extension field is primitive, ctor and toObjectFn will be null.
+ * isRepeated should be 0 or 1.
+ *
+ * binary{Reader,Writer}Fn and (if message type) binaryMessageSerializeFn are
+ * always provided. binaryReaderFn and binaryWriterFn are references to the
+ * appropriate methods on BinaryReader/BinaryWriter to read/write the value of
+ * this extension, and binaryMessageSerializeFn is a reference to the message
+ * class's .serializeBinary method, if available.
+ *
+ * @param {number} fieldNumber
+ * @param {Object} fieldName This has the extension field name as a property.
+ * @param {?function(new: jspb.Message, Array=)} ctor
+ * @param {?function((boolean|undefined),!jspb.Message):!Object} toObjectFn
+ * @param {number} isRepeated
+ * @param {?function(number,?)=} opt_binaryReaderFn
+ * @param {?function(number,?)|function(number,?,?,?,?,?)=} opt_binaryWriterFn
+ * @param {?function(?,?)=} opt_binaryMessageSerializeFn
+ * @param {?function(?,?)=} opt_binaryMessageDeserializeFn
+ * @param {?boolean=} opt_isPacked
+ * @constructor
+ * @struct
+ * @template T
+ */
+jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn,
+ isRepeated, opt_binaryReaderFn, opt_binaryWriterFn,
+ opt_binaryMessageSerializeFn, opt_binaryMessageDeserializeFn,
+ opt_isPacked) {
+ /** @const */
+ this.fieldIndex = fieldNumber;
+ /** @const */
+ this.fieldName = fieldName;
+ /** @const */
+ this.ctor = ctor;
+ /** @const */
+ this.toObjectFn = toObjectFn;
+ /** @const */
+ this.binaryReaderFn = opt_binaryReaderFn;
+ /** @const */
+ this.binaryWriterFn = opt_binaryWriterFn;
+ /** @const */
+ this.binaryMessageSerializeFn = opt_binaryMessageSerializeFn;
+ /** @const */
+ this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn;
+ /** @const */
+ this.isRepeated = isRepeated;
+ /** @const */
+ this.isPacked = opt_isPacked;
+};
+
+
+/**
+ * Base class for all JsPb messages.
+ * @constructor
+ * @struct
+ */
+jspb.Message = function() {
+};
+
+
+/**
+ * @define {boolean} Whether to generate toObject methods for objects. Turn
+ * this off, if you do not want toObject to be ever used in your project.
+ * When turning off this flag, consider adding a conformance test that bans
+ * calling toObject. Enabling this will disable the JSCompiler's ability to
+ * dead code eliminate fields used in protocol buffers that are never used
+ * in an application.
+ */
+goog.define('jspb.Message.GENERATE_TO_OBJECT', true);
+
+
+/**
+ * @define {boolean} Whether to generate fromObject methods for objects. Turn
+ * this off, if you do not want fromObject to be ever used in your project.
+ * When turning off this flag, consider adding a conformance test that bans
+ * calling fromObject. Enabling this might disable the JSCompiler's ability
+ * to dead code eliminate fields used in protocol buffers that are never
+ * used in an application.
+ * NOTE: By default no protos actually have a fromObject method. You need to
+ * add the jspb.generate_from_object options to the proto definition to
+ * activate the feature.
+ * By default this is enabled for test code only.
+ */
+goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE);
+
+
+/**
+ * @define {boolean} Turning on this flag does NOT change the behavior of JSPB
+ * and only affects private internal state. It may, however, break some
+ * tests that use naive deeply-equals algorithms, because using a proto
+ * mutates its internal state.
+ * Projects are advised to turn this flag always on.
+ */
+goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED);
+// TODO(b/19419436) Turn this on by default.
+
+
+/**
+ * The internal data array.
+ * @type {!Array}
+ * @protected
+ */
+jspb.Message.prototype.array;
+
+
+/**
+ * Wrappers are the constructed instances of message-type fields. They are built
+ * on demand from the raw array data. Includes message fields, repeated message
+ * fields and extension message fields. Indexed by field number.
+ * @type {Object}
+ * @private
+ */
+jspb.Message.prototype.wrappers_;
+
+
+/**
+ * The object that contains extension fields, if any. This is an object that
+ * maps from a proto field number to the field's value.
+ * @type {Object}
+ * @private
+ */
+jspb.Message.prototype.extensionObject_;
+
+
+/**
+ * Non-extension fields with a field number at or above the pivot are
+ * stored in the extension object (in addition to all extension fields).
+ * @type {number}
+ * @private
+ */
+jspb.Message.prototype.pivot_;
+
+
+/**
+ * The JsPb message_id of this proto.
+ * @type {string|undefined} the message id or undefined if this message
+ * has no id.
+ * @private
+ */
+jspb.Message.prototype.messageId_;
+
+
+/**
+ * The xid of this proto type (The same for all instances of a proto). Provides
+ * a way to identify a proto by stable obfuscated name.
+ * @see {xid}.
+ * Available if {@link jspb.generate_xid} is added as a Message option to
+ * a protocol buffer.
+ * @const {!xid.String|undefined} The xid or undefined if message is
+ * annotated to generate the xid.
+ */
+jspb.Message.prototype.messageXid;
+
+
+
+/**
+ * Returns the JsPb message_id of this proto.
+ * @return {string|undefined} the message id or undefined if this message
+ * has no id.
+ */
+jspb.Message.prototype.getJsPbMessageId = function() {
+ return this.messageId_;
+};
+
+
+/**
+ * An offset applied to lookups into this.array to account for the presence or
+ * absence of a messageId at position 0. For response messages, this will be 0.
+ * Otherwise, it will be -1 so that the first array position is not wasted.
+ * @type {number}
+ * @private
+ */
+jspb.Message.prototype.arrayIndexOffset_;
+
+
+/**
+ * Returns the index into msg.array at which the proto field with tag number
+ * fieldNumber will be located.
+ * @param {!jspb.Message} msg Message for which we're calculating an index.
+ * @param {number} fieldNumber The field number.
+ * @return {number} The index.
+ * @private
+ */
+jspb.Message.getIndex_ = function(msg, fieldNumber) {
+ return fieldNumber + msg.arrayIndexOffset_;
+};
+
+
+/**
+ * Initializes a JsPb Message.
+ * @param {!jspb.Message} msg The JsPb proto to modify.
+ * @param {Array|undefined} data An initial data array.
+ * @param {string|number} messageId For response messages, the message id or ''
+ * if no message id is specified. For non-response messages, 0.
+ * @param {number} suggestedPivot The field number at which to start putting
+ * fields into the extension object. This is only used if data does not
+ * contain an extension object already. -1 if no extension object is
+ * required for this message type.
+ * @param {Array<number>} repeatedFields The message's repeated fields.
+ * @param {Array<!Array<number>>=} opt_oneofFields The fields belonging to
+ * each of the message's oneof unions.
+ * @protected
+ */
+jspb.Message.initialize = function(
+ msg, data, messageId, suggestedPivot, repeatedFields, opt_oneofFields) {
+ msg.wrappers_ = jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? null : {};
+ if (!data) {
+ data = messageId ? [messageId] : [];
+ }
+ msg.messageId_ = messageId ? String(messageId) : undefined;
+ // If the messageId is 0, this message is not a response message, so we shift
+ // array indices down by 1 so as not to waste the first position in the array,
+ // which would otherwise go unused.
+ msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
+ msg.array = data;
+ jspb.Message.materializeExtensionObject_(msg, suggestedPivot);
+ if (repeatedFields) {
+ for (var i = 0; i < repeatedFields.length; i++) {
+ var fieldNumber = repeatedFields[i];
+ if (fieldNumber < msg.pivot_) {
+ var index = jspb.Message.getIndex_(msg, fieldNumber);
+ msg.array[index] = msg.array[index] ||
+ (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ?
+ jspb.Message.EMPTY_LIST_SENTINEL_ :
+ []);
+ } else {
+ msg.extensionObject_[fieldNumber] =
+ msg.extensionObject_[fieldNumber] ||
+ (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ?
+ jspb.Message.EMPTY_LIST_SENTINEL_ :
+ []);
+ }
+ }
+ }
+
+ if (opt_oneofFields && opt_oneofFields.length) {
+ // Compute the oneof case for each union. This ensures only one value is
+ // set in the union.
+ goog.array.forEach(
+ opt_oneofFields, goog.partial(jspb.Message.computeOneofCase, msg));
+ }
+};
+
+
+/**
+ * Used to mark empty repeated fields. Serializes to null when serialized
+ * to JSON.
+ * When reading a repeated field readers must check the return value against
+ * this value and return and replace it with a new empty array if it is
+ * present.
+ * @private @const {!Object}
+ */
+jspb.Message.EMPTY_LIST_SENTINEL_ = goog.DEBUG && Object.freeze ?
+ Object.freeze([]) :
+ [];
+
+
+/**
+ * Ensures that the array contains an extension object if necessary.
+ * If the array contains an extension object in its last position, then the
+ * object is kept in place and its position is used as the pivot. If not, then
+ * create an extension object using suggestedPivot. If suggestedPivot is -1,
+ * we don't have an extension object at all, in which case all fields are stored
+ * in the array.
+ * @param {!jspb.Message} msg The JsPb proto to modify.
+ * @param {number} suggestedPivot See description for initialize().
+ * @private
+ */
+jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) {
+ if (msg.array.length) {
+ var foundIndex = msg.array.length - 1;
+ var obj = msg.array[foundIndex];
+ // Normal fields are never objects, so we can be sure that if we find an
+ // object here, then it's the extension object. However, we must ensure that
+ // the object is not an array, since arrays are valid field values.
+ // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug
+ // in Safari on iOS 8. See the description of CL/86511464 for details.
+ if (obj && typeof obj == 'object' && !goog.isArray(obj)) {
+ msg.pivot_ = foundIndex - msg.arrayIndexOffset_;
+ msg.extensionObject_ = obj;
+ return;
+ }
+ }
+ // This complexity exists because we keep all extension fields in the
+ // extensionObject_ regardless of proto field number. Changing this would
+ // simplify the code here, but it would require changing the serialization
+ // format from the server, which is not backwards compatible.
+ // TODO(jshneier): Should we just treat extension fields the same as
+ // non-extension fields, and select whether they appear in the object or in
+ // the array purely based on tag number? This would allow simplifying all the
+ // get/setExtension logic, but it would require the breaking change described
+ // above.
+ if (suggestedPivot > -1) {
+ msg.pivot_ = suggestedPivot;
+ var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot);
+ if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) {
+ msg.extensionObject_ = msg.array[pivotIndex] = {};
+ } else {
+ // Initialize to null to avoid changing the shape of the proto when it
+ // gets eventually set.
+ msg.extensionObject_ = null;
+ }
+ } else {
+ msg.pivot_ = Number.MAX_VALUE;
+ }
+};
+
+
+/**
+ * Creates an empty extensionObject_ if non exists.
+ * @param {!jspb.Message} msg The JsPb proto to modify.
+ * @private
+ */
+jspb.Message.maybeInitEmptyExtensionObject_ = function(msg) {
+ var pivotIndex = jspb.Message.getIndex_(msg, msg.pivot_);
+ if (!msg.array[pivotIndex]) {
+ msg.extensionObject_ = msg.array[pivotIndex] = {};
+ }
+};
+
+
+/**
+ * Converts a JsPb repeated message field into an object list.
+ * @param {!Array<T>} field The repeated message field to be
+ * converted.
+ * @param {?function(boolean=): Object|
+ * function((boolean|undefined),T): Object} toObjectFn The toObject
+ * function for this field. We need to pass this for effective dead code
+ * removal.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ * for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Array<Object>} An array of converted message objects.
+ * @template T
+ */
+jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) {
+ // Not using goog.array.map in the generated code to keep it small.
+ // And not using it here to avoid a function call.
+ var result = [];
+ for (var i = 0; i < field.length; i++) {
+ result[i] = toObjectFn.call(field[i], opt_includeInstance,
+ /** @type {!jspb.Message} */ (field[i]));
+ }
+ return result;
+};
+
+
+/**
+ * Adds a proto's extension data to a Soy rendering object.
+ * @param {!jspb.Message} proto The proto whose extensions to convert.
+ * @param {!Object} obj The Soy object to add converted extension data to.
+ * @param {!Object} extensions The proto class' registered extensions.
+ * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto
+ * class' getExtension function. Passed for effective dead code removal.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ * for transitional soy proto support: http://goto/soy-param-migration
+ */
+jspb.Message.toObjectExtension = function(proto, obj, extensions,
+ getExtensionFn, opt_includeInstance) {
+ for (var fieldNumber in extensions) {
+ var fieldInfo = extensions[fieldNumber];
+ var value = getExtensionFn.call(proto, fieldInfo);
+ if (value) {
+ for (var name in fieldInfo.fieldName) {
+ if (fieldInfo.fieldName.hasOwnProperty(name)) {
+ break; // the compiled field name
+ }
+ }
+ if (!fieldInfo.toObjectFn) {
+ obj[name] = value;
+ } else {
+ if (fieldInfo.isRepeated) {
+ obj[name] = jspb.Message.toObjectList(
+ /** @type {!Array<jspb.Message>} */ (value),
+ fieldInfo.toObjectFn, opt_includeInstance);
+ } else {
+ obj[name] = fieldInfo.toObjectFn(opt_includeInstance, value);
+ }
+ }
+ }
+ }
+};
+
+
+/**
+ * Writes a proto's extension data to a binary-format output stream.
+ * @param {!jspb.Message} proto The proto whose extensions to convert.
+ * @param {*} writer The binary-format writer to write to.
+ * @param {!Object} extensions The proto class' registered extensions.
+ * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto
+ * class' getExtension function. Passed for effective dead code removal.
+ */
+jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
+ getExtensionFn) {
+ for (var fieldNumber in extensions) {
+ var fieldInfo = extensions[fieldNumber];
+ // The old codegen doesn't add the extra fields to ExtensionFieldInfo, so we
+ // need to gracefully error-out here rather than produce a null dereference
+ // below.
+ if (!fieldInfo.binaryWriterFn) {
+ throw new Error('Message extension present that was generated ' +
+ 'without binary serialization support');
+ }
+ var value = getExtensionFn.call(proto, fieldInfo);
+ if (value) {
+ if (fieldInfo.ctor) { // is this a message type?
+ // If the message type of the extension was generated without binary
+ // support, there may not be a binary message serializer function, and
+ // we can't know when we codegen the extending message that the extended
+ // message may require binary support, so we can *only* catch this error
+ // here, at runtime (and this decoupled codegen is the whole point of
+ // extensions!).
+ if (fieldInfo.binaryMessageSerializeFn) {
+ fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex,
+ value, fieldInfo.binaryMessageSerializeFn);
+ } else {
+ throw new Error('Message extension present holding submessage ' +
+ 'without binary support enabled, and message is ' +
+ 'being serialized to binary format');
+ }
+ } else {
+ fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, value);
+ }
+ }
+ }
+};
+
+
+/**
+ * Reads an extension field from the given reader and, if a valid extension,
+ * sets the extension value.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {{skipField:function(),getFieldNumber:function():number}} reader
+ * @param {!Object} extensions The extensions object.
+ * @param {function(jspb.ExtensionFieldInfo)} getExtensionFn
+ * @param {function(jspb.ExtensionFieldInfo, ?)} setExtensionFn
+ */
+jspb.Message.readBinaryExtension = function(msg, reader, extensions,
+ getExtensionFn, setExtensionFn) {
+ var fieldInfo = extensions[reader.getFieldNumber()];
+ if (!fieldInfo) {
+ reader.skipField();
+ return;
+ }
+ if (!fieldInfo.binaryReaderFn) {
+ throw new Error('Deserializing extension whose generated code does not ' +
+ 'support binary format');
+ }
+
+ var value;
+ if (fieldInfo.ctor) {
+ // Message type.
+ value = new fieldInfo.ctor();
+ fieldInfo.binaryReaderFn.call(
+ reader, value, fieldInfo.binaryMessageDeserializeFn);
+ } else {
+ // All other types.
+ value = fieldInfo.binaryReaderFn.call(reader);
+ }
+
+ if (fieldInfo.isRepeated && !fieldInfo.isPacked) {
+ var currentList = getExtensionFn.call(msg, fieldInfo);
+ if (!currentList) {
+ setExtensionFn.call(msg, fieldInfo, [value]);
+ } else {
+ currentList.push(value);
+ }
+ } else {
+ setExtensionFn.call(msg, fieldInfo, value);
+ }
+};
+
+
+/**
+ * Gets the value of a non-extension field.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @return {string|number|boolean|Uint8Array|Array|null|undefined}
+ * The field's value.
+ * @protected
+ */
+jspb.Message.getField = function(msg, fieldNumber) {
+ if (fieldNumber < msg.pivot_) {
+ var index = jspb.Message.getIndex_(msg, fieldNumber);
+ var val = msg.array[index];
+ if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
+ return msg.array[index] = [];
+ }
+ return val;
+ } else {
+ var val = msg.extensionObject_[fieldNumber];
+ if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
+ return msg.extensionObject_[fieldNumber] = [];
+ }
+ return val;
+ }
+};
+
+
+/**
+ * Gets the value of a non-extension primitive field, with proto3 (non-nullable
+ * primitives) semantics. Returns `defaultValue` if the field is not otherwise
+ * set.
+ * @template T
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {T} defaultValue The default value.
+ * @return {T} The field's value.
+ * @protected
+ */
+jspb.Message.getFieldProto3 = function(msg, fieldNumber, defaultValue) {
+ var value = jspb.Message.getField(msg, fieldNumber);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+};
+
+
+/**
+ * Sets the value of a non-extension field.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {string|number|boolean|Uint8Array|Array|undefined} value New value
+ * @protected
+ */
+jspb.Message.setField = function(msg, fieldNumber, value) {
+ if (fieldNumber < msg.pivot_) {
+ msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value;
+ } else {
+ msg.extensionObject_[fieldNumber] = value;
+ }
+};
+
+
+/**
+ * Sets the value of a field in a oneof union and clears all other fields in
+ * the union.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {!Array<number>} oneof The fields belonging to the union.
+ * @param {string|number|boolean|Uint8Array|Array|undefined} value New value
+ * @protected
+ */
+jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) {
+ var currentCase = jspb.Message.computeOneofCase(msg, oneof);
+ if (currentCase && currentCase !== fieldNumber && value !== undefined) {
+ if (msg.wrappers_ && currentCase in msg.wrappers_) {
+ msg.wrappers_[currentCase] = undefined;
+ }
+ jspb.Message.setField(msg, currentCase, undefined);
+ }
+ jspb.Message.setField(msg, fieldNumber, value);
+};
+
+
+/**
+ * Computes the selection in a oneof group for the given message, ensuring
+ * only one field is set in the process.
+ *
+ * According to the protobuf language guide (
+ * https://developers.google.com/protocol-buffers/docs/proto#oneof), "if the
+ * parser encounters multiple members of the same oneof on the wire, only the
+ * last member seen is used in the parsed message." Since JSPB serializes
+ * messages to a JSON array, the "last member seen" will always be the field
+ * with the greatest field number (directly corresponding to the greatest
+ * array index).
+ *
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {!Array<number>} oneof The field numbers belonging to the union.
+ * @return {number} The field number currently set in the union, or 0 if none.
+ * @protected
+ */
+jspb.Message.computeOneofCase = function(msg, oneof) {
+ var oneofField;
+ var oneofValue;
+
+ goog.array.forEach(oneof, function(fieldNumber) {
+ var value = jspb.Message.getField(msg, fieldNumber);
+ if (goog.isDefAndNotNull(value)) {
+ oneofField = fieldNumber;
+ oneofValue = value;
+ jspb.Message.setField(msg, fieldNumber, undefined);
+ }
+ });
+
+ if (oneofField) {
+ // NB: We know the value is unique, so we can call jspb.Message.setField
+ // directly instead of jpsb.Message.setOneofField. Also, setOneofField
+ // calls this function.
+ jspb.Message.setField(msg, oneofField, oneofValue);
+ return oneofField;
+ }
+
+ return 0;
+};
+
+
+/**
+ * Gets and wraps a proto field on access.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
+ * @param {number} fieldNumber The field number.
+ * @param {number=} opt_required True (1) if this is a required field.
+ * @return {jspb.Message} The field as a jspb proto.
+ * @protected
+ */
+jspb.Message.getWrapperField = function(msg, ctor, fieldNumber, opt_required) {
+ // TODO(mwr): Consider copying data and/or arrays.
+ if (!msg.wrappers_) {
+ msg.wrappers_ = {};
+ }
+ if (!msg.wrappers_[fieldNumber]) {
+ var data = /** @type {Array} */ (jspb.Message.getField(msg, fieldNumber));
+ if (opt_required || data) {
+ // TODO(mwr): Remove existence test for always valid default protos.
+ msg.wrappers_[fieldNumber] = new ctor(data);
+ }
+ }
+ return /** @type {jspb.Message} */ (msg.wrappers_[fieldNumber]);
+};
+
+
+/**
+ * Gets and wraps a repeated proto field on access.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
+ * @param {number} fieldNumber The field number.
+ * @return {Array<!jspb.Message>} The repeated field as an array of protos.
+ * @protected
+ */
+jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) {
+ if (!msg.wrappers_) {
+ msg.wrappers_ = {};
+ }
+ if (!msg.wrappers_[fieldNumber]) {
+ var data = jspb.Message.getField(msg, fieldNumber);
+ for (var wrappers = [], i = 0; i < data.length; i++) {
+ wrappers[i] = new ctor(data[i]);
+ }
+ msg.wrappers_[fieldNumber] = wrappers;
+ }
+ var val = msg.wrappers_[fieldNumber];
+ if (val == jspb.Message.EMPTY_LIST_SENTINEL_) {
+ val = msg.wrappers_[fieldNumber] = [];
+ }
+ return /** @type {Array<!jspb.Message>} */ (val);
+};
+
+
+/**
+ * Sets a proto field and syncs it to the backing array.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {jspb.Message|undefined} value A new value for this proto field.
+ * @protected
+ */
+jspb.Message.setWrapperField = function(msg, fieldNumber, value) {
+ if (!msg.wrappers_) {
+ msg.wrappers_ = {};
+ }
+ var data = value ? value.toArray() : value;
+ msg.wrappers_[fieldNumber] = value;
+ jspb.Message.setField(msg, fieldNumber, data);
+};
+
+
+/**
+ * Sets a proto field in a oneof union and syncs it to the backing array.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {!Array<number>} oneof The fields belonging to the union.
+ * @param {jspb.Message|undefined} value A new value for this proto field.
+ * @protected
+ */
+jspb.Message.setOneofWrapperField = function(msg, fieldNumber, oneof, value) {
+ if (!msg.wrappers_) {
+ msg.wrappers_ = {};
+ }
+ var data = value ? value.toArray() : value;
+ msg.wrappers_[fieldNumber] = value;
+ jspb.Message.setOneofField(msg, fieldNumber, oneof, data);
+};
+
+
+/**
+ * Sets a repeated proto field and syncs it to the backing array.
+ * @param {!jspb.Message} msg A jspb proto.
+ * @param {number} fieldNumber The field number.
+ * @param {Array<!jspb.Message>|undefined} value An array of protos.
+ * @protected
+ */
+jspb.Message.setRepeatedWrapperField = function(msg, fieldNumber, value) {
+ if (!msg.wrappers_) {
+ msg.wrappers_ = {};
+ }
+ value = value || [];
+ for (var data = [], i = 0; i < value.length; i++) {
+ data[i] = value[i].toArray();
+ }
+ msg.wrappers_[fieldNumber] = value;
+ jspb.Message.setField(msg, fieldNumber, data);
+};
+
+
+/**
+ * Converts a JsPb repeated message field into a map. The map will contain
+ * protos unless an optional toObject function is given, in which case it will
+ * contain objects suitable for Soy rendering.
+ * @param {!Array<T>} field The repeated message field to be
+ * converted.
+ * @param {function() : string?} mapKeyGetterFn The function to get the key of
+ * the map.
+ * @param {?function(boolean=): Object|
+ * function((boolean|undefined),T): Object} opt_toObjectFn The
+ * toObject function for this field. We need to pass this for effective
+ * dead code removal.
+ * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
+ * for transitional soy proto support: http://goto/soy-param-migration
+ * @return {!Object.<string, Object>} A map of proto or Soy objects.
+ * @template T
+ */
+jspb.Message.toMap = function(
+ field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) {
+ var result = {};
+ for (var i = 0; i < field.length; i++) {
+ result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ?
+ opt_toObjectFn.call(field[i], opt_includeInstance,
+ /** @type {!jspb.Message} */ (field[i])) : field[i];
+ }
+ return result;
+};
+
+
+/**
+ * Returns the internal array of this proto.
+ * <p>Note: If you use this array to construct a second proto, the content
+ * would then be partially shared between the two protos.
+ * @return {!Array} The proto represented as an array.
+ */
+jspb.Message.prototype.toArray = function() {
+ return this.array;
+};
+
+
+
+
+/**
+ * Creates a string representation of the internal data array of this proto.
+ * <p>NOTE: This string is *not* suitable for use in server requests.
+ * @return {string} A string representation of this proto.
+ * @override
+ */
+jspb.Message.prototype.toString = function() {
+ return this.array.toString();
+};
+
+
+/**
+ * Gets the value of the extension field from the extended object.
+ * @param {jspb.ExtensionFieldInfo.<T>} fieldInfo Specifies the field to get.
+ * @return {T} The value of the field.
+ * @template T
+ */
+jspb.Message.prototype.getExtension = function(fieldInfo) {
+ if (!this.extensionObject_) {
+ return undefined;
+ }
+ if (!this.wrappers_) {
+ this.wrappers_ = {};
+ }
+ var fieldNumber = fieldInfo.fieldIndex;
+ if (fieldInfo.isRepeated) {
+ if (fieldInfo.ctor) {
+ if (!this.wrappers_[fieldNumber]) {
+ this.wrappers_[fieldNumber] =
+ goog.array.map(this.extensionObject_[fieldNumber] || [],
+ function(arr) {
+ return new fieldInfo.ctor(arr);
+ });
+ }
+ return this.wrappers_[fieldNumber];
+ } else {
+ return this.extensionObject_[fieldNumber];
+ }
+ } else {
+ if (fieldInfo.ctor) {
+ if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) {
+ this.wrappers_[fieldNumber] = new fieldInfo.ctor(
+ /** @type {Array|undefined} */ (
+ this.extensionObject_[fieldNumber]));
+ }
+ return this.wrappers_[fieldNumber];
+ } else {
+ return this.extensionObject_[fieldNumber];
+ }
+ }
+};
+
+
+/**
+ * Sets the value of the extension field in the extended object.
+ * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set.
+ * @param {jspb.Message|string|number|boolean|Array} value The value to set.
+ */
+jspb.Message.prototype.setExtension = function(fieldInfo, value) {
+ if (!this.wrappers_) {
+ this.wrappers_ = {};
+ }
+ jspb.Message.maybeInitEmptyExtensionObject_(this);
+ var fieldNumber = fieldInfo.fieldIndex;
+ if (fieldInfo.isRepeated) {
+ value = value || [];
+ if (fieldInfo.ctor) {
+ this.wrappers_[fieldNumber] = value;
+ this.extensionObject_[fieldNumber] = goog.array.map(
+ /** @type {Array<jspb.Message>} */ (value), function(msg) {
+ return msg.toArray();
+ });
+ } else {
+ this.extensionObject_[fieldNumber] = value;
+ }
+ } else {
+ if (fieldInfo.ctor) {
+ this.wrappers_[fieldNumber] = value;
+ this.extensionObject_[fieldNumber] = value ? value.toArray() : value;
+ } else {
+ this.extensionObject_[fieldNumber] = value;
+ }
+ }
+};
+
+
+/**
+ * Creates a difference object between two messages.
+ *
+ * The result will contain the top-level fields of m2 that differ from those of
+ * m1 at any level of nesting. No data is cloned, the result object will
+ * share its top-level elements with m2 (but not with m1).
+ *
+ * Note that repeated fields should not have null/undefined elements, but if
+ * they do, this operation will treat repeated fields of different length as
+ * the same if the only difference between them is due to trailing
+ * null/undefined values.
+ *
+ * @param {!jspb.Message} m1 The first message object.
+ * @param {!jspb.Message} m2 The second message object.
+ * @return {!jspb.Message} The difference returned as a proto message.
+ * Note that the returned message may be missing required fields. This is
+ * currently tolerated in Js, but would cause an error if you tried to
+ * send such a proto to the server. You can access the raw difference
+ * array with result.toArray().
+ * @throws {Error} If the messages are responses with different types.
+ */
+jspb.Message.difference = function(m1, m2) {
+ if (!(m1 instanceof m2.constructor)) {
+ throw new Error('Messages have different types.');
+ }
+ var arr1 = m1.toArray();
+ var arr2 = m2.toArray();
+ var res = [];
+ var start = 0;
+ var length = arr1.length > arr2.length ? arr1.length : arr2.length;
+ if (m1.getJsPbMessageId()) {
+ res[0] = m1.getJsPbMessageId();
+ start = 1;
+ }
+ for (var i = start; i < length; i++) {
+ if (!jspb.Message.compareFields(arr1[i], arr2[i])) {
+ res[i] = arr2[i];
+ }
+ }
+ return new m1.constructor(res);
+};
+
+
+/**
+ * Tests whether two messages are equal.
+ * @param {jspb.Message|undefined} m1 The first message object.
+ * @param {jspb.Message|undefined} m2 The second message object.
+ * @return {boolean} true if both messages are null/undefined, or if both are
+ * of the same type and have the same field values.
+ */
+jspb.Message.equals = function(m1, m2) {
+ return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) &&
+ jspb.Message.compareFields(m1.toArray(), m2.toArray()));
+};
+
+
+/**
+ * Compares two message fields recursively.
+ * @param {*} field1 The first field.
+ * @param {*} field2 The second field.
+ * @return {boolean} true if the fields are null/undefined, or otherwise equal.
+ */
+jspb.Message.compareFields = function(field1, field2) {
+ if (goog.isObject(field1) && goog.isObject(field2)) {
+ var keys = {}, name, extensionObject1, extensionObject2;
+ for (name in field1) {
+ field1.hasOwnProperty(name) && (keys[name] = 0);
+ }
+ for (name in field2) {
+ field2.hasOwnProperty(name) && (keys[name] = 0);
+ }
+ for (name in keys) {
+ var val1 = field1[name], val2 = field2[name];
+ if (goog.isObject(val1) && !goog.isArray(val1)) {
+ if (extensionObject1 !== undefined) {
+ throw new Error('invalid jspb state');
+ }
+ extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1;
+ val1 = undefined;
+ }
+ if (goog.isObject(val2) && !goog.isArray(val2)) {
+ if (extensionObject2 !== undefined) {
+ throw new Error('invalid jspb state');
+ }
+ extensionObject2 = goog.object.isEmpty(val2) ? undefined : val2;
+ val2 = undefined;
+ }
+ if (!jspb.Message.compareFields(val1, val2)) {
+ return false;
+ }
+ }
+ if (extensionObject1 || extensionObject2) {
+ return jspb.Message.compareFields(extensionObject1, extensionObject2);
+ }
+ return true;
+ }
+ // Primitive fields, null and undefined compare as equal.
+ // This also forces booleans and 0/1 to compare as equal to ensure
+ // compatibility with the jspb serializer.
+ return field1 == field2;
+};
+
+
+/**
+ * Static clone function. NOTE: A type-safe method called "cloneMessage" exists
+ * on each generated JsPb class. Do not call this function directly.
+ * @param {!jspb.Message} msg A message to clone.
+ * @return {!jspb.Message} A deep clone of the given message.
+ */
+jspb.Message.clone = function(msg) {
+ // Although we could include the wrappers, we leave them out here.
+ return jspb.Message.cloneMessage(msg);
+};
+
+
+/**
+ * @param {!jspb.Message} msg A message to clone.
+ * @return {!jspb.Message} A deep clone of the given message.
+ * @protected
+ */
+jspb.Message.cloneMessage = function(msg) {
+ // Although we could include the wrappers, we leave them out here.
+ return new msg.constructor(jspb.Message.clone_(msg.toArray()));
+};
+
+
+/**
+ * Takes 2 messages of the same type and copies the contents of the first
+ * message into the second. After this the 2 messages will equals in terms of
+ * value semantics but share no state. All data in the destination message will
+ * be overridden.
+ *
+ * @param {MESSAGE} fromMessage Message that will be copied into toMessage.
+ * @param {MESSAGE} toMessage Message which will receive a copy of fromMessage
+ * as its contents.
+ * @template MESSAGE
+ */
+jspb.Message.copyInto = function(fromMessage, toMessage) {
+ goog.asserts.assertInstanceof(fromMessage, jspb.Message);
+ goog.asserts.assertInstanceof(toMessage, jspb.Message);
+ goog.asserts.assert(fromMessage.constructor == toMessage.constructor,
+ 'Copy source and target message should have the same type.');
+ var copyOfFrom = jspb.Message.clone(fromMessage);
+
+ var to = toMessage.toArray();
+ var from = copyOfFrom.toArray();
+
+ // Empty destination in case it has more values at the end of the array.
+ to.length = 0;
+ // and then copy everything from the new to the existing message.
+ for (var i = 0; i < from.length; i++) {
+ to[i] = from[i];
+ }
+
+ // This is either null or empty for a fresh copy.
+ toMessage.wrappers_ = copyOfFrom.wrappers_;
+ // Just a reference into the shared array.
+ toMessage.extensionObject_ = copyOfFrom.extensionObject_;
+};
+
+
+/**
+ * Helper for cloning an internal JsPb object.
+ * @param {!Object} obj A JsPb object, eg, a field, to be cloned.
+ * @return {!Object} A clone of the input object.
+ * @private
+ */
+jspb.Message.clone_ = function(obj) {
+ var o;
+ if (goog.isArray(obj)) {
+ // Allocate array of correct size.
+ var clonedArray = new Array(obj.length);
+ // Use array iteration where possible because it is faster than for-in.
+ for (var i = 0; i < obj.length; i++) {
+ if ((o = obj[i]) != null) {
+ clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o;
+ }
+ }
+ return clonedArray;
+ }
+ var clone = {};
+ for (var key in obj) {
+ if ((o = obj[key]) != null) {
+ clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o;
+ }
+ }
+ return clone;
+};
+
+
+/**
+ * Registers a JsPb message type id with its constructor.
+ * @param {string} id The id for this type of message.
+ * @param {Function} constructor The message constructor.
+ */
+jspb.Message.registerMessageType = function(id, constructor) {
+ jspb.Message.registry_[id] = constructor;
+ // This is needed so we can later access messageId directly on the contructor,
+ // otherwise it is not available due to 'property collapsing' by the compiler.
+ constructor.messageId = id;
+};
+
+
+/**
+ * The registry of message ids to message constructors.
+ * @private
+ */
+jspb.Message.registry_ = {};
+
+
+/**
+ * The extensions registered on MessageSet. This is a map of extension
+ * field number to field info object. This should be considered as a
+ * private API.
+ *
+ * This is similar to [jspb class name].extensions object for
+ * non-MessageSet. We special case MessageSet so that we do not need
+ * to goog.require MessageSet from classes that extends MessageSet.
+ *
+ * @type {!Object.<number, jspb.ExtensionFieldInfo>}
+ */
+jspb.Message.messageSetExtensions = {};
diff --git a/js/message_test.js b/js/message_test.js
new file mode 100644
index 00000000..971ea4f4
--- /dev/null
+++ b/js/message_test.js
@@ -0,0 +1,974 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test suite is written using Jasmine -- see http://jasmine.github.io/
+
+goog.setTestOnly();
+
+goog.require('goog.json');
+goog.require('goog.testing.asserts');
+goog.require('jspb.Message');
+goog.require('proto.jspb.exttest.beta.floatingStrField');
+goog.require('proto.jspb.exttest.floatingMsgField');
+goog.require('proto.jspb.exttest.floatingMsgFieldTwo');
+goog.require('proto.jspb.test.CloneExtension');
+goog.require('proto.jspb.test.Complex');
+goog.require('proto.jspb.test.DefaultValues');
+goog.require('proto.jspb.test.Empty');
+goog.require('proto.jspb.test.EnumContainer');
+goog.require('proto.jspb.test.ExtensionMessage');
+goog.require('proto.jspb.test.floatingMsgField');
+goog.require('proto.jspb.test.floatingStrField');
+goog.require('proto.jspb.test.HasExtensions');
+goog.require('proto.jspb.test.IndirectExtension');
+goog.require('proto.jspb.test.IsExtension');
+goog.require('proto.jspb.test.OptionalFields');
+goog.require('proto.jspb.test.OuterEnum');
+goog.require('proto.jspb.test.simple1');
+goog.require('proto.jspb.test.Simple1');
+goog.require('proto.jspb.test.Simple2');
+goog.require('proto.jspb.test.SpecialCases');
+goog.require('proto.jspb.test.TestClone');
+goog.require('proto.jspb.test.TestExtensionsMessage');
+goog.require('proto.jspb.test.TestGroup');
+goog.require('proto.jspb.test.TestGroup1');
+goog.require('proto.jspb.test.TestMessageWithOneof');
+goog.require('proto.jspb.test.TestReservedNames');
+goog.require('proto.jspb.test.TestReservedNamesExtension');
+
+
+
+
+describe('Message test suite', function() {
+ it('testEmptyProto', function() {
+ var empty1 = new proto.jspb.test.Empty([]);
+ var empty2 = new proto.jspb.test.Empty([]);
+ assertObjectEquals({}, empty1.toObject());
+ assertObjectEquals('Message should not be corrupted:', empty2, empty1);
+ });
+
+ it('testTopLevelEnum', function() {
+ var response = new proto.jspb.test.EnumContainer([]);
+ response.setOuterEnum(proto.jspb.test.OuterEnum.FOO);
+ assertEquals(proto.jspb.test.OuterEnum.FOO, response.getOuterEnum());
+ });
+
+ it('testByteStrings', function() {
+ var data = new proto.jspb.test.DefaultValues([]);
+ data.setBytesField('some_bytes');
+ assertEquals('some_bytes', data.getBytesField());
+ });
+
+ it('testComplexConversion', function() {
+ var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
+ var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
+ var foo = new proto.jspb.test.Complex(data1);
+ var bar = new proto.jspb.test.Complex(data2);
+ var result = foo.toObject();
+ assertObjectEquals({
+ aString: 'a',
+ anOutOfOrderBool: 1,
+ aNestedMessage: {
+ anInt: 11
+ },
+ aRepeatedMessageList: [{anInt: 22}, {anInt: 33}],
+ aRepeatedStringList: ['s1', 's2']
+ }, result);
+
+ // Now test with the jspb instances included.
+ result = foo.toObject(true /* opt_includeInstance */);
+ assertObjectEquals({
+ aString: 'a',
+ anOutOfOrderBool: 1,
+ aNestedMessage: {
+ anInt: 11,
+ $jspbMessageInstance: foo.getANestedMessage()
+ },
+ aRepeatedMessageList: [
+ {anInt: 22, $jspbMessageInstance: foo.getARepeatedMessageList()[0]},
+ {anInt: 33, $jspbMessageInstance: foo.getARepeatedMessageList()[1]}
+ ],
+ aRepeatedStringList: ['s1', 's2'],
+ $jspbMessageInstance: foo
+ }, result);
+
+ });
+
+ it('testMissingFields', function() {
+ var foo = new proto.jspb.test.Complex([
+ undefined, undefined, undefined, [],
+ undefined, undefined, undefined, undefined]);
+ var bar = new proto.jspb.test.Complex([
+ undefined, undefined, undefined, [],
+ undefined, undefined, undefined, undefined]);
+ var result = foo.toObject();
+ assertObjectEquals({
+ aString: undefined,
+ anOutOfOrderBool: undefined,
+ aNestedMessage: {
+ anInt: undefined
+ },
+ // Note: JsPb converts undefined repeated fields to empty arrays.
+ aRepeatedMessageList: [],
+ aRepeatedStringList: []
+ }, result);
+
+ });
+
+ it('testSpecialCases', function() {
+ // Note: Some property names are reserved in JavaScript.
+ // These names are converted to the Js property named pb_<reserved_name>.
+ var special =
+ new proto.jspb.test.SpecialCases(['normal', 'default', 'function',
+ 'var']);
+ var result = special.toObject();
+ assertObjectEquals({
+ normal: 'normal',
+ pb_default: 'default',
+ pb_function: 'function',
+ pb_var: 'var'
+ }, result);
+ });
+
+ it('testDefaultValues', function() {
+ var defaultString = "default<>\'\"abc";
+ var response = new proto.jspb.test.DefaultValues();
+
+ // Test toObject
+ var expectedObject = {
+ stringField: defaultString,
+ boolField: true,
+ intField: 11,
+ enumField: 13,
+ emptyField: '',
+ bytesField: 'bW9v'
+ };
+ assertObjectEquals(expectedObject, response.toObject());
+
+
+ // Test getters
+ response = new proto.jspb.test.DefaultValues();
+ assertEquals(defaultString, response.getStringField());
+ assertEquals(true, response.getBoolField());
+ assertEquals(11, response.getIntField());
+ assertEquals(13, response.getEnumField());
+ assertEquals('', response.getEmptyField());
+ assertEquals('bW9v', response.getBytesField());
+
+ function makeDefault(values) {
+ return new proto.jspb.test.DefaultValues(values);
+ }
+
+ // Test with undefined values,
+ // Use push to workaround IE treating undefined array elements as holes.
+ response = makeDefault([undefined, undefined, undefined, undefined]);
+ assertEquals(defaultString, response.getStringField());
+ assertEquals(true, response.getBoolField());
+ assertEquals(11, response.getIntField());
+ assertEquals(13, response.getEnumField());
+
+ // Test with null values, as would be returned by a JSON serializer.
+ response = makeDefault([null, null, null, null]);
+ assertEquals(defaultString, response.getStringField());
+ assertEquals(true, response.getBoolField());
+ assertEquals(11, response.getIntField());
+ assertEquals(13, response.getEnumField());
+
+ // Test with false-like values.
+ response = makeDefault(['', false, 0, 0]);
+ assertEquals('', response.getStringField());
+ assertEquals(false, response.getBoolField());
+ assertEquals(true, response.getIntField() == 0);
+ assertEquals(true, response.getEnumField() == 0);
+
+ // Test that clearing the values reverts them to the default state.
+ response = makeDefault(['blah', false, 111, 77]);
+ response.clearStringField(); response.clearBoolField();
+ response.clearIntField(); response.clearEnumField();
+ assertEquals(defaultString, response.getStringField());
+ assertEquals(true, response.getBoolField());
+ assertEquals(11, response.getIntField());
+ assertEquals(13, response.getEnumField());
+
+ // Test that setFoo(null) clears the values.
+ response = makeDefault(['blah', false, 111, 77]);
+ response.setStringField(null); response.setBoolField(null);
+ response.setIntField(undefined); response.setEnumField(undefined);
+ assertEquals(defaultString, response.getStringField());
+ assertEquals(true, response.getBoolField());
+ assertEquals(11, response.getIntField());
+ assertEquals(13, response.getEnumField());
+ });
+
+ it('testMessageRegistration', function() {
+ // goog.require(SomeResponse) will include its library, which will in
+ // turn add SomeResponse to the message registry.
+ assertEquals(jspb.Message.registry_['res'], proto.jspb.test.SomeResponse);
+ });
+
+ it('testClearFields', function() {
+ // We don't set 'proper' defaults, rather, bools, strings,
+ // etc, are cleared to undefined or null and take on the Javascript
+ // meaning for that value. Repeated fields are set to [] when cleared.
+ var data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
+ var foo = new proto.jspb.test.OptionalFields(data);
+ foo.clearAString();
+ foo.clearABool();
+ foo.clearANestedMessage();
+ foo.clearARepeatedMessageList();
+ foo.clearARepeatedStringList();
+ assertUndefined(foo.getAString());
+ assertUndefined(foo.getABool());
+ assertUndefined(foo.getANestedMessage());
+ assertObjectEquals([], foo.getARepeatedMessageList());
+ assertObjectEquals([], foo.getARepeatedStringList());
+ // NOTE: We want the missing fields in 'expected' to be undefined,
+ // but we actually get a sparse array instead. We could use something
+ // like [1,undefined,2] to avoid this, except that this is still
+ // sparse on IE. No comment...
+ var expected = [,,, [], []];
+ expected[0] = expected[1] = expected[2] = undefined;
+ assertObjectEquals(expected, foo.toArray());
+
+ // Test set(null). We could deprecated this in favor of clear(), but
+ // it's also convenient to have.
+ data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
+ foo = new proto.jspb.test.OptionalFields(data);
+ foo.setAString(null);
+ foo.setABool(null);
+ foo.setANestedMessage(null);
+ foo.setARepeatedMessageList(null);
+ foo.setARepeatedStringList(null);
+ assertNull(foo.getAString());
+ assertNull(foo.getABool());
+ assertNull(foo.getANestedMessage());
+ assertObjectEquals([], foo.getARepeatedMessageList());
+ assertObjectEquals([], foo.getARepeatedStringList());
+ assertObjectEquals([null, null, null, [], []], foo.toArray());
+
+ // Test set(undefined). Again, not something we really need, and not
+ // supported directly by our typing, but it should 'do the right thing'.
+ data = ['str', true, [11], [[22], [33]], ['s1', 's2']];
+ foo = new proto.jspb.test.OptionalFields(data);
+ foo.setAString(undefined);
+ foo.setABool(undefined);
+ foo.setANestedMessage(undefined);
+ foo.setARepeatedMessageList(undefined);
+ foo.setARepeatedStringList(undefined);
+ assertUndefined(foo.getAString());
+ assertUndefined(foo.getABool());
+ assertUndefined(foo.getANestedMessage());
+ assertObjectEquals([], foo.getARepeatedMessageList());
+ assertObjectEquals([], foo.getARepeatedStringList());
+ expected = [,,, [], []];
+ expected[0] = expected[1] = expected[2] = undefined;
+ assertObjectEquals(expected, foo.toArray());
+ });
+
+ it('testDifferenceRawObject', function() {
+ var p1 = new proto.jspb.test.HasExtensions(['hi', 'diff', {}]);
+ var p2 = new proto.jspb.test.HasExtensions(['hi', 'what',
+ {1000: 'unique'}]);
+ var diff = /** @type {proto.jspb.test.HasExtensions} */
+ (jspb.Message.difference(p1, p2));
+ assertUndefined(diff.getStr1());
+ assertEquals('what', diff.getStr2());
+ assertUndefined(diff.getStr3());
+ assertEquals('unique', diff.extensionObject_[1000]);
+ });
+
+ it('testEqualsSimple', function() {
+ var s1 = new proto.jspb.test.Simple1(['hi']);
+ assertTrue(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['hi'])));
+ assertFalse(jspb.Message.equals(s1, new proto.jspb.test.Simple1(['bye'])));
+ var s1b = new proto.jspb.test.Simple1(['hi', ['hello']]);
+ assertTrue(jspb.Message.equals(s1b,
+ new proto.jspb.test.Simple1(['hi', ['hello']])));
+ assertTrue(jspb.Message.equals(s1b,
+ new proto.jspb.test.Simple1(['hi', ['hello', undefined,
+ undefined, undefined]])));
+ assertFalse(jspb.Message.equals(s1b,
+ new proto.jspb.test.Simple1(['no', ['hello']])));
+ // Test with messages of different types
+ var s2 = new proto.jspb.test.Simple2(['hi']);
+ assertFalse(jspb.Message.equals(s1, s2));
+ });
+
+ it('testEquals_softComparison', function() {
+ var s1 = new proto.jspb.test.Simple1(['hi', [], null]);
+ assertTrue(jspb.Message.equals(s1,
+ new proto.jspb.test.Simple1(['hi', []])));
+
+ var s1b = new proto.jspb.test.Simple1(['hi', [], true]);
+ assertTrue(jspb.Message.equals(s1b,
+ new proto.jspb.test.Simple1(['hi', [], 1])));
+ });
+
+ it('testEqualsComplex', function() {
+ var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
+ var data2 = ['a',,, [, 11], [[, 22], [, 34]],, ['s1', 's2'],, 1];
+ var data3 = ['a',,, [, 11], [[, 22]],, ['s1', 's2'],, 1];
+ var data4 = ['hi'];
+ var c1a = new proto.jspb.test.Complex(data1);
+ var c1b = new proto.jspb.test.Complex(data1);
+ var c2 = new proto.jspb.test.Complex(data2);
+ var c3 = new proto.jspb.test.Complex(data3);
+ var s1 = new proto.jspb.test.Simple1(data4);
+
+ assertTrue(jspb.Message.equals(c1a, c1b));
+ assertFalse(jspb.Message.equals(c1a, c2));
+ assertFalse(jspb.Message.equals(c2, c3));
+ assertFalse(jspb.Message.equals(c1a, s1));
+ });
+
+ it('testEqualsExtensionsConstructed', function() {
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions([]),
+ new proto.jspb.test.HasExtensions([{}])
+ ));
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}])
+ ));
+ assertFalse(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'b'}]}])
+ ));
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}])
+ ));
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions([,,, {100: [{200: 'a'}]}])
+ ));
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions([,,, {100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions([{100: [{200: 'a'}]}])
+ ));
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions(['hi',,, {100: [{200: 'a'}]}])
+ ));
+ assertTrue(jspb.Message.equals(
+ new proto.jspb.test.HasExtensions(['hi',,, {100: [{200: 'a'}]}]),
+ new proto.jspb.test.HasExtensions(['hi', {100: [{200: 'a'}]}])
+ ));
+ });
+
+ it('testEqualsExtensionsUnconstructed', function() {
+ assertTrue(jspb.Message.compareFields([], [{}]));
+ assertTrue(jspb.Message.compareFields([,,, {}], []));
+ assertTrue(jspb.Message.compareFields([,,, {}], [,, {}]));
+ assertTrue(jspb.Message.compareFields(
+ ['hi', {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}]));
+ assertFalse(jspb.Message.compareFields(
+ ['hi', {100: [{200: 'a'}]}], ['hi', {100: [{200: 'b'}]}]));
+ assertTrue(jspb.Message.compareFields(
+ [{100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
+ assertTrue(jspb.Message.compareFields(
+ [{100: [{200: 'a'}]}], [,,, {100: [{200: 'a'}]}]));
+ assertTrue(jspb.Message.compareFields(
+ [,,, {100: [{200: 'a'}]}], [{100: [{200: 'a'}]}]));
+ assertTrue(jspb.Message.compareFields(
+ ['hi', {100: [{200: 'a'}]}], ['hi',,, {100: [{200: 'a'}]}]));
+ assertTrue(jspb.Message.compareFields(
+ ['hi',,, {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}]));
+ });
+
+ it('testToMap', function() {
+ var p1 = new proto.jspb.test.Simple1(['k', ['v']]);
+ var p2 = new proto.jspb.test.Simple1(['k1', ['v1', 'v2']]);
+ var soymap = jspb.Message.toMap([p1, p2],
+ proto.jspb.test.Simple1.prototype.getAString,
+ proto.jspb.test.Simple1.prototype.toObject);
+ assertEquals('k', soymap['k'].aString);
+ assertArrayEquals(['v'], soymap['k'].aRepeatedStringList);
+ var protomap = jspb.Message.toMap([p1, p2],
+ proto.jspb.test.Simple1.prototype.getAString);
+ assertEquals('k', protomap['k'].getAString());
+ assertArrayEquals(['v'], protomap['k'].getARepeatedStringList());
+ });
+
+ it('testClone', function() {
+ var original = new proto.jspb.test.TestClone();
+ original.setStr('v1');
+ var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]);
+ var simple2 = new proto.jspb.test.Simple1(['x2', ['y2', 'z2']]);
+ var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]);
+ original.setSimple1(simple1);
+ original.setSimple2List([simple2, simple3]);
+ var extension = new proto.jspb.test.CloneExtension();
+ extension.setExt('e1');
+ original.setExtension(proto.jspb.test.IsExtension.extField, extension);
+ var clone = original.cloneMessage();
+ assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
+ [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]],,, { 100: [, 'e1'] }],
+ clone.toArray());
+ clone.setStr('v2');
+ var simple4 = new proto.jspb.test.Simple1(['a1', ['b1', 'c1']]);
+ var simple5 = new proto.jspb.test.Simple1(['a2', ['b2', 'c2']]);
+ var simple6 = new proto.jspb.test.Simple1(['a3', ['b3', 'c3']]);
+ clone.setSimple1(simple4);
+ clone.setSimple2List([simple5, simple6]);
+ var newExtension = new proto.jspb.test.CloneExtension();
+ newExtension.setExt('e2');
+ clone.setExtension(proto.jspb.test.CloneExtension.extField, newExtension);
+ assertArrayEquals(['v2',, ['a1', ['b1', 'c1']],,
+ [['a2', ['b2', 'c2']], ['a3', ['b3', 'c3']]],,, { 100: [, 'e2'] }],
+ clone.toArray());
+ assertArrayEquals(['v1',, ['x1', ['y1', 'z1']],,
+ [['x2', ['y2', 'z2']], ['x3', ['y3', 'z3']]],,, { 100: [, 'e1'] }],
+ original.toArray());
+ });
+
+ it('testCopyInto', function() {
+ var original = new proto.jspb.test.TestClone();
+ original.setStr('v1');
+ var dest = new proto.jspb.test.TestClone();
+ dest.setStr('override');
+ var simple1 = new proto.jspb.test.Simple1(['x1', ['y1', 'z1']]);
+ var simple2 = new proto.jspb.test.Simple1(['x2', ['y2', 'z2']]);
+ var simple3 = new proto.jspb.test.Simple1(['x3', ['y3', 'z3']]);
+ var destSimple1 = new proto.jspb.test.Simple1(['ox1', ['oy1', 'oz1']]);
+ var destSimple2 = new proto.jspb.test.Simple1(['ox2', ['oy2', 'oz2']]);
+ var destSimple3 = new proto.jspb.test.Simple1(['ox3', ['oy3', 'oz3']]);
+ original.setSimple1(simple1);
+ original.setSimple2List([simple2, simple3]);
+ dest.setSimple1(destSimple1);
+ dest.setSimple2List([destSimple2, destSimple3]);
+ var extension = new proto.jspb.test.CloneExtension();
+ extension.setExt('e1');
+ original.setExtension(proto.jspb.test.CloneExtension.extField, extension);
+
+ jspb.Message.copyInto(original, dest);
+ assertArrayEquals(original.toArray(), dest.toArray());
+ assertEquals('x1', dest.getSimple1().getAString());
+ assertEquals('e1',
+ dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt());
+ dest.getSimple1().setAString('new value');
+ assertNotEquals(dest.getSimple1().getAString(),
+ original.getSimple1().getAString());
+ dest.getExtension(proto.jspb.test.CloneExtension.extField).
+ setExt('new value');
+ assertNotEquals(
+ dest.getExtension(proto.jspb.test.CloneExtension.extField).getExt(),
+ original.getExtension(
+ proto.jspb.test.CloneExtension.extField).getExt());
+ });
+
+ it('testCopyInto_notSameType', function() {
+ var a = new proto.jspb.test.TestClone();
+ var b = new proto.jspb.test.Simple1(['str', ['s1', 's2']]);
+
+ var e = assertThrows(function() {
+ jspb.Message.copyInto(a, b);
+ });
+ assertContains('should have the same type', e.message);
+ });
+
+ it('testExtensions', function() {
+ var extension1 = new proto.jspb.test.IsExtension(['ext1field']);
+ var extension2 = new proto.jspb.test.Simple1(['str', ['s1', 's2']]);
+ var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']);
+ extendable.setExtension(proto.jspb.test.IsExtension.extField, extension1);
+ extendable.setExtension(proto.jspb.test.IndirectExtension.simple,
+ extension2);
+ extendable.setExtension(proto.jspb.test.IndirectExtension.str, 'xyzzy');
+ extendable.setExtension(proto.jspb.test.IndirectExtension.repeatedStrList,
+ ['a', 'b']);
+ var s1 = new proto.jspb.test.Simple1(['foo', ['s1', 's2']]);
+ var s2 = new proto.jspb.test.Simple1(['bar', ['t1', 't2']]);
+ extendable.setExtension(
+ proto.jspb.test.IndirectExtension.repeatedSimpleList,
+ [s1, s2]);
+ assertObjectEquals(extension1,
+ extendable.getExtension(proto.jspb.test.IsExtension.extField));
+ assertObjectEquals(extension2,
+ extendable.getExtension(proto.jspb.test.IndirectExtension.simple));
+ assertObjectEquals('xyzzy',
+ extendable.getExtension(proto.jspb.test.IndirectExtension.str));
+ assertObjectEquals(['a', 'b'], extendable.getExtension(
+ proto.jspb.test.IndirectExtension.repeatedStrList));
+ assertObjectEquals([s1, s2], extendable.getExtension(
+ proto.jspb.test.IndirectExtension.repeatedSimpleList));
+ // Not supported yet, but it should work...
+ extendable.setExtension(proto.jspb.test.IndirectExtension.simple, null);
+ assertNull(
+ extendable.getExtension(proto.jspb.test.IndirectExtension.simple));
+ extendable.setExtension(proto.jspb.test.IndirectExtension.str, null);
+ assertNull(extendable.getExtension(proto.jspb.test.IndirectExtension.str));
+
+ // These assertions will only work properly in uncompiled mode.
+ // Extension fields defined on proto2 Descriptor messages are filtered out.
+
+ // TODO(haberman): codegen changes to properly ignore descriptor.proto
+ // extensions need to be merged from google3.
+ // assertUndefined(proto.jspb.test.IsExtension['simpleOption']);
+
+ // Extension fields with jspb.ignore = true are ignored.
+ assertUndefined(proto.jspb.test.IndirectExtension['ignored']);
+ assertUndefined(proto.jspb.test.HasExtensions['ignoredFloating']);
+ });
+
+ it('testFloatingExtensions', function() {
+ // From an autogenerated container.
+ var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']);
+ var extension = new proto.jspb.test.Simple1(['foo', ['s1', 's2']]);
+ extendable.setExtension(proto.jspb.test.simple1, extension);
+ assertObjectEquals(extension,
+ extendable.getExtension(proto.jspb.test.simple1));
+
+ // From _lib mode.
+ extension = new proto.jspb.test.ExtensionMessage(['s1']);
+ extendable = new proto.jspb.test.TestExtensionsMessage([16]);
+ extendable.setExtension(proto.jspb.test.floatingMsgField, extension);
+ extendable.setExtension(proto.jspb.test.floatingStrField, 's2');
+ assertObjectEquals(extension,
+ extendable.getExtension(proto.jspb.test.floatingMsgField));
+ assertObjectEquals('s2',
+ extendable.getExtension(proto.jspb.test.floatingStrField));
+ assertNotUndefined(proto.jspb.exttest.floatingMsgField);
+ assertNotUndefined(proto.jspb.exttest.floatingMsgFieldTwo);
+ assertNotUndefined(proto.jspb.exttest.beta.floatingStrField);
+ });
+
+ it('testToObject_extendedObject', function() {
+ var extension1 = new proto.jspb.test.IsExtension(['ext1field']);
+ var extension2 = new proto.jspb.test.Simple1(['str', ['s1', 's2'], true]);
+ var extendable = new proto.jspb.test.HasExtensions(['v1', 'v2', 'v3']);
+ extendable.setExtension(proto.jspb.test.IsExtension.extField, extension1);
+ extendable.setExtension(proto.jspb.test.IndirectExtension.simple,
+ extension2);
+ extendable.setExtension(proto.jspb.test.IndirectExtension.str, 'xyzzy');
+ extendable.setExtension(proto.jspb.test.IndirectExtension.repeatedStrList,
+ ['a', 'b']);
+ var s1 = new proto.jspb.test.Simple1(['foo', ['s1', 's2'], true]);
+ var s2 = new proto.jspb.test.Simple1(['bar', ['t1', 't2'], false]);
+ extendable.setExtension(
+ proto.jspb.test.IndirectExtension.repeatedSimpleList,
+ [s1, s2]);
+ assertObjectEquals({
+ str1: 'v1', str2: 'v2', str3: 'v3',
+ extField: { ext1: 'ext1field' },
+ simple: {
+ aString: 'str', aRepeatedStringList: ['s1', 's2'], aBoolean: true
+ },
+ str: 'xyzzy',
+ repeatedStrList: ['a', 'b'],
+ repeatedSimpleList: [
+ { aString: 'foo', aRepeatedStringList: ['s1', 's2'], aBoolean: true},
+ { aString: 'bar', aRepeatedStringList: ['t1', 't2'], aBoolean: false}
+ ]
+ }, extendable.toObject());
+
+ // Now, with instances included.
+ assertObjectEquals({
+ str1: 'v1', str2: 'v2', str3: 'v3',
+ extField: {
+ ext1: 'ext1field',
+ $jspbMessageInstance:
+ extendable.getExtension(proto.jspb.test.IsExtension.extField)
+ },
+ simple: {
+ aString: 'str',
+ aRepeatedStringList: ['s1', 's2'],
+ aBoolean: true,
+ $jspbMessageInstance:
+ extendable.getExtension(proto.jspb.test.IndirectExtension.simple)
+ },
+ str: 'xyzzy',
+ repeatedStrList: ['a', 'b'],
+ repeatedSimpleList: [{
+ aString: 'foo',
+ aRepeatedStringList: ['s1', 's2'],
+ aBoolean: true,
+ $jspbMessageInstance: s1
+ }, {
+ aString: 'bar',
+ aRepeatedStringList: ['t1', 't2'],
+ aBoolean: false,
+ $jspbMessageInstance: s2
+ }],
+ $jspbMessageInstance: extendable
+ }, extendable.toObject(true /* opt_includeInstance */));
+ });
+
+ it('testInitialization_emptyArray', function() {
+ var msg = new proto.jspb.test.HasExtensions([]);
+ if (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) {
+ assertArrayEquals([], msg.toArray());
+ } else {
+ // Extension object is created past all regular fields.
+ assertArrayEquals([,,, {}], msg.toArray());
+ }
+ });
+
+ it('testInitialization_justExtensionObject', function() {
+ var msg = new proto.jspb.test.Empty([{1: 'hi'}]);
+ // The extensionObject is not moved from its original location.
+ assertArrayEquals([{1: 'hi'}], msg.toArray());
+ });
+
+ it('testInitialization_incompleteList', function() {
+ var msg = new proto.jspb.test.Empty([1, {4: 'hi'}]);
+ // The extensionObject is not moved from its original location.
+ assertArrayEquals([1, {4: 'hi'}], msg.toArray());
+ });
+
+ it('testInitialization_forwardCompatible', function() {
+ var msg = new proto.jspb.test.Empty([1, 2, 3, {1: 'hi'}]);
+ assertArrayEquals([1, 2, 3, {1: 'hi'}], msg.toArray());
+ });
+
+ it('testExtendedMessageEnsureObject', function() {
+ var data = new proto.jspb.test.HasExtensions(['str1',
+ {'a_key': 'an_object'}]);
+ assertEquals('an_object', data.extensionObject_['a_key']);
+ });
+
+ it('testToObject_hasExtensionField', function() {
+ var data = new proto.jspb.test.HasExtensions(['str1', {100: ['ext1']}]);
+ var obj = data.toObject();
+ assertEquals('str1', obj.str1);
+ assertEquals('ext1', obj.extField.ext1);
+ });
+
+ it('testGetExtension', function() {
+ var data = new proto.jspb.test.HasExtensions(['str1', {100: ['ext1']}]);
+ assertEquals('str1', data.getStr1());
+ var extension = data.getExtension(proto.jspb.test.IsExtension.extField);
+ assertNotNull(extension);
+ assertEquals('ext1', extension.getExt1());
+ });
+
+ it('testSetExtension', function() {
+ var data = new proto.jspb.test.HasExtensions();
+ var extensionMessage = new proto.jspb.test.IsExtension(['is_extension']);
+ data.setExtension(proto.jspb.test.IsExtension.extField, extensionMessage);
+ var obj = data.toObject();
+ assertNotNull(
+ data.getExtension(proto.jspb.test.IsExtension.extField));
+ assertEquals('is_extension', obj.extField.ext1);
+ });
+
+ /**
+ * Note that group is long deprecated, we only support it because JsPb has
+ * a goal of being able to generate JS classes for all proto descriptors.
+ */
+ it('testGroups', function() {
+ var group = new proto.jspb.test.TestGroup();
+ var someGroup = new proto.jspb.test.TestGroup.RepeatedGroup();
+ someGroup.setId('g1');
+ someGroup.setSomeBoolList([true, false]);
+ group.setRepeatedGroupList([someGroup]);
+ var groups = group.getRepeatedGroupList();
+ assertEquals('g1', groups[0].getId());
+ assertObjectEquals([true, false], groups[0].getSomeBoolList());
+ assertObjectEquals({id: 'g1', someBoolList: [true, false]},
+ groups[0].toObject());
+ assertObjectEquals({
+ repeatedGroupList: [{id: 'g1', someBoolList: [true, false]}],
+ requiredGroup: {id: undefined},
+ optionalGroup: undefined,
+ requiredSimple: {aRepeatedStringList: [], aString: undefined},
+ optionalSimple: undefined,
+ id: undefined
+ }, group.toObject());
+ var group1 = new proto.jspb.test.TestGroup1();
+ group1.setGroup(someGroup);
+ assertEquals(someGroup, group1.getGroup());
+ });
+
+ it('testNonExtensionFieldsAfterExtensionRange', function() {
+ var data = [{'1': 'a_string'}];
+ var message = new proto.jspb.test.Complex(data);
+ assertArrayEquals([], message.getARepeatedStringList());
+ });
+
+ it('testReservedGetterNames', function() {
+ var message = new proto.jspb.test.TestReservedNames();
+ message.setExtension$(11);
+ message.setExtension(proto.jspb.test.TestReservedNamesExtension.foo, 12);
+ assertEquals(11, message.getExtension$());
+ assertEquals(12, message.getExtension(
+ proto.jspb.test.TestReservedNamesExtension.foo));
+ assertObjectEquals({extension: 11, foo: 12}, message.toObject());
+ });
+
+ it('testInitializeMessageWithUnsetOneof', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof([]);
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
+ PARTIAL_ONEOF_NOT_SET,
+ message.getPartialOneofCase());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
+ RECURSIVE_ONEOF_NOT_SET,
+ message.getRecursiveOneofCase());
+ });
+
+ it('testInitializeMessageWithSingleValueSetInOneof', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof([,, 'x']);
+
+ assertEquals('x', message.getPone());
+ assertUndefined(message.getPthree());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
+ message.getPartialOneofCase());
+ });
+
+ it('testKeepsLastWireValueSetInUnion_multipleValues', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof([,, 'x',, 'y']);
+
+ assertUndefined('x', message.getPone());
+ assertEquals('y', message.getPthree());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
+ message.getPartialOneofCase());
+ });
+
+ it('testSettingOneofFieldClearsOthers', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ assertUndefined(message.getPone());
+ assertUndefined(message.getPthree());
+
+ message.setPone('hi');
+ assertEquals('hi', message.getPone());
+ assertUndefined(message.getPthree());
+
+ message.setPthree('bye');
+ assertUndefined(message.getPone());
+ assertEquals('bye', message.getPthree());
+ });
+
+ it('testSettingOneofFieldDoesNotClearFieldsFromOtherUnions', function() {
+ var other = new proto.jspb.test.TestMessageWithOneof;
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ assertUndefined(message.getPone());
+ assertUndefined(message.getPthree());
+ assertUndefined(message.getRone());
+
+ message.setPone('hi');
+ message.setRone(other);
+ assertEquals('hi', message.getPone());
+ assertUndefined(message.getPthree());
+ assertEquals(other, message.getRone());
+
+ message.setPthree('bye');
+ assertUndefined(message.getPone());
+ assertEquals('bye', message.getPthree());
+ assertEquals(other, message.getRone());
+ });
+
+ it('testUnsetsOneofCaseWhenFieldIsCleared', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
+ PARTIAL_ONEOF_NOT_SET,
+ message.getPartialOneofCase());
+
+ message.setPone('hi');
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
+ message.getPartialOneofCase());
+
+ message.clearPone();
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.
+ PARTIAL_ONEOF_NOT_SET,
+ message.getPartialOneofCase());
+ });
+
+ it('testMessageWithDefaultOneofValues', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ assertEquals(1234, message.getAone());
+ assertUndefined(message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase
+ .DEFAULT_ONEOF_A_NOT_SET,
+ message.getDefaultOneofACase());
+
+ message.setAone(567);
+ assertEquals(567, message.getAone());
+ assertUndefined(message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE,
+ message.getDefaultOneofACase());
+
+ message.setAtwo(890);
+ assertEquals(1234, message.getAone());
+ assertEquals(890, message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
+ message.getDefaultOneofACase());
+
+ message.clearAtwo();
+ assertEquals(1234, message.getAone());
+ assertUndefined(message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase
+ .DEFAULT_ONEOF_A_NOT_SET,
+ message.getDefaultOneofACase());
+ });
+
+ it('testMessageWithDefaultOneofValues_defaultNotOnFirstField', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ assertUndefined(message.getBone());
+ assertEquals(1234, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase
+ .DEFAULT_ONEOF_B_NOT_SET,
+ message.getDefaultOneofBCase());
+
+ message.setBone(2);
+ assertEquals(2, message.getBone());
+ assertEquals(1234, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
+ message.getDefaultOneofBCase());
+
+ message.setBtwo(3);
+ assertUndefined(message.getBone());
+ assertEquals(3, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
+ message.getDefaultOneofBCase());
+
+ message.clearBtwo();
+ assertUndefined(message.getBone());
+ assertEquals(1234, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase
+ .DEFAULT_ONEOF_B_NOT_SET,
+ message.getDefaultOneofBCase());
+ });
+
+ it('testInitializeMessageWithOneofDefaults', function() {
+ var message =
+ new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567));
+ assertEquals(567, message.getAone());
+ assertUndefined(message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.AONE,
+ message.getDefaultOneofACase());
+
+ message =
+ new proto.jspb.test.TestMessageWithOneof(new Array(10).concat(890));
+ assertEquals(1234, message.getAone());
+ assertEquals(890, message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
+ message.getDefaultOneofACase());
+
+ message =
+ new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567,890));
+ assertEquals(1234, message.getAone());
+ assertEquals(890, message.getAtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofACase.ATWO,
+ message.getDefaultOneofACase());
+ });
+
+ it('testInitializeMessageWithOneofDefaults_defaultNotSetOnFirstField',
+ function() {
+ var message;
+
+ message =
+ new proto.jspb.test.TestMessageWithOneof(new Array(11).concat(567));
+ assertEquals(567, message.getBone());
+ assertEquals(1234, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BONE,
+ message.getDefaultOneofBCase());
+
+ message =
+ new proto.jspb.test.TestMessageWithOneof(new Array(12).concat(890));
+ assertUndefined(message.getBone());
+ assertEquals(890, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
+ message.getDefaultOneofBCase());
+
+ message = new proto.jspb.test.TestMessageWithOneof(
+ new Array(11).concat(567,890));
+ assertUndefined(message.getBone());
+ assertEquals(890, message.getBtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.DefaultOneofBCase.BTWO,
+ message.getDefaultOneofBCase());
+ });
+
+ it('testOneofContainingAnotherMessage', function() {
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.
+ RECURSIVE_ONEOF_NOT_SET,
+ message.getRecursiveOneofCase());
+
+ var other = new proto.jspb.test.TestMessageWithOneof;
+ message.setRone(other);
+ assertEquals(other, message.getRone());
+ assertUndefined(message.getRtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RONE,
+ message.getRecursiveOneofCase());
+
+ message.setRtwo('hi');
+ assertUndefined(message.getRone());
+ assertEquals('hi', message.getRtwo());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.RecursiveOneofCase.RTWO,
+ message.getRecursiveOneofCase());
+ });
+
+ it('testQueryingOneofCaseEnsuresOnlyOneFieldIsSetInUnderlyingArray',
+ function() {
+ var message = new proto.jspb.test.TestMessageWithOneof;
+ message.setPone('x');
+ assertEquals('x', message.getPone());
+ assertUndefined(message.getPthree());
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PONE,
+ message.getPartialOneofCase());
+
+ var array = message.toArray();
+ assertEquals('x', array[2]);
+ assertUndefined(array[4]);
+ array[4] = 'y';
+
+ assertEquals(
+ proto.jspb.test.TestMessageWithOneof.PartialOneofCase.PTHREE,
+ message.getPartialOneofCase());
+ assertUndefined(array[2]);
+ assertEquals('y', array[4]);
+ });
+
+});
diff --git a/js/node_loader.js b/js/node_loader.js
new file mode 100644
index 00000000..79211add
--- /dev/null
+++ b/js/node_loader.js
@@ -0,0 +1,49 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Loader that handles goog.require() for Node.JS.
+ */
+
+var oldLoader = goog.global.CLOSURE_IMPORT_SCRIPT;
+
+goog.global.CLOSURE_IMPORT_SCRIPT = function(src, opt_sourceText) {
+ if (opt_sourceText === undefined) {
+ try {
+ // Load from the current directory.
+ require("./" + src);
+ return true;
+ } catch (e) {
+ // Fall back to the Closure loader.
+ }
+ }
+
+ return oldLoader(src, opt_sourceText);
+};
diff --git a/js/package.json b/js/package.json
new file mode 100644
index 00000000..be93286f
--- /dev/null
+++ b/js/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "google-protobuf",
+ "version": "3.0.0-alpha.5",
+ "description": "Protocol Buffers for JavaScript",
+ "main": "debug.js",
+ "dependencies": {
+ "google-closure-library": "~20160125.0.0",
+ "gulp": "~3.9.0",
+ "jasmine": "~2.4.1"
+ },
+ "devDependencies": {},
+ "scripts": {
+ "test": "./node_modules/gulp/bin/gulp.js test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/google/protobuf/tree/master/js"
+ },
+ "author": "",
+ "license": "Apache-2.0"
+}
diff --git a/js/proto3_test.js b/js/proto3_test.js
new file mode 100644
index 00000000..8102bab6
--- /dev/null
+++ b/js/proto3_test.js
@@ -0,0 +1,279 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+goog.require('goog.testing.asserts');
+goog.require('proto.jspb.test.ForeignMessage');
+goog.require('proto.jspb.test.Proto3Enum');
+goog.require('proto.jspb.test.TestProto3');
+
+/**
+ * Helper: compare a bytes field to a string with codepoints 0--255.
+ * @param {Uint8Array|string} arr
+ * @param {string} str
+ * @return {boolean}
+ */
+function bytesCompare(arr, str) {
+ if (arr.length != str.length) {
+ return false;
+ }
+ if (typeof arr == 'string') {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr.charCodeAt(i) != str.charCodeAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] != str.charCodeAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+
+describe('proto3Test', function() {
+ /**
+ * Test defaults for proto3 message fields.
+ */
+ it('testProto3FieldDefaults', function() {
+ var msg = new proto.jspb.test.TestProto3();
+
+ assertEquals(msg.getOptionalInt32(), 0);
+ assertEquals(msg.getOptionalInt64(), 0);
+ assertEquals(msg.getOptionalUint32(), 0);
+ assertEquals(msg.getOptionalUint64(), 0);
+ assertEquals(msg.getOptionalSint32(), 0);
+ assertEquals(msg.getOptionalSint64(), 0);
+ assertEquals(msg.getOptionalFixed32(), 0);
+ assertEquals(msg.getOptionalFixed64(), 0);
+ assertEquals(msg.getOptionalSfixed32(), 0);
+ assertEquals(msg.getOptionalSfixed64(), 0);
+ assertEquals(msg.getOptionalFloat(), 0);
+ assertEquals(msg.getOptionalDouble(), 0);
+ assertEquals(msg.getOptionalString(), '');
+
+ // If/when we change bytes fields to return Uint8Array, we'll want to switch
+ // to this assertion instead:
+ //assertEquals(msg.getOptionalBytes() instanceof Uint8Array, true);
+ assertEquals(typeof msg.getOptionalBytes(), 'string');
+
+ assertEquals(msg.getOptionalBytes().length, 0);
+ assertEquals(msg.getOptionalForeignEnum(), proto.jspb.test.Proto3Enum.PROTO3_FOO);
+ assertEquals(msg.getOptionalForeignMessage(), undefined);
+ assertEquals(msg.getOptionalForeignMessage(), undefined);
+
+ assertEquals(msg.getRepeatedInt32List().length, 0);
+ assertEquals(msg.getRepeatedInt64List().length, 0);
+ assertEquals(msg.getRepeatedUint32List().length, 0);
+ assertEquals(msg.getRepeatedUint64List().length, 0);
+ assertEquals(msg.getRepeatedSint32List().length, 0);
+ assertEquals(msg.getRepeatedSint64List().length, 0);
+ assertEquals(msg.getRepeatedFixed32List().length, 0);
+ assertEquals(msg.getRepeatedFixed64List().length, 0);
+ assertEquals(msg.getRepeatedSfixed32List().length, 0);
+ assertEquals(msg.getRepeatedSfixed64List().length, 0);
+ assertEquals(msg.getRepeatedFloatList().length, 0);
+ assertEquals(msg.getRepeatedDoubleList().length, 0);
+ assertEquals(msg.getRepeatedStringList().length, 0);
+ assertEquals(msg.getRepeatedBytesList().length, 0);
+ assertEquals(msg.getRepeatedForeignEnumList().length, 0);
+ assertEquals(msg.getRepeatedForeignMessageList().length, 0);
+
+ });
+
+
+ /**
+ * Test that all fields can be set and read via a serialization roundtrip.
+ */
+ it('testProto3FieldSetGet', function() {
+ var msg = new proto.jspb.test.TestProto3();
+
+ msg.setOptionalInt32(-42);
+ msg.setOptionalInt64(-0x7fffffff00000000);
+ msg.setOptionalUint32(0x80000000);
+ msg.setOptionalUint64(0xf000000000000000);
+ msg.setOptionalSint32(-100);
+ msg.setOptionalSint64(-0x8000000000000000);
+ msg.setOptionalFixed32(1234);
+ msg.setOptionalFixed64(0x1234567800000000);
+ msg.setOptionalSfixed32(-1234);
+ msg.setOptionalSfixed64(-0x1234567800000000);
+ msg.setOptionalFloat(1.5);
+ msg.setOptionalDouble(-1.5);
+ msg.setOptionalBool(true);
+ msg.setOptionalString('hello world');
+ msg.setOptionalBytes('bytes');
+ var submsg = new proto.jspb.test.ForeignMessage();
+ submsg.setC(16);
+ msg.setOptionalForeignMessage(submsg);
+ msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_BAR);
+
+ msg.setRepeatedInt32List([-42]);
+ msg.setRepeatedInt64List([-0x7fffffff00000000]);
+ msg.setRepeatedUint32List([0x80000000]);
+ msg.setRepeatedUint64List([0xf000000000000000]);
+ msg.setRepeatedSint32List([-100]);
+ msg.setRepeatedSint64List([-0x8000000000000000]);
+ msg.setRepeatedFixed32List([1234]);
+ msg.setRepeatedFixed64List([0x1234567800000000]);
+ msg.setRepeatedSfixed32List([-1234]);
+ msg.setRepeatedSfixed64List([-0x1234567800000000]);
+ msg.setRepeatedFloatList([1.5]);
+ msg.setRepeatedDoubleList([-1.5]);
+ msg.setRepeatedBoolList([true]);
+ msg.setRepeatedStringList(['hello world']);
+ msg.setRepeatedBytesList(['bytes']);
+ submsg = new proto.jspb.test.ForeignMessage();
+ submsg.setC(1000);
+ msg.setRepeatedForeignMessageList([submsg]);
+ msg.setRepeatedForeignEnumList([proto.jspb.test.Proto3Enum.PROTO3_BAR]);
+
+ msg.setOneofString('asdf');
+
+ var serialized = msg.serializeBinary();
+ msg = proto.jspb.test.TestProto3.deserializeBinary(serialized);
+
+ assertEquals(msg.getOptionalInt32(), -42);
+ assertEquals(msg.getOptionalInt64(), -0x7fffffff00000000);
+ assertEquals(msg.getOptionalUint32(), 0x80000000);
+ assertEquals(msg.getOptionalUint64(), 0xf000000000000000);
+ assertEquals(msg.getOptionalSint32(), -100);
+ assertEquals(msg.getOptionalSint64(), -0x8000000000000000);
+ assertEquals(msg.getOptionalFixed32(), 1234);
+ assertEquals(msg.getOptionalFixed64(), 0x1234567800000000);
+ assertEquals(msg.getOptionalSfixed32(), -1234);
+ assertEquals(msg.getOptionalSfixed64(), -0x1234567800000000);
+ assertEquals(msg.getOptionalFloat(), 1.5);
+ assertEquals(msg.getOptionalDouble(), -1.5);
+ assertEquals(msg.getOptionalBool(), true);
+ assertEquals(msg.getOptionalString(), 'hello world');
+ assertEquals(true, bytesCompare(msg.getOptionalBytes(), 'bytes'));
+ assertEquals(msg.getOptionalForeignMessage().getC(), 16);
+ assertEquals(msg.getOptionalForeignEnum(),
+ proto.jspb.test.Proto3Enum.PROTO3_BAR);
+
+ assertElementsEquals(msg.getRepeatedInt32List(), [-42]);
+ assertElementsEquals(msg.getRepeatedInt64List(), [-0x7fffffff00000000]);
+ assertElementsEquals(msg.getRepeatedUint32List(), [0x80000000]);
+ assertElementsEquals(msg.getRepeatedUint64List(), [0xf000000000000000]);
+ assertElementsEquals(msg.getRepeatedSint32List(), [-100]);
+ assertElementsEquals(msg.getRepeatedSint64List(), [-0x8000000000000000]);
+ assertElementsEquals(msg.getRepeatedFixed32List(), [1234]);
+ assertElementsEquals(msg.getRepeatedFixed64List(), [0x1234567800000000]);
+ assertElementsEquals(msg.getRepeatedSfixed32List(), [-1234]);
+ assertElementsEquals(msg.getRepeatedSfixed64List(), [-0x1234567800000000]);
+ assertElementsEquals(msg.getRepeatedFloatList(), [1.5]);
+ assertElementsEquals(msg.getRepeatedDoubleList(), [-1.5]);
+ assertElementsEquals(msg.getRepeatedBoolList(), [true]);
+ assertElementsEquals(msg.getRepeatedStringList(), ['hello world']);
+ assertEquals(msg.getRepeatedBytesList().length, 1);
+ assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], 'bytes'));
+ assertEquals(msg.getRepeatedForeignMessageList().length, 1);
+ assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000);
+ assertElementsEquals(msg.getRepeatedForeignEnumList(),
+ [proto.jspb.test.Proto3Enum.PROTO3_BAR]);
+
+ assertEquals(msg.getOneofString(), 'asdf');
+ });
+
+
+ /**
+ * Test that oneofs continue to have a notion of field presence.
+ */
+ it('testOneofs', function() {
+ var msg = new proto.jspb.test.TestProto3();
+
+ assertEquals(msg.getOneofUint32(), undefined);
+ assertEquals(msg.getOneofForeignMessage(), undefined);
+ assertEquals(msg.getOneofString(), undefined);
+ assertEquals(msg.getOneofBytes(), undefined);
+
+ msg.setOneofUint32(42);
+ assertEquals(msg.getOneofUint32(), 42);
+ assertEquals(msg.getOneofForeignMessage(), undefined);
+ assertEquals(msg.getOneofString(), undefined);
+ assertEquals(msg.getOneofBytes(), undefined);
+
+
+ var submsg = new proto.jspb.test.ForeignMessage();
+ msg.setOneofForeignMessage(submsg);
+ assertEquals(msg.getOneofUint32(), undefined);
+ assertEquals(msg.getOneofForeignMessage(), submsg);
+ assertEquals(msg.getOneofString(), undefined);
+ assertEquals(msg.getOneofBytes(), undefined);
+
+ msg.setOneofString('hello');
+ assertEquals(msg.getOneofUint32(), undefined);
+ assertEquals(msg.getOneofForeignMessage(), undefined);
+ assertEquals(msg.getOneofString(), 'hello');
+ assertEquals(msg.getOneofBytes(), undefined);
+
+ msg.setOneofBytes('\u00FF\u00FF');
+ assertEquals(msg.getOneofUint32(), undefined);
+ assertEquals(msg.getOneofForeignMessage(), undefined);
+ assertEquals(msg.getOneofString(), undefined);
+ assertEquals(msg.getOneofBytes(), '\u00FF\u00FF');
+ });
+
+
+ /**
+ * Test that "default"-valued primitive fields are not emitted on the wire.
+ */
+ it('testNoSerializeDefaults', function() {
+ var msg = new proto.jspb.test.TestProto3();
+
+ // Set each primitive to a non-default value, then back to its default, to
+ // ensure that the serialization is actually checking the value and not just
+ // whether it has ever been set.
+ msg.setOptionalInt32(42);
+ msg.setOptionalInt32(0);
+ msg.setOptionalDouble(3.14);
+ msg.setOptionalDouble(0.0);
+ msg.setOptionalBool(true);
+ msg.setOptionalBool(false);
+ msg.setOptionalString('hello world');
+ msg.setOptionalString('');
+ msg.setOptionalBytes('\u00FF\u00FF');
+ msg.setOptionalBytes('');
+ msg.setOptionalForeignMessage(new proto.jspb.test.ForeignMessage());
+ msg.setOptionalForeignMessage(null);
+ msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_BAR);
+ msg.setOptionalForeignEnum(proto.jspb.test.Proto3Enum.PROTO3_FOO);
+ msg.setOneofUint32(32);
+ msg.setOneofUint32(null);
+
+
+ var serialized = msg.serializeBinary();
+ assertEquals(0, serialized.length);
+ });
+});
diff --git a/js/proto3_test.proto b/js/proto3_test.proto
new file mode 100644
index 00000000..acb67164
--- /dev/null
+++ b/js/proto3_test.proto
@@ -0,0 +1,89 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+import "testbinary.proto";
+
+package jspb.test;
+
+message TestProto3 {
+ int32 optional_int32 = 1;
+ int64 optional_int64 = 2;
+ uint32 optional_uint32 = 3;
+ uint64 optional_uint64 = 4;
+ sint32 optional_sint32 = 5;
+ sint64 optional_sint64 = 6;
+ fixed32 optional_fixed32 = 7;
+ fixed64 optional_fixed64 = 8;
+ sfixed32 optional_sfixed32 = 9;
+ sfixed64 optional_sfixed64 = 10;
+ float optional_float = 11;
+ double optional_double = 12;
+ bool optional_bool = 13;
+ string optional_string = 14;
+ bytes optional_bytes = 15;
+
+ ForeignMessage optional_foreign_message = 19;
+ Proto3Enum optional_foreign_enum = 22;
+
+ 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 ForeignMessage repeated_foreign_message = 49;
+ repeated Proto3Enum repeated_foreign_enum = 52;
+
+
+ oneof oneof_field {
+ uint32 oneof_uint32 = 111;
+ ForeignMessage oneof_foreign_message = 112;
+ string oneof_string = 113;
+ bytes oneof_bytes = 114;
+ }
+}
+
+enum Proto3Enum {
+ PROTO3_FOO = 0;
+ PROTO3_BAR = 1;
+ PROTO3_BAZ = 2;
+}
diff --git a/js/test.proto b/js/test.proto
new file mode 100644
index 00000000..5f9078ef
--- /dev/null
+++ b/js/test.proto
@@ -0,0 +1,212 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: mwr@google.com (Mark Rawling)
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+import "google/protobuf/descriptor.proto";
+
+package jspb.test;
+
+message Empty {
+}
+
+enum OuterEnum {
+ FOO = 1;
+ BAR = 2;
+}
+
+message EnumContainer {
+ optional OuterEnum outer_enum = 1;
+}
+
+message Simple1 {
+ required string a_string = 1;
+ repeated string a_repeated_string = 2;
+ optional bool a_boolean = 3;
+}
+
+// A message that differs from Simple1 only by name
+message Simple2 {
+ required string a_string = 1;
+ repeated string a_repeated_string = 2;
+}
+
+message SpecialCases {
+ required string normal = 1;
+ // Examples of Js reserved names that are converted to pb_<name>.
+ required string default = 2;
+ required string function = 3;
+ required string var = 4;
+}
+
+message OptionalFields {
+ message Nested {
+ optional int32 an_int = 1;
+ }
+ optional string a_string = 1;
+ required bool a_bool = 2;
+ optional Nested a_nested_message = 3;
+ repeated Nested a_repeated_message = 4;
+ repeated string a_repeated_string = 5;
+}
+
+message HasExtensions {
+ optional string str1 = 1;
+ optional string str2 = 2;
+ optional string str3 = 3;
+ extensions 10 to max;
+}
+
+message Complex {
+ message Nested {
+ required int32 an_int = 2;
+ }
+ required string a_string = 1;
+ required bool an_out_of_order_bool = 9;
+ optional Nested a_nested_message = 4;
+ repeated Nested a_repeated_message = 5;
+ repeated string a_repeated_string = 7;
+}
+
+message IsExtension {
+ extend HasExtensions {
+ optional IsExtension ext_field = 100;
+ }
+ optional string ext1 = 1;
+
+ // Extensions of proto2 Descriptor messages will be ignored.
+ extend google.protobuf.EnumOptions {
+ optional string simple_option = 42113038;
+ }
+}
+
+message IndirectExtension {
+ extend HasExtensions {
+ optional Simple1 simple = 101;
+ optional string str = 102;
+ repeated string repeated_str = 103;
+ repeated Simple1 repeated_simple = 104;
+ }
+}
+
+extend HasExtensions {
+ optional Simple1 simple1 = 105;
+}
+
+message DefaultValues {
+ enum Enum {
+ E1 = 13;
+ E2 = 77;
+ }
+ optional string string_field = 1 [default="default<>\'\"abc"];
+ optional bool bool_field = 2 [default=true];
+ optional int64 int_field = 3 [default=11];
+ optional Enum enum_field = 4 [default=E1];
+ optional string empty_field = 6 [default=""];
+ optional bytes bytes_field = 8 [default="moo"]; // Base64 encoding is "bW9v"
+}
+
+message TestClone {
+ optional string str = 1;
+ optional Simple1 simple1 = 3;
+ repeated Simple1 simple2 = 5;
+ optional string unused = 7;
+ extensions 10 to max;
+}
+
+message CloneExtension {
+ extend TestClone {
+ optional CloneExtension ext_field = 100;
+ }
+ optional string ext = 2;
+}
+
+message TestGroup {
+ repeated group RepeatedGroup = 1 {
+ required string id = 1;
+ repeated bool some_bool = 2;
+ }
+ required group RequiredGroup = 2 {
+ required string id = 1;
+ }
+ optional group OptionalGroup = 3 {
+ required string id = 1;
+ }
+ optional string id = 4;
+ required Simple2 required_simple = 5;
+ optional Simple2 optional_simple = 6;
+}
+
+message TestGroup1 {
+ optional TestGroup.RepeatedGroup group = 1;
+}
+
+message TestReservedNames {
+ optional int32 extension = 1;
+ extensions 10 to max;
+}
+
+message TestReservedNamesExtension {
+ extend TestReservedNames {
+ optional int32 foo = 10;
+ }
+}
+
+message TestMessageWithOneof {
+
+ oneof partial_oneof {
+ string pone = 3;
+ string pthree = 5;
+ }
+
+ oneof recursive_oneof {
+ TestMessageWithOneof rone = 6;
+ string rtwo = 7;
+ }
+
+ optional bool normal_field = 8;
+ repeated string repeated_field = 9;
+
+ oneof default_oneof_a {
+ int32 aone = 10 [default = 1234];
+ int32 atwo = 11;
+ }
+
+ oneof default_oneof_b {
+ int32 bone = 12;
+ int32 btwo = 13 [default = 1234];
+ }
+}
+
diff --git a/js/test2.proto b/js/test2.proto
new file mode 100644
index 00000000..44e55eff
--- /dev/null
+++ b/js/test2.proto
@@ -0,0 +1,54 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.test;
+
+message TestExtensionsMessage {
+ optional int32 intfield = 1;
+ extensions 100 to max;
+}
+
+message ExtensionMessage {
+ extend TestExtensionsMessage {
+ optional ExtensionMessage ext_field = 100;
+ }
+ optional string ext1 = 1;
+}
+
+// Floating extensions are only supported when generating a _lib.js library.
+extend TestExtensionsMessage {
+ optional ExtensionMessage floating_msg_field = 101;
+ optional string floating_str_field = 102;
+}
diff --git a/js/test3.proto b/js/test3.proto
new file mode 100644
index 00000000..940a552e
--- /dev/null
+++ b/js/test3.proto
@@ -0,0 +1,53 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.exttest;
+
+message TestExtensionsMessage {
+ optional int32 intfield = 1;
+ extensions 100 to max;
+}
+
+message ExtensionMessage {
+ extend TestExtensionsMessage {
+ optional ExtensionMessage ext_field = 100;
+ }
+ optional string ext1 = 1;
+}
+
+extend TestExtensionsMessage {
+ optional ExtensionMessage floating_msg_field = 101;
+ optional string floating_str_field = 102;
+}
diff --git a/src/google/protobuf/compiler/cpp/test_large_enum_value.proto b/js/test4.proto
index cb6ca1b1..cf2451e9 100644
--- a/src/google/protobuf/compiler/cpp/test_large_enum_value.proto
+++ b/js/test4.proto
@@ -28,16 +28,15 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Test that proto2 compiler can generate valid code when the enum value
-// is INT_MAX. Note that this is a compile-only test and this proto is not
-// referenced in any C++ code.
syntax = "proto2";
-package protobuf_unittest;
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
-message TestLargeEnumValue {
- enum EnumWithLargeValue {
- VALUE_1 = 1;
- VALUE_MAX = 0x7fffffff;
- }
+package jspb.exttest;
+
+import "test3.proto";
+
+extend TestExtensionsMessage {
+ optional ExtensionMessage floating_msg_field_two = 103;
}
diff --git a/js/test5.proto b/js/test5.proto
new file mode 100644
index 00000000..34979517
--- /dev/null
+++ b/js/test5.proto
@@ -0,0 +1,44 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+option java_package = "com.google.apps.jspb.proto";
+option java_multiple_files = true;
+
+package jspb.exttest.beta;
+
+message TestBetaExtensionsMessage {
+ extensions 100 to max;
+}
+
+extend TestBetaExtensionsMessage {
+ optional string floating_str_field = 101;
+}
diff --git a/js/test_bootstrap.js b/js/test_bootstrap.js
new file mode 100644
index 00000000..9d00a1c4
--- /dev/null
+++ b/js/test_bootstrap.js
@@ -0,0 +1,41 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Sets flags for uncompiled JSUnit tests.
+ */
+
+/**
+ * Set uncompiled flags.
+ */
+var CLOSURE_DEFINES = {
+ // Enable the fromObject method on the message class.
+ 'jspb.Message.GENERATE_FROM_OBJECT': true
+};
diff --git a/js/testbinary.proto b/js/testbinary.proto
new file mode 100644
index 00000000..60c70190
--- /dev/null
+++ b/js/testbinary.proto
@@ -0,0 +1,185 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// LINT: ALLOW_GROUPS
+
+syntax = "proto2";
+
+
+package jspb.test;
+
+// These types are borrowed from `unittest.proto` in the protobuf tree. We want
+// to ensure that the binary-format support will handle all field types
+// properly.
+message TestAllTypes {
+ 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 ForeignMessage optional_foreign_message = 19;
+ optional ForeignEnum optional_foreign_enum = 22;
+
+ // 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 ForeignMessage repeated_foreign_message = 49;
+ repeated ForeignEnum repeated_foreign_enum = 52;
+
+ // Packed repeated
+ repeated int32 packed_repeated_int32 = 61 [packed=true];
+ repeated int64 packed_repeated_int64 = 62 [packed=true];
+ repeated uint32 packed_repeated_uint32 = 63 [packed=true];
+ repeated uint64 packed_repeated_uint64 = 64 [packed=true];
+ repeated sint32 packed_repeated_sint32 = 65 [packed=true];
+ repeated sint64 packed_repeated_sint64 = 66 [packed=true];
+ repeated fixed32 packed_repeated_fixed32 = 67 [packed=true];
+ repeated fixed64 packed_repeated_fixed64 = 68 [packed=true];
+ repeated sfixed32 packed_repeated_sfixed32 = 69 [packed=true];
+ repeated sfixed64 packed_repeated_sfixed64 = 70 [packed=true];
+ repeated float packed_repeated_float = 71 [packed=true];
+ repeated double packed_repeated_double = 72 [packed=true];
+ repeated bool packed_repeated_bool = 73 [packed=true];
+
+ oneof oneof_field {
+ uint32 oneof_uint32 = 111;
+ ForeignMessage oneof_foreign_message = 112;
+ string oneof_string = 113;
+ bytes oneof_bytes = 114;
+ }
+
+}
+
+message ForeignMessage {
+ optional int32 c = 1;
+}
+
+enum ForeignEnum {
+ FOREIGN_FOO = 4;
+ FOREIGN_BAR = 5;
+ FOREIGN_BAZ = 6;
+}
+
+message TestExtendable {
+ extensions 1 to max;
+}
+
+message ExtendsWithMessage {
+ extend TestExtendable {
+ optional ExtendsWithMessage optional_extension = 19;
+ repeated ExtendsWithMessage repeated_extension = 49;
+ }
+ optional int32 foo = 1;
+}
+
+extend TestExtendable {
+ optional int32 extend_optional_int32 = 1;
+ optional int64 extend_optional_int64 = 2;
+ optional uint32 extend_optional_uint32 = 3;
+ optional uint64 extend_optional_uint64 = 4;
+ optional sint32 extend_optional_sint32 = 5;
+ optional sint64 extend_optional_sint64 = 6;
+ optional fixed32 extend_optional_fixed32 = 7;
+ optional fixed64 extend_optional_fixed64 = 8;
+ optional sfixed32 extend_optional_sfixed32 = 9;
+ optional sfixed64 extend_optional_sfixed64 = 10;
+ optional float extend_optional_float = 11;
+ optional double extend_optional_double = 12;
+ optional bool extend_optional_bool = 13;
+ optional string extend_optional_string = 14;
+ optional bytes extend_optional_bytes = 15;
+ optional ForeignEnum extend_optional_foreign_enum = 22;
+
+ repeated int32 extend_repeated_int32 = 31;
+ repeated int64 extend_repeated_int64 = 32;
+ repeated uint32 extend_repeated_uint32 = 33;
+ repeated uint64 extend_repeated_uint64 = 34;
+ repeated sint32 extend_repeated_sint32 = 35;
+ repeated sint64 extend_repeated_sint64 = 36;
+ repeated fixed32 extend_repeated_fixed32 = 37;
+ repeated fixed64 extend_repeated_fixed64 = 38;
+ repeated sfixed32 extend_repeated_sfixed32 = 39;
+ repeated sfixed64 extend_repeated_sfixed64 = 40;
+ repeated float extend_repeated_float = 41;
+ repeated double extend_repeated_double = 42;
+ repeated bool extend_repeated_bool = 43;
+ repeated string extend_repeated_string = 44;
+ repeated bytes extend_repeated_bytes = 45;
+ repeated ForeignEnum extend_repeated_foreign_enum = 52;
+
+ repeated int32 extend_packed_repeated_int32 = 61 [packed=true];
+ repeated int64 extend_packed_repeated_int64 = 62 [packed=true];
+ repeated uint32 extend_packed_repeated_uint32 = 63 [packed=true];
+ repeated uint64 extend_packed_repeated_uint64 = 64 [packed=true];
+ repeated sint32 extend_packed_repeated_sint32 = 65 [packed=true];
+ repeated sint64 extend_packed_repeated_sint64 = 66 [packed=true];
+ repeated fixed32 extend_packed_repeated_fixed32 = 67 [packed=true];
+ repeated fixed64 extend_packed_repeated_fixed64 = 68 [packed=true];
+ repeated sfixed32 extend_packed_repeated_sfixed32 = 69 [packed=true];
+ repeated sfixed64 extend_packed_repeated_sfixed64 = 70 [packed=true];
+ repeated float extend_packed_repeated_float = 71 [packed=true];
+ repeated double extend_packed_repeated_double = 72 [packed=true];
+ repeated bool extend_packed_repeated_bool = 73 [packed=true];
+ repeated ForeignEnum extend_packed_repeated_foreign_enum = 82
+ [packed=true];
+
+}
diff --git a/js/testempty.proto b/js/testempty.proto
new file mode 100644
index 00000000..960bce4e
--- /dev/null
+++ b/js/testempty.proto
@@ -0,0 +1,34 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package javatests.com.google.apps.jspb;
+
diff --git a/m4/acx_check_suncc.m4 b/m4/acx_check_suncc.m4
index 89185932..8bc0a890 100644
--- a/m4/acx_check_suncc.m4
+++ b/m4/acx_check_suncc.m4
@@ -42,7 +42,6 @@ AC_DEFUN([ACX_CHECK_SUNCC],[
AS_IF([test "x$ac_enable_64bit" = "xyes"],[
- AC_DEFINE([SOLARIS_64BIT_ENABLED], [1], [64bit enabled])
AS_IF([test "x$libdir" = "x\${exec_prefix}/lib"],[
dnl The user hasn't overridden the default libdir, so we'll
dnl the dir suffix to match solaris 32/64-bit policy
@@ -52,17 +51,13 @@ AC_DEFUN([ACX_CHECK_SUNCC],[
dnl This should just be set in CPPFLAGS and in LDFLAGS, but libtool
dnl does the wrong thing if you don't put it into CXXFLAGS. sigh.
dnl (It also needs it in CFLAGS, or it does a different wrong thing!)
- AS_IF([test "x${ac_cv_env_CXXFLAGS_set}" = "x"],[
- CXXFLAGS="${CXXFLAGS} -m64"
- ac_cv_env_CXXFLAGS_set=set
- ac_cv_env_CXXFLAGS_value='-m64'
- ])
+ CXXFLAGS="${CXXFLAGS} -m64"
+ ac_cv_env_CXXFLAGS_set=set
+ ac_cv_env_CXXFLAGS_value='-m64'
- AS_IF([test "x${ac_cv_env_CFLAGS_set}" = "x"],[
- CFLAGS="${CFLAGS} -m64"
- ac_cv_env_CFLAGS_set=set
- ac_cv_env_CFLAGS_value='-m64'
- ])
+ CFLAGS="${CFLAGS} -m64"
+ ac_cv_env_CFLAGS_set=set
+ ac_cv_env_CFLAGS_value='-m64'
AS_IF([test "$target_cpu" = "sparc" -a "x$SUNCC" = "xyes" ],[
CXXFLAGS="-xmemalign=8s ${CXXFLAGS}"
diff --git a/m4/stl_hash.m4 b/m4/stl_hash.m4
index 0722b14a..d7def1af 100644
--- a/m4/stl_hash.m4
+++ b/m4/stl_hash.m4
@@ -4,8 +4,7 @@
# include AC_TRY_COMPILE for all the combinations we've seen in the
# wild. We define HASH_MAP_H to the location of the header file, and
# HASH_NAMESPACE to the namespace the class (unordered_map or
-# hash_map) is in. We define HAVE_UNORDERED_MAP if the class we found
-# is named unordered_map, or leave it undefined if not.
+# hash_map) is in.
# This also checks if unordered map exists.
AC_DEFUN([AC_CXX_STL_HASH],
diff --git a/objectivec/DevTools/full_mac_build.sh b/objectivec/DevTools/full_mac_build.sh
index 71d3fa45..709aae0a 100755
--- a/objectivec/DevTools/full_mac_build.sh
+++ b/objectivec/DevTools/full_mac_build.sh
@@ -38,6 +38,8 @@ OPTIONS:
Skip the invoke of Xcode to test the runtime on iOS.
--skip-xcode-osx
Skip the invoke of Xcode to test the runtime on OS X.
+ --skip-objc-conformance
+ Skip the Objective C conformance tests (run on OS X).
EOF
}
@@ -73,6 +75,7 @@ REGEN_CPP_DESCRIPTORS=no
CORE_ONLY=no
DO_XCODE_IOS_TESTS=yes
DO_XCODE_OSX_TESTS=yes
+DO_OBJC_CONFORMANCE_TESTS=yes
while [[ $# != 0 ]]; do
case "${1}" in
-h | --help )
@@ -105,6 +108,9 @@ while [[ $# != 0 ]]; do
--skip-xcode-osx )
DO_XCODE_OSX_TESTS=no
;;
+ --skip-objc-conformance )
+ DO_OBJC_CONFORMANCE_TESTS=no
+ ;;
-*)
echo "ERROR: Unknown option: ${1}" 1>&2
printUsage
@@ -130,7 +136,9 @@ fi
if [[ "${DO_AUTOGEN}" == "yes" ]] ; then
header "Running autogen & configure"
./autogen.sh
- ./configure CXXFLAGS="-mmacosx-version-min=10.9 -Wnon-virtual-dtor -Woverloaded-virtual -Wunused-const-variable -Wunused-function"
+ ./configure \
+ CPPFLAGS="-mmacosx-version-min=10.9 -Wunused-const-variable -Wunused-function" \
+ CXXFLAGS="-Wnon-virtual-dtor -Woverloaded-virtual"
fi
if [[ "${DO_CLEAN}" == "yes" ]] ; then
@@ -172,7 +180,7 @@ else
wrapped_make -j "${NUM_MAKE_JOBS}" check
# Fire off the conformance tests also.
cd conformance
- wrapped_make -j "${NUM_MAKE_JOBS}"
+ wrapped_make -j "${NUM_MAKE_JOBS}" test_cpp
cd ..
fi
@@ -223,13 +231,8 @@ if [[ "${DO_XCODE_IOS_TESTS}" == "yes" ]] ; then
IOS_SIMULATOR_NAME="Simulator"
case "${XCODE_VERSION}" in
6.* )
- XCODEBUILD_TEST_BASE_IOS+=(
- -destination "platform=iOS Simulator,name=iPhone 4s,OS=7.1" # 32bit
- -destination "platform=iOS Simulator,name=iPhone 6,OS=8.4" # 64bit
- -destination "platform=iOS Simulator,name=iPad 2,OS=7.1" # 32bit
- -destination "platform=iOS Simulator,name=iPad Air,OS=8.4" # 64bit
- )
- IOS_SIMULATOR_NAME="iOS Simulator"
+ echo "ERROR: Xcode 6.3/6.4 no longer supported for building, please use 7.0 or higher." 1>&2
+ exit 10
;;
7.* )
XCODEBUILD_TEST_BASE_IOS+=(
@@ -264,3 +267,9 @@ if [[ "${DO_XCODE_OSX_TESTS}" == "yes" ]] ; then
header "Doing Xcode OS X build/tests - Release"
"${XCODEBUILD_TEST_BASE_OSX[@]}" -configuration Release test
fi
+
+if [[ "${DO_OBJC_CONFORMANCE_TESTS}" == "yes" ]] ; then
+ cd conformance
+ wrapped_make -j "${NUM_MAKE_JOBS}" test_objc
+ cd ..
+fi
diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m
index 71758baa..fd877838 100644
--- a/objectivec/GPBCodedInputStream.m
+++ b/objectivec/GPBCodedInputStream.m
@@ -216,7 +216,17 @@ NSString *GPBCodedInputStreamReadRetainedString(
result = @"";
} else {
CheckSize(state, size);
- result = GPBCreateGPBStringWithUTF8(&state->bytes[state->bufferPos], size);
+ result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos]
+ length:size
+ encoding:NSUTF8StringEncoding];
+ if (!result) {
+ result = @"";
+#ifdef DEBUG
+ // https://developers.google.com/protocol-buffers/docs/proto#scalar
+ NSLog(@"UTF8 failure, is some field type 'string' when it should be "
+ @"'bytes'?");
+#endif
+ }
state->bufferPos += size;
}
return result;
@@ -264,10 +274,6 @@ void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state,
}
size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state) {
- if (state->currentLimit == SIZE_T_MAX) {
- return state->currentLimit;
- }
-
return state->currentLimit - state->bufferPos;
}
@@ -299,7 +305,7 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
buffer_ = [data retain];
state_.bytes = (const uint8_t *)[data bytes];
state_.bufferSize = [data length];
- state_.currentLimit = NSUIntegerMax;
+ state_.currentLimit = state_.bufferSize;
}
return self;
}
@@ -482,320 +488,3 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
}
@end
-
-@implementation GPBString {
- @package
- CFStringRef string_;
- unsigned char *utf8_;
- NSUInteger utf8Len_;
-
- // This lock is used to gate access to utf8_. Once GPBStringInitStringValue()
- // has been called, string_ will be filled in, and utf8_ will be NULL.
- OSSpinLock lock_;
-
- BOOL hasBOM_;
- BOOL is7BitAscii_;
-}
-
-// Returns true if the passed in bytes are 7 bit ascii.
-// This routine needs to be fast.
-static bool AreBytesIn7BitASCII(const uint8_t *bytes, NSUInteger len) {
-// In the loops below, it's more efficient to collect rather than do
-// conditional at every step.
-#if __LP64__
- // Align bytes. This is especially important in case of 3 byte BOM.
- while (len > 0 && ((size_t)bytes & 0x07)) {
- if (*bytes++ & 0x80) return false;
- len--;
- }
- while (len >= 32) {
- uint64_t val = *(const uint64_t *)bytes;
- uint64_t hiBits = (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- hiBits |= (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- hiBits |= (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- if (hiBits | (val & 0x8080808080808080ULL)) return false;
- bytes += 8;
- len -= 32;
- }
-
- while (len >= 16) {
- uint64_t val = *(const uint64_t *)bytes;
- uint64_t hiBits = (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- if (hiBits | (val & 0x8080808080808080ULL)) return false;
- bytes += 8;
- len -= 16;
- }
-
- while (len >= 8) {
- uint64_t val = *(const uint64_t *)bytes;
- if (val & 0x8080808080808080ULL) return false;
- bytes += 8;
- len -= 8;
- }
-#else // __LP64__
- // Align bytes. This is especially important in case of 3 byte BOM.
- while (len > 0 && ((size_t)bytes & 0x03)) {
- if (*bytes++ & 0x80) return false;
- len--;
- }
- while (len >= 16) {
- uint32_t val = *(const uint32_t *)bytes;
- uint32_t hiBits = (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- hiBits |= (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- hiBits |= (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- if (hiBits | (val & 0x80808080U)) return false;
- bytes += 4;
- len -= 16;
- }
-
- while (len >= 8) {
- uint32_t val = *(const uint32_t *)bytes;
- uint32_t hiBits = (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- if (hiBits | (val & 0x80808080U)) return false;
- bytes += 4;
- len -= 8;
- }
-#endif // __LP64__
-
- while (len >= 4) {
- uint32_t val = *(const uint32_t *)bytes;
- if (val & 0x80808080U) return false;
- bytes += 4;
- len -= 4;
- }
-
- while (len--) {
- if (*bytes++ & 0x80) return false;
- }
-
- return true;
-}
-
-static void GPBStringInitStringValue(GPBString *string) {
- OSSpinLockLock(&string->lock_);
- GPBStringInitStringValueAlreadyLocked(string);
- OSSpinLockUnlock(&string->lock_);
-}
-
-static void GPBStringInitStringValueAlreadyLocked(GPBString *string) {
- if (string->string_ == NULL && string->utf8_ != NULL) {
- // Using kCFAllocatorMalloc for contentsDeallocator, as buffer in
- // string->utf8_ is being handed off.
- string->string_ = CFStringCreateWithBytesNoCopy(
- NULL, string->utf8_, string->utf8Len_, kCFStringEncodingUTF8, false,
- kCFAllocatorMalloc);
- if (!string->string_) {
-#ifdef DEBUG
- // https://developers.google.com/protocol-buffers/docs/proto#scalar
- NSLog(@"UTF8 failure, is some field type 'string' when it should be "
- @"'bytes'?");
-#endif
- string->string_ = CFSTR("");
- string->utf8Len_ = 0;
- // On failure, we have to clean up the buffer.
- free(string->utf8_);
- }
- string->utf8_ = NULL;
- }
-}
-
-GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length) {
- GPBString *result = [[GPBString alloc] initWithBytes:bytes length:length];
- return result;
-}
-
-- (instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)length {
- self = [super init];
- if (self) {
- utf8_ = malloc(length);
- memcpy(utf8_, bytes, length);
- utf8Len_ = length;
- lock_ = OS_SPINLOCK_INIT;
- is7BitAscii_ = AreBytesIn7BitASCII(bytes, length);
- if (length >= 3 && memcmp(utf8_, "\xef\xbb\xbf", 3) == 0) {
- // We can't just remove the BOM from the string here, because in the case
- // where we have > 1 BOM at the beginning of the string, we will remove one,
- // and the internal NSString we create will remove the next one, and we will
- // end up with a GPBString != NSString issue.
- // We also just can't remove all the BOMs because then we would end up with
- // potential cases where a GPBString and an NSString made with the same
- // UTF8 buffer would in fact be different.
- // We record the fact we have a BOM, and use it as necessary to simulate
- // what NSString would return for various calls.
- hasBOM_ = YES;
-#if DEBUG
- // Sending BOMs across the line is just wasting bits.
- NSLog(@"Bad data? String should not have BOM!");
-#endif // DEBUG
- }
- }
- return self;
-}
-
-- (void)dealloc {
- if (string_ != NULL) {
- CFRelease(string_);
- }
- if (utf8_ != NULL) {
- free(utf8_);
- }
- [super dealloc];
-}
-
-// Required NSString overrides.
-- (NSUInteger)length {
- if (is7BitAscii_) {
- return utf8Len_;
- } else {
- GPBStringInitStringValue(self);
- return CFStringGetLength(string_);
- }
-}
-
-- (unichar)characterAtIndex:(NSUInteger)anIndex {
- OSSpinLockLock(&lock_);
- if (is7BitAscii_ && utf8_) {
- unichar result = utf8_[anIndex];
- OSSpinLockUnlock(&lock_);
- return result;
- } else {
- GPBStringInitStringValueAlreadyLocked(self);
- OSSpinLockUnlock(&lock_);
- return CFStringGetCharacterAtIndex(string_, anIndex);
- }
-}
-
-// Override a couple of methods that typically want high performance.
-
-- (id)copyWithZone:(NSZone *)zone {
- GPBStringInitStringValue(self);
- return [(NSString *)string_ copyWithZone:zone];
-}
-
-- (id)mutableCopyWithZone:(NSZone *)zone {
- GPBStringInitStringValue(self);
- return [(NSString *)string_ mutableCopyWithZone:zone];
-}
-
-- (NSUInteger)hash {
- // Must convert to string here to make sure that the hash is always
- // consistent no matter what state the GPBString is in.
- GPBStringInitStringValue(self);
- return CFHash(string_);
-}
-
-- (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
- if ([object isKindOfClass:[NSString class]]) {
- GPBStringInitStringValue(self);
- return CFStringCompare(string_, (CFStringRef)object, 0) ==
- kCFCompareEqualTo;
- }
- return NO;
-}
-
-- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange {
- OSSpinLockLock(&lock_);
- if (is7BitAscii_ && utf8_) {
- unsigned char *bytes = &(utf8_[aRange.location]);
- for (NSUInteger i = 0; i < aRange.length; ++i) {
- buffer[i] = bytes[i];
- }
- OSSpinLockUnlock(&lock_);
- } else {
- GPBStringInitStringValueAlreadyLocked(self);
- OSSpinLockUnlock(&lock_);
- CFStringGetCharacters(string_, CFRangeMake(aRange.location, aRange.length),
- buffer);
- }
-}
-
-- (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)encoding {
- if ((encoding == NSUTF8StringEncoding) ||
- (encoding == NSASCIIStringEncoding && is7BitAscii_)) {
- return utf8Len_ - (hasBOM_ ? 3 : 0);
- } else {
- GPBStringInitStringValue(self);
- return [(NSString *)string_ lengthOfBytesUsingEncoding:encoding];
- }
-}
-
-- (BOOL)getBytes:(void *)buffer
- maxLength:(NSUInteger)maxLength
- usedLength:(NSUInteger *)usedLength
- encoding:(NSStringEncoding)encoding
- options:(NSStringEncodingConversionOptions)options
- range:(NSRange)range
- remainingRange:(NSRangePointer)remainingRange {
- // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
- // does not return reliable results if the maxLength argument is 0
- // (Radar 16385183). Therefore we have special cased it as a slow case so
- // that it behaves however Apple intends it to behave. It should be a rare
- // case.
- //
- // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
- // does not return reliable results if the range is outside of the strings
- // length (Radar 16396177). Therefore we have special cased it as a slow
- // case so that it behaves however Apple intends it to behave. It should
- // be a rare case.
- //
- // We can optimize the UTF8StringEncoding and NSASCIIStringEncoding with no
- // options cases.
- if ((options == 0) &&
- (encoding == NSUTF8StringEncoding || encoding == NSASCIIStringEncoding) &&
- (maxLength != 0) &&
- (NSMaxRange(range) <= utf8Len_)) {
- // Might be able to optimize it.
- OSSpinLockLock(&lock_);
- if (is7BitAscii_ && utf8_) {
- NSUInteger length = range.length;
- length = (length < maxLength) ? length : maxLength;
- memcpy(buffer, utf8_ + range.location, length);
- if (usedLength) {
- *usedLength = length;
- }
- if (remainingRange) {
- remainingRange->location = range.location + length;
- remainingRange->length = range.length - length;
- }
- OSSpinLockUnlock(&lock_);
- if (length > 0) {
- return YES;
- } else {
- return NO;
- }
- } else {
- GPBStringInitStringValueAlreadyLocked(self);
- OSSpinLockUnlock(&lock_);
- }
- } else {
- GPBStringInitStringValue(self);
- }
- return [(NSString *)string_ getBytes:buffer
- maxLength:maxLength
- usedLength:usedLength
- encoding:encoding
- options:options
- range:range
- remainingRange:remainingRange];
-}
-
-@end
diff --git a/objectivec/GPBCodedInputStream_PackagePrivate.h b/objectivec/GPBCodedInputStream_PackagePrivate.h
index e482b4c9..90bd0c92 100644
--- a/objectivec/GPBCodedInputStream_PackagePrivate.h
+++ b/objectivec/GPBCodedInputStream_PackagePrivate.h
@@ -39,19 +39,6 @@
@class GPBUnknownFieldSet;
@class GPBFieldDescriptor;
-// GPBString is a string subclass that avoids the overhead of initializing
-// a full NSString until it is actually needed. Lots of protocol buffers contain
-// strings, and instantiating all of those strings and having them parsed to
-// verify correctness when the message was being read was expensive, when many
-// of the strings were never being used.
-//
-// Note for future-self. I tried implementing this using a NSProxy.
-// Turned out the performance was horrible in client apps because folks
-// like to use libraries like SBJSON that grab characters one at a time.
-// The proxy overhead was a killer.
-@interface GPBString : NSString
-@end
-
typedef struct GPBCodedInputStreamState {
const uint8_t *bytes;
size_t bufferSize;
@@ -92,10 +79,6 @@ typedef struct GPBCodedInputStreamState {
CF_EXTERN_C_BEGIN
-// Returns a GPBString with a +1 retain count.
-GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length)
- __attribute__((ns_returns_retained));
-
int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state);
double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state);
diff --git a/objectivec/GPBDictionary.m b/objectivec/GPBDictionary.m
index 31ee410e..6baa2a18 100644
--- a/objectivec/GPBDictionary.m
+++ b/objectivec/GPBDictionary.m
@@ -45,6 +45,18 @@
// directly.
// ------------------------------------------------------------------
+// Used to include code only visible to specific versions of the static
+// analyzer. Useful for wrapping code that only exists to silence the analyzer.
+// Determine the values you want to use for BEGIN_APPLE_BUILD_VERSION,
+// END_APPLE_BUILD_VERSION using:
+// xcrun clang -dM -E -x c /dev/null | grep __apple_build_version__
+// Example usage:
+// #if GPB_STATIC_ANALYZER_ONLY(5621, 5623) ... #endif
+#define GPB_STATIC_ANALYZER_ONLY(BEGIN_APPLE_BUILD_VERSION, END_APPLE_BUILD_VERSION) \
+ (defined(__clang_analyzer__) && \
+ (__apple_build_version__ >= BEGIN_APPLE_BUILD_VERSION && \
+ __apple_build_version__ <= END_APPLE_BUILD_VERSION))
+
enum {
kMapKeyFieldNumber = 1,
kMapValueFieldNumber = 2,
@@ -496,6 +508,19 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
if ((keyDataType == GPBDataTypeString) && GPBDataTypeIsObject(valueDataType)) {
+#if GPB_STATIC_ANALYZER_ONLY(6020053, 7000181)
+ // Limited to Xcode 6.4 - 7.2, are known to fail here. The upper end can
+ // be raised as needed for new Xcodes.
+ //
+ // This is only needed on a "shallow" analyze; on a "deep" analyze, the
+ // existing code path gets this correct. In shallow, the analyzer decides
+ // GPBDataTypeIsObject(valueDataType) is both false and true on a single
+ // path through this function, allowing nil to be used for the
+ // setObject:forKey:.
+ if (value.valueString == nil) {
+ value.valueString = [@"" retain];
+ }
+#endif
// mapDictionary is an NSMutableDictionary
[(NSMutableDictionary *)mapDictionary setObject:value.valueString
forKey:key.valueString];
@@ -601,7 +626,7 @@ void GPBDictionaryReadEntry(id mapDictionary,
//% _dictionary = [[NSMutableDictionary alloc] init];
//% if (count && VNAME##s && keys) {
//% for (NSUInteger i = 0; i < count; ++i) {
-//% [_dictionary setObject:WRAPPED##VHELPER(VNAME##s[i]) forKey:WRAPPED##KHELPER(keys[i])];
+//%DICTIONARY_VALIDATE_VALUE_##VHELPER(VNAME##s[i], ______)##DICTIONARY_VALIDATE_KEY_##KHELPER(keys[i], ______) [_dictionary setObject:WRAPPED##VHELPER(VNAME##s[i]) forKey:WRAPPED##KHELPER(keys[i])];
//% }
//% }
//% }
@@ -711,7 +736,7 @@ void GPBDictionaryReadEntry(id mapDictionary,
//% _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
//% if (count && rawValues && keys) {
//% for (NSUInteger i = 0; i < count; ++i) {
-//% [_dictionary setObject:WRAPPED##VHELPER(rawValues[i]) forKey:WRAPPED##KHELPER(keys[i])];
+//%DICTIONARY_VALIDATE_KEY_##KHELPER(keys[i], ______) [_dictionary setObject:WRAPPED##VHELPER(rawValues[i]) forKey:WRAPPED##KHELPER(keys[i])];
//% }
//% }
//% }
@@ -776,7 +801,7 @@ void GPBDictionaryReadEntry(id mapDictionary,
//%DICTIONARY_MUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, value, Raw)
//%
//%- (void)setValue:(VALUE_TYPE)value forKey:(KEY_TYPE##KisP$S##KisP)key {
-//% if (!_validationFunc(value)) {
+//%DICTIONARY_VALIDATE_KEY_##KHELPER(key, ) if (!_validationFunc(value)) {
//% [NSException raise:NSInvalidArgumentException
//% format:@"GPB##KEY_NAME##VALUE_NAME##Dictionary: Attempt to set an unknown enum value (%d)",
//% value];
@@ -900,7 +925,7 @@ void GPBDictionaryReadEntry(id mapDictionary,
//%}
//%
//%- (void)set##ACCESSOR_NAME##VNAME$u##:(VALUE_TYPE)VNAME forKey:(KEY_TYPE##KisP$S##KisP)key {
-//% [_dictionary setObject:WRAPPED##VHELPER(VNAME) forKey:WRAPPED##KHELPER(key)];
+//%DICTIONARY_VALIDATE_VALUE_##VHELPER(VNAME, )##DICTIONARY_VALIDATE_KEY_##KHELPER(key, ) [_dictionary setObject:WRAPPED##VHELPER(VNAME) forKey:WRAPPED##KHELPER(key)];
//% if (_autocreator) {
//% GPBAutocreatedDictionaryModified(_autocreator, self);
//% }
@@ -1171,6 +1196,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
//%
//%PDDM-DEFINE GPBVALUE_POD(VALUE_NAME)
//%value##VALUE_NAME
+//%PDDM-DEFINE DICTIONARY_VALIDATE_VALUE_POD(VALUE_NAME, EXTRA_INDENT)
+// Empty
+//%PDDM-DEFINE DICTIONARY_VALIDATE_KEY_POD(KEY_NAME, EXTRA_INDENT)
+// Empty
//%PDDM-DEFINE BOOL_DICT_HAS_STORAGE_POD()
//% BOOL _valueSet[2];
@@ -1363,7 +1392,18 @@ void GPBDictionaryReadEntry(id mapDictionary,
// Empty
//%PDDM-DEFINE GPBVALUE_OBJECT(VALUE_NAME)
//%valueString
-
+//%PDDM-DEFINE DICTIONARY_VALIDATE_VALUE_OBJECT(VALUE_NAME, EXTRA_INDENT)
+//%##EXTRA_INDENT$S## if (!##VALUE_NAME) {
+//%##EXTRA_INDENT$S## [NSException raise:NSInvalidArgumentException
+//%##EXTRA_INDENT$S## format:@"Attempting to add nil object to a Dictionary"];
+//%##EXTRA_INDENT$S## }
+//%
+//%PDDM-DEFINE DICTIONARY_VALIDATE_KEY_OBJECT(KEY_NAME, EXTRA_INDENT)
+//%##EXTRA_INDENT$S## if (!##KEY_NAME) {
+//%##EXTRA_INDENT$S## [NSException raise:NSInvalidArgumentException
+//%##EXTRA_INDENT$S## format:@"Attempting to add nil key to a Dictionary"];
+//%##EXTRA_INDENT$S## }
+//%
//%PDDM-DEFINE BOOL_DICT_HAS_STORAGE_OBJECT()
// Empty
@@ -1374,6 +1414,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
//% self = [super init];
//% if (self) {
//% for (NSUInteger i = 0; i < count; ++i) {
+//% if (!objects[i]) {
+//% [NSException raise:NSInvalidArgumentException
+//% format:@"Attempting to add nil object to a Dictionary"];
+//% }
//% int idx = keys[i] ? 1 : 0;
//% [_values[idx] release];
//% _values[idx] = (VALUE_TYPE)[objects[i] retain];
@@ -1433,6 +1477,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
//%}
//%
//%- (void)setObject:(VALUE_TYPE)object forKey:(BOOL)key {
+//% if (!object) {
+//% [NSException raise:NSInvalidArgumentException
+//% format:@"Attempting to add nil object to a Dictionary"];
+//% }
//% int idx = (key ? 1 : 0);
//% [_values[idx] release];
//% _values[idx] = [object retain];
@@ -3239,6 +3287,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && objects && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!objects[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:objects[i] forKey:@(keys[i])];
}
}
@@ -3397,6 +3449,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setObject:(id)object forKey:(uint32_t)key {
+ if (!object) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:object forKey:@(key)];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -5195,6 +5251,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && objects && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!objects[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:objects[i] forKey:@(keys[i])];
}
}
@@ -5353,6 +5413,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setObject:(id)object forKey:(int32_t)key {
+ if (!object) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:object forKey:@(key)];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -7151,6 +7215,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && objects && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!objects[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:objects[i] forKey:@(keys[i])];
}
}
@@ -7309,6 +7377,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setObject:(id)object forKey:(uint64_t)key {
+ if (!object) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:object forKey:@(key)];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -9107,6 +9179,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && objects && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!objects[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:objects[i] forKey:@(keys[i])];
}
}
@@ -9265,6 +9341,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setObject:(id)object forKey:(int64_t)key {
+ if (!object) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
[_dictionary setObject:object forKey:@(key)];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -9336,6 +9416,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -9474,6 +9558,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(uint32_t)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -9542,6 +9630,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -9680,6 +9772,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(int32_t)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -9748,6 +9844,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -9886,6 +9986,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(uint64_t)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -9954,6 +10058,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -10092,6 +10200,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(int64_t)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -10160,6 +10272,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -10298,6 +10414,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(BOOL)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -10366,6 +10486,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -10504,6 +10628,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(float)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -10572,6 +10700,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_dictionary = [[NSMutableDictionary alloc] init];
if (count && values && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(values[i]) forKey:keys[i]];
}
}
@@ -10710,6 +10842,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(double)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -10802,6 +10938,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
_validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
if (count && rawValues && keys) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!keys[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(rawValues[i]) forKey:keys[i]];
}
}
@@ -10982,6 +11122,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setRawValue:(int32_t)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
[_dictionary setObject:@(value) forKey:key];
if (_autocreator) {
GPBAutocreatedDictionaryModified(_autocreator, self);
@@ -10997,6 +11141,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setValue:(int32_t)value forKey:(NSString *)key {
+ if (!key) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil key to a Dictionary"];
+ }
if (!_validationFunc(value)) {
[NSException raise:NSInvalidArgumentException
format:@"GPBStringEnumDictionary: Attempt to set an unknown enum value (%d)",
@@ -12754,6 +12902,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
self = [super init];
if (self) {
for (NSUInteger i = 0; i < count; ++i) {
+ if (!objects[i]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
int idx = keys[i] ? 1 : 0;
[_values[idx] release];
_values[idx] = (id)[objects[i] retain];
@@ -12932,6 +13084,10 @@ void GPBDictionaryReadEntry(id mapDictionary,
}
- (void)setObject:(id)object forKey:(BOOL)key {
+ if (!object) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Attempting to add nil object to a Dictionary"];
+ }
int idx = (key ? 1 : 0);
[_values[idx] release];
_values[idx] = [object retain];
diff --git a/objectivec/GPBMessage.h b/objectivec/GPBMessage.h
index d4c2fccf..332393ed 100644
--- a/objectivec/GPBMessage.h
+++ b/objectivec/GPBMessage.h
@@ -127,7 +127,7 @@ CF_EXTERN_C_END
// Same as -[data], except a delimiter is added to the start of the data
// indicating the size of the message data that follows.
-- (nullable NSData *)delimitedData;
+- (NSData *)delimitedData;
// Returns the size of the object if it were serialized.
// This is not a cached value. If you are following a pattern like this:
diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m
index c655edd3..fdb695ec 100644
--- a/objectivec/GPBMessage.m
+++ b/objectivec/GPBMessage.m
@@ -55,10 +55,15 @@ NSString *const GPBExceptionMessageKey =
static NSString *const kGPBDataCoderKey = @"GPBData";
#ifndef _GPBCompileAssert
-#define _GPBCompileAssertSymbolInner(line, msg) _GPBCompileAssert ## line ## __ ## msg
-#define _GPBCompileAssertSymbol(line, msg) _GPBCompileAssertSymbolInner(line, msg)
-#define _GPBCompileAssert(test, msg) \
- typedef char _GPBCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+ #if __has_feature(c_static_assert) || __has_extension(c_static_assert)
+ #define _GPBCompileAssert(test, msg) _Static_assert((test), #msg)
+ #else
+ // Pre-Xcode 7 support.
+ #define _GPBCompileAssertSymbolInner(line, msg) _GPBCompileAssert ## line ## __ ## msg
+ #define _GPBCompileAssertSymbol(line, msg) _GPBCompileAssertSymbolInner(line, msg)
+ #define _GPBCompileAssert(test, msg) \
+ typedef char _GPBCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+ #endif // __has_feature(c_static_assert) || __has_extension(c_static_assert)
#endif // _GPBCompileAssert
//
@@ -563,13 +568,13 @@ static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!array) {
// Check again after getting the lock.
- OSSpinLockLock(&self->readOnlyMutex_);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!array) {
array = CreateArrayForField(field, self);
GPBSetAutocreatedRetainedObjectIvarWithField(self, field, array);
}
- OSSpinLockUnlock(&self->readOnlyMutex_);
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
}
return array;
}
@@ -593,13 +598,13 @@ static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!dict) {
// Check again after getting the lock.
- OSSpinLockLock(&self->readOnlyMutex_);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!dict) {
dict = CreateMapForField(field, self);
GPBSetAutocreatedRetainedObjectIvarWithField(self, field, dict);
}
- OSSpinLockUnlock(&self->readOnlyMutex_);
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
}
return dict;
}
@@ -805,7 +810,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
messageStorage_ = (GPBMessage_StoragePtr)(
((uint8_t *)self) + class_getInstanceSize([self class]));
- readOnlyMutex_ = OS_SPINLOCK_INIT;
+ readOnlySemaphore_ = dispatch_semaphore_create(1);
}
return self;
@@ -881,6 +886,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
- (void)dealloc {
[self internalClear:NO];
NSCAssert(!autocreator_, @"Autocreator was not cleared before dealloc.");
+ dispatch_release(readOnlySemaphore_);
[super dealloc];
}
@@ -1212,7 +1218,8 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
NSLog(@"%@: Internal exception while building message delimitedData: %@",
[self class], exception);
#endif
- data = nil;
+ // If it happens, truncate.
+ data.length = 0;
}
[stream release];
return data;
@@ -1717,7 +1724,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
}
// Check for an autocreated value.
- OSSpinLockLock(&readOnlyMutex_);
+ dispatch_semaphore_wait(readOnlySemaphore_, DISPATCH_TIME_FOREVER);
value = [autocreatedExtensionMap_ objectForKey:extension];
if (!value) {
// Auto create the message extensions to match normal fields.
@@ -1734,7 +1741,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[value release];
}
- OSSpinLockUnlock(&readOnlyMutex_);
+ dispatch_semaphore_signal(readOnlySemaphore_);
return value;
}
@@ -1791,7 +1798,12 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
extensionMap_ = [[NSMutableDictionary alloc] init];
}
- [extensionMap_ setObject:value forKey:extension];
+ // This pointless cast is for CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION.
+ // Without it, the compiler complains we're passing an id nullable when
+ // setObject:forKey: requires a id nonnull for the value. The check for
+ // !value at the start of the method ensures it isn't nil, but the check
+ // isn't smart enough to realize that.
+ [extensionMap_ setObject:(id)value forKey:extension];
GPBExtensionDescriptor *descriptor = extension;
diff --git a/objectivec/GPBMessage_PackagePrivate.h b/objectivec/GPBMessage_PackagePrivate.h
index d4c3501a..b7e24fc9 100644
--- a/objectivec/GPBMessage_PackagePrivate.h
+++ b/objectivec/GPBMessage_PackagePrivate.h
@@ -62,7 +62,12 @@ typedef struct GPBMessage_Storage *GPBMessage_StoragePtr;
// by *read* operations such as getters (autocreation of message fields and
// message extensions, not setting of values). Used to guarantee thread safety
// for concurrent reads on the message.
- OSSpinLock readOnlyMutex_;
+ // NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+ // pointed out that they are vulnerable to live locking on iOS in cases of
+ // priority inversion:
+ // http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+ // https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+ dispatch_semaphore_t readOnlySemaphore_;
}
// Gets an extension value without autocreating the result if not found. (i.e.
diff --git a/objectivec/GPBProtocolBuffers.h b/objectivec/GPBProtocolBuffers.h
index 0c85259e..677903ed 100644
--- a/objectivec/GPBProtocolBuffers.h
+++ b/objectivec/GPBProtocolBuffers.h
@@ -41,6 +41,7 @@
#import "GPBUnknownField.h"
#import "GPBUnknownFieldSet.h"
#import "GPBUtilities.h"
+#import "GPBWellKnownTypes.h"
#import "GPBWireFormat.h"
// Well-known proto types
diff --git a/objectivec/GPBRootObject.m b/objectivec/GPBRootObject.m
index 7036723f..4570716f 100644
--- a/objectivec/GPBRootObject.m
+++ b/objectivec/GPBRootObject.m
@@ -31,7 +31,6 @@
#import "GPBRootObject_PackagePrivate.h"
#import <objc/runtime.h>
-#import <libkern/OSAtomic.h>
#import <CoreFoundation/CoreFoundation.h>
@@ -96,13 +95,19 @@ static CFHashCode GPBRootExtensionKeyHash(const void *value) {
return jenkins_one_at_a_time_hash(key);
}
-static OSSpinLock gExtensionSingletonDictionaryLock_ = OS_SPINLOCK_INIT;
+// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+// pointed out that they are vulnerable to live locking on iOS in cases of
+// priority inversion:
+// http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+static dispatch_semaphore_t gExtensionSingletonDictionarySemaphore;
static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+ (void)initialize {
// Ensure the global is started up.
if (!gExtensionSingletonDictionary) {
+ gExtensionSingletonDictionarySemaphore = dispatch_semaphore_create(1);
CFDictionaryKeyCallBacks keyCallBacks = {
// See description above for reason for using custom dictionary.
0,
@@ -134,9 +139,10 @@ static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field {
const char *key = [field singletonNameC];
- OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+ DISPATCH_TIME_FOREVER);
CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
- OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
}
static id ExtensionForName(id self, SEL _cmd) {
@@ -166,14 +172,24 @@ static id ExtensionForName(id self, SEL _cmd) {
key[classNameLen] = '_';
memcpy(&key[classNameLen + 1], selName, selNameLen);
key[classNameLen + 1 + selNameLen] = '\0';
- OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
+
+ // NOTE: Even though this method is called from another C function,
+ // gExtensionSingletonDictionarySemaphore and gExtensionSingletonDictionary
+ // will always be initialized. This is because this call flow is just to
+ // lookup the Extension, meaning the code is calling an Extension class
+ // message on a Message or Root class. This guarantees that the class was
+ // initialized and Message classes ensure their Root was also initialized.
+ NSAssert(gExtensionSingletonDictionary, @"Startup order broken!");
+
+ dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+ DISPATCH_TIME_FOREVER);
id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
if (extension) {
// The method is getting wired in to the class, so no need to keep it in
// the dictionary.
CFDictionaryRemoveValue(gExtensionSingletonDictionary, key);
}
- OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
return extension;
}
diff --git a/objectivec/GPBUtilities.h b/objectivec/GPBUtilities.h
index 1301b436..5b55104b 100644
--- a/objectivec/GPBUtilities.h
+++ b/objectivec/GPBUtilities.h
@@ -44,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
// most likely won't exactly match the original .proto file.
NSString *GPBTextFormatForMessage(GPBMessage *message,
NSString * __nullable lineIndent);
-NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet *unknownSet,
+NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet * __nullable unknownSet,
NSString * __nullable lineIndent);
//
diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m
index 5ee61230..eaa28bbc 100644
--- a/objectivec/GPBUtilities.m
+++ b/objectivec/GPBUtilities.m
@@ -411,7 +411,7 @@ id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
return field.defaultValue.valueMessage;
}
- OSSpinLockLock(&self->readOnlyMutex_);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!result) {
// For non repeated messages, create the object, set it and return it.
@@ -420,7 +420,7 @@ id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
result = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
GPBSetAutocreatedRetainedObjectIvarWithField(self, field, result);
}
- OSSpinLockUnlock(&self->readOnlyMutex_);
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
return result;
}
diff --git a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
index 08d0b7ef..6b34b9bf 100644
--- a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
+++ b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
@@ -28,7 +28,6 @@
8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B8B615C17DF7056002EE618 /* GPBARCUnittestProtos.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
8B96157414C8C38C00A2AC0B /* GPBDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B96157314C8C38C00A2AC0B /* GPBDescriptor.m */; };
8B96157514CA019D00A2AC0B /* Descriptor.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */; };
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */; };
8BBEA4A9147C727D00C4ADB7 /* GPBCodedInputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69B0F94FDF800A0C422 /* GPBCodedInputStreamTests.m */; };
8BBEA4AA147C727D00C4ADB7 /* GPBCodedOuputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */; };
8BBEA4AC147C727D00C4ADB7 /* GPBMessageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B6A30F94FDF800A0C422 /* GPBMessageTests.m */; };
@@ -158,7 +157,6 @@
8B8B615C17DF7056002EE618 /* GPBARCUnittestProtos.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBARCUnittestProtos.m; sourceTree = "<group>"; };
8B96157214C8B06000A2AC0B /* GPBDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBDescriptor.h; sourceTree = "<group>"; };
8B96157314C8C38C00A2AC0B /* GPBDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBDescriptor.m; sourceTree = "<group>"; };
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBStringTests.m; sourceTree = "<group>"; };
8BBD9DB016DD1DC8008E1EC1 /* unittest_lite.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_lite.proto; path = ../../src/google/protobuf/unittest_lite.proto; sourceTree = "<group>"; };
8BBEA4A6147C727100C4ADB7 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
8BCF338814ED799900BC5317 /* GPBProtocolBuffers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GPBProtocolBuffers.m; sourceTree = "<group>"; };
@@ -419,7 +417,6 @@
F4487C7E1AAF62CD00531423 /* GPBMessageTests+Serialization.m */,
F4B51B1D1BBC610700744318 /* GPBObjectiveCPlusPlusTest.mm */,
F41C175C1833D3310064ED4D /* GPBPerfTests.m */,
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */,
8B4248BA1A8C256A00BC1EC6 /* GPBSwiftTests.swift */,
7461B6AB0F94FDF800A0C422 /* GPBTestUtilities.h */,
7461B6AC0F94FDF800A0C422 /* GPBTestUtilities.m */,
@@ -567,8 +564,9 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
+ LastSwiftUpdateCheck = 0710;
LastTestingUpgradeCheck = 0600;
- LastUpgradeCheck = 0630;
+ LastUpgradeCheck = 0710;
TargetAttributes = {
8BBEA4A5147C727100C4ADB7 = {
TestTargetID = 8B9A5EA41831993600A9D33B;
@@ -688,7 +686,6 @@
F41C175D1833D3310064ED4D /* GPBPerfTests.m in Sources */,
F4353D341AC06F10005A6198 /* GPBDictionaryTests+Bool.m in Sources */,
F4487C831AAF6AB300531423 /* GPBMessageTests+Merge.m in Sources */,
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */,
8BBEA4B6147C727D00C4ADB7 /* GPBUnknownFieldSetTest.m in Sources */,
F4353D371AC06F10005A6198 /* GPBDictionaryTests+String.m in Sources */,
F4353D381AC06F10005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
@@ -756,6 +753,7 @@
);
INFOPLIST_FILE = "Tests/UnitTests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:identifier}";
PRODUCT_NAME = UnitTests;
SWIFT_OBJC_BRIDGING_HEADER = "Tests/UnitTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -773,6 +771,7 @@
);
INFOPLIST_FILE = "Tests/UnitTests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:identifier}";
PRODUCT_NAME = UnitTests;
SWIFT_OBJC_BRIDGING_HEADER = "Tests/UnitTests-Bridging-Header.h";
};
@@ -787,10 +786,12 @@
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
@@ -831,6 +832,7 @@
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
diff --git a/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme b/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
index 3b3eeb12..25814c5f 100644
--- a/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
+++ b/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0630"
+ LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- buildConfiguration = "Release">
+ shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -159,6 +159,9 @@
Identifier = "GPBInt64UInt64DictionaryTests">
</Test>
<Test
+ Identifier = "GPBObjectiveCPlusPlusTests">
+ </Test>
+ <Test
Identifier = "GPBStringBoolDictionaryTests">
</Test>
<Test
@@ -180,9 +183,6 @@
Identifier = "GPBStringInt64DictionaryTests">
</Test>
<Test
- Identifier = "GPBStringTests">
- </Test>
- <Test
Identifier = "GPBStringUInt32DictionaryTests">
</Test>
<Test
@@ -284,15 +284,18 @@
</SkippedTests>
</TestableReference>
</Testables>
+ <AdditionalOptions>
+ </AdditionalOptions>
</TestAction>
<LaunchAction
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@@ -307,10 +310,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
+ buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
diff --git a/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme b/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
index 583a464a..8f510f5d 100644
--- a/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
+++ b/objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0630"
+ LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -51,10 +51,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- buildConfiguration = "Debug">
+ shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -81,15 +81,18 @@
ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
</BuildableReference>
</MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
</TestAction>
<LaunchAction
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@@ -104,10 +107,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
+ buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
diff --git a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
index 14e51037..e9d3fc95 100644
--- a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
+++ b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
@@ -36,7 +36,6 @@
8B9A5EB41831993600A9D33B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B9A5EB31831993600A9D33B /* AppDelegate.m */; };
8B9A5EB61831993600A9D33B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B9A5EB51831993600A9D33B /* Images.xcassets */; };
8B9A5EEC18330A0F00A9D33B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B9A5E9F1831913D00A9D33B /* UIKit.framework */; };
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */; };
8BBEA4A9147C727D00C4ADB7 /* GPBCodedInputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69B0F94FDF800A0C422 /* GPBCodedInputStreamTests.m */; };
8BBEA4AA147C727D00C4ADB7 /* GPBCodedOuputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */; };
8BBEA4AC147C727D00C4ADB7 /* GPBMessageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B6A30F94FDF800A0C422 /* GPBMessageTests.m */; };
@@ -179,7 +178,6 @@
8B9A5EAD1831993600A9D33B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8B9A5EB31831993600A9D33B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8B9A5EB51831993600A9D33B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBStringTests.m; sourceTree = "<group>"; };
8BBD9DB016DD1DC8008E1EC1 /* unittest_lite.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_lite.proto; path = ../../src/google/protobuf/unittest_lite.proto; sourceTree = "<group>"; };
8BBEA4A6147C727100C4ADB7 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
8BCF338814ED799900BC5317 /* GPBProtocolBuffers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GPBProtocolBuffers.m; sourceTree = "<group>"; };
@@ -457,7 +455,6 @@
F4487C801AAF62FC00531423 /* GPBMessageTests+Serialization.m */,
F4B51B1B1BBC5C7100744318 /* GPBObjectiveCPlusPlusTest.mm */,
F41C175C1833D3310064ED4D /* GPBPerfTests.m */,
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */,
8B4248B31A8BD96E00BC1EC6 /* GPBSwiftTests.swift */,
7461B6AB0F94FDF800A0C422 /* GPBTestUtilities.h */,
7461B6AC0F94FDF800A0C422 /* GPBTestUtilities.m */,
@@ -644,8 +641,9 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
+ LastSwiftUpdateCheck = 0710;
LastTestingUpgradeCheck = 0600;
- LastUpgradeCheck = 0630;
+ LastUpgradeCheck = 0710;
TargetAttributes = {
8BBEA4A5147C727100C4ADB7 = {
TestTargetID = 8B9A5EA41831993600A9D33B;
@@ -784,7 +782,6 @@
F41C175D1833D3310064ED4D /* GPBPerfTests.m in Sources */,
F4353D421AC06F31005A6198 /* GPBDictionaryTests+Bool.m in Sources */,
F4487C851AAF6AC500531423 /* GPBMessageTests+Merge.m in Sources */,
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */,
8BBEA4B6147C727D00C4ADB7 /* GPBUnknownFieldSetTest.m in Sources */,
F4353D451AC06F31005A6198 /* GPBDictionaryTests+String.m in Sources */,
F4353D461AC06F31005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
@@ -865,6 +862,7 @@
CLANG_ENABLE_OBJC_ARC = YES;
INFOPLIST_FILE = "$(SRCROOT)/Tests/iOSTestHarness/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
WRAPPER_EXTENSION = app;
@@ -879,6 +877,7 @@
CLANG_ENABLE_OBJC_ARC = YES;
INFOPLIST_FILE = "$(SRCROOT)/Tests/iOSTestHarness/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
WRAPPER_EXTENSION = app;
@@ -949,11 +948,13 @@
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
@@ -994,6 +995,7 @@
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
diff --git a/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme b/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
index 4898eb5f..0b96b75f 100644
--- a/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
+++ b/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0630"
+ LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- buildConfiguration = "Release">
+ shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -159,6 +159,9 @@
Identifier = "GPBInt64UInt64DictionaryTests">
</Test>
<Test
+ Identifier = "GPBObjectiveCPlusPlusTests">
+ </Test>
+ <Test
Identifier = "GPBStringBoolDictionaryTests">
</Test>
<Test
@@ -180,9 +183,6 @@
Identifier = "GPBStringInt64DictionaryTests">
</Test>
<Test
- Identifier = "GPBStringTests">
- </Test>
- <Test
Identifier = "GPBStringUInt32DictionaryTests">
</Test>
<Test
@@ -293,15 +293,18 @@
ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
</TestAction>
<LaunchAction
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -317,10 +320,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
+ buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
diff --git a/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme b/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
index 19267fc3..7d219bcd 100644
--- a/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
+++ b/objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0630"
+ LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -51,10 +51,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- buildConfiguration = "Debug">
+ shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -81,15 +81,18 @@
ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
</TestAction>
<LaunchAction
+ buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -105,10 +108,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
+ buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
diff --git a/objectivec/README.md b/objectivec/README.md
index 134bf1ab..c7313e4f 100644
--- a/objectivec/README.md
+++ b/objectivec/README.md
@@ -10,10 +10,10 @@ This directory contains the Objective C Protocol Buffers runtime library.
Requirements
------------
-The Objective C implemention requires:
+The Objective C implementation requires:
- Objective C 2.0 Runtime (32bit & 64bit iOS, 64bit OS X).
-- Xcode 6.3 (or later).
+- Xcode 7.0 (or later).
- The library code does *not* use ARC (for performance reasons), but it all can
be called from ARC code.
diff --git a/objectivec/Tests/GPBCodedInputStreamTests.m b/objectivec/Tests/GPBCodedInputStreamTests.m
index 579fe65c..b0e39d2c 100644
--- a/objectivec/Tests/GPBCodedInputStreamTests.m
+++ b/objectivec/Tests/GPBCodedInputStreamTests.m
@@ -225,8 +225,10 @@
// Serialize and parse it. Make sure to parse from an InputStream, not
// directly from a ByteString, so that CodedInputStream uses buffered
// reading.
+ NSData *messageData = message.data;
+ XCTAssertNotNil(messageData);
GPBCodedInputStream* stream =
- [GPBCodedInputStream streamWithData:message.data];
+ [GPBCodedInputStream streamWithData:messageData];
TestAllTypes* message2 = [TestAllTypes parseFromCodedInputStream:stream
extensionRegistry:nil
error:NULL];
@@ -264,6 +266,9 @@
}
// Verifies fix for b/10315336.
+// Note: Now that there isn't a custom string class under the hood, this test
+// isn't as critical, but it does cover bad input and if a custom class is added
+// again, it will help validate that class' handing of bad utf8.
- (void)testReadMalformedString {
NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
GPBCodedOutputStream* output =
@@ -274,7 +279,7 @@
[output writeRawVarint32:tag];
[output writeRawVarint32:5];
// Create an invalid utf-8 byte array.
- uint8_t bytes[5] = {0xc2, 0xf2};
+ uint8_t bytes[] = {0xc2, 0xf2, 0x0, 0x0, 0x0};
[output writeRawData:[NSData dataWithBytes:bytes length:sizeof(bytes)]];
[output flush];
@@ -284,6 +289,7 @@
TestAllTypes* message = [TestAllTypes parseFromCodedInputStream:input
extensionRegistry:nil
error:NULL];
+ XCTAssertNotNil(message);
// Make sure we can read string properties twice without crashing.
XCTAssertEqual([message.defaultString length], (NSUInteger)0);
XCTAssertEqualObjects(@"", message.defaultString);
diff --git a/objectivec/Tests/GPBMessageTests+Merge.m b/objectivec/Tests/GPBMessageTests+Merge.m
index a3908aab..c0bd5897 100644
--- a/objectivec/Tests/GPBMessageTests+Merge.m
+++ b/objectivec/Tests/GPBMessageTests+Merge.m
@@ -675,14 +675,12 @@
TestAllTypes *subMsg = [TestAllTypes message];
subMsg.repeatedInt32Array = [GPBInt32Array arrayWithValue:100];
- msg1.mapInt32Message = [GPBInt32ObjectDictionary dictionary];
[msg1.mapInt32Message setObject:subMsg forKey:0];
subMsg = nil;
subMsg = [TestAllTypes message];
subMsg.repeatedInt32Array = [GPBInt32Array arrayWithValue:101];
- msg2.mapInt32Message = [GPBInt32ObjectDictionary dictionary];
-
+
[msg2.mapInt32Message setObject:subMsg forKey:0];
subMsg = nil;
diff --git a/objectivec/Tests/GPBMessageTests+Runtime.m b/objectivec/Tests/GPBMessageTests+Runtime.m
index e536bfec..5e197711 100644
--- a/objectivec/Tests/GPBMessageTests+Runtime.m
+++ b/objectivec/Tests/GPBMessageTests+Runtime.m
@@ -2074,7 +2074,6 @@
// Add an uninitialized message.
TestRequired *subMsg = [[TestRequired alloc] init];
- msg.mapField = [GPBInt32ObjectDictionary dictionary];
[msg.mapField setObject:subMsg forKey:0];
XCTAssertFalse(msg.initialized);
diff --git a/objectivec/Tests/GPBMessageTests+Serialization.m b/objectivec/Tests/GPBMessageTests+Serialization.m
index 4dcca7a3..0d811a96 100644
--- a/objectivec/Tests/GPBMessageTests+Serialization.m
+++ b/objectivec/Tests/GPBMessageTests+Serialization.m
@@ -121,8 +121,9 @@ static NSData *DataFromCStr(const char *str) {
fooWithExtras.enumValue = DropUnknownsFooWithExtraFields_NestedEnum_Baz;
fooWithExtras.extraInt32Value = 2;
- DropUnknownsFoo *foo =
- [DropUnknownsFoo parseFromData:[fooWithExtras data] error:NULL];
+ NSData *data = [fooWithExtras data];
+ XCTAssertNotNil(data);
+ DropUnknownsFoo *foo = [DropUnknownsFoo parseFromData:data error:NULL];
XCTAssertEqual(foo.int32Value, 1);
XCTAssertEqual(foo.enumValue, DropUnknownsFoo_NestedEnum_Baz);
@@ -130,8 +131,9 @@ static NSData *DataFromCStr(const char *str) {
XCTAssertEqual([foo.unknownFields countOfFields], 0U);
[fooWithExtras release];
+ data = [foo data];
fooWithExtras =
- [DropUnknownsFooWithExtraFields parseFromData:[foo data] error:NULL];
+ [DropUnknownsFooWithExtraFields parseFromData:data error:NULL];
XCTAssertEqual(fooWithExtras.int32Value, 1);
XCTAssertEqual(fooWithExtras.enumValue,
DropUnknownsFooWithExtraFields_NestedEnum_Baz);
@@ -149,7 +151,9 @@ static NSData *DataFromCStr(const char *str) {
rawValue:Message3_Enum_Extra3];
orig.oneofEnum = Message3_Enum_Extra3;
- Message2 *msg = [[Message2 alloc] initWithData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [[Message2 alloc] initWithData:data error:NULL];
// None of the fields should be set.
@@ -201,8 +205,10 @@ static NSData *DataFromCStr(const char *str) {
// Everything should be there via raw values.
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
UnknownEnumsMyMessage *msg =
- [UnknownEnumsMyMessage parseFromData:[orig data] error:NULL];
+ [UnknownEnumsMyMessage parseFromData:data error:NULL];
XCTAssertEqual(msg.e, UnknownEnumsMyEnum_GPBUnrecognizedEnumeratorValue);
XCTAssertEqual(UnknownEnumsMyMessage_E_RawValue(msg),
@@ -224,7 +230,8 @@ static NSData *DataFromCStr(const char *str) {
// Everything should go out and come back.
- orig = [UnknownEnumsMyMessagePlusExtra parseFromData:[msg data] error:NULL];
+ data = [msg data];
+ orig = [UnknownEnumsMyMessagePlusExtra parseFromData:data error:NULL];
XCTAssertEqual(orig.e, UnknownEnumsMyEnumPlusExtra_EExtra);
XCTAssertEqual(orig.repeatedEArray.count, 1U);
@@ -243,7 +250,9 @@ static NSData *DataFromCStr(const char *str) {
//% MESSAGE *orig = [[MESSAGE alloc] init];
//% orig.oneof##FIELD = VALUE;
//% XCTAssertEqual(orig.oOneOfCase, MESSAGE##_O_OneOfCase_Oneof##FIELD);
-//% MESSAGE *msg = [MESSAGE parseFromData:[orig data] error:NULL];
+//% NSData *data = [orig data];
+//% XCTAssertNotNil(data);
+//% MESSAGE *msg = [MESSAGE parseFromData:data error:NULL];
//% XCTAssertEqual(msg.oOneOfCase, MESSAGE##_O_OneOfCase_Oneof##FIELD);
//% XCTAssertEqual##EQ_SUFFIX(msg.oneof##FIELD, VALUE);
//% [orig release];
@@ -311,7 +320,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofInt32 = 1;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofInt32);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofInt32);
XCTAssertEqual(msg.oneofInt32, 1);
[orig release];
@@ -321,7 +332,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofInt64 = 2;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofInt64);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofInt64);
XCTAssertEqual(msg.oneofInt64, 2);
[orig release];
@@ -331,7 +344,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofUint32 = 3U;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofUint32);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofUint32);
XCTAssertEqual(msg.oneofUint32, 3U);
[orig release];
@@ -341,7 +356,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofUint64 = 4U;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofUint64);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofUint64);
XCTAssertEqual(msg.oneofUint64, 4U);
[orig release];
@@ -351,7 +368,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofSint32 = 5;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofSint32);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofSint32);
XCTAssertEqual(msg.oneofSint32, 5);
[orig release];
@@ -361,7 +380,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofSint64 = 6;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofSint64);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofSint64);
XCTAssertEqual(msg.oneofSint64, 6);
[orig release];
@@ -371,7 +392,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofFixed32 = 7U;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofFixed32);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofFixed32);
XCTAssertEqual(msg.oneofFixed32, 7U);
[orig release];
@@ -381,7 +404,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofFixed64 = 8U;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofFixed64);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofFixed64);
XCTAssertEqual(msg.oneofFixed64, 8U);
[orig release];
@@ -391,7 +416,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofSfixed32 = 9;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofSfixed32);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofSfixed32);
XCTAssertEqual(msg.oneofSfixed32, 9);
[orig release];
@@ -401,7 +428,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofSfixed64 = 10;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofSfixed64);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofSfixed64);
XCTAssertEqual(msg.oneofSfixed64, 10);
[orig release];
@@ -411,7 +440,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofFloat = 11.0f;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofFloat);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofFloat);
XCTAssertEqual(msg.oneofFloat, 11.0f);
[orig release];
@@ -421,7 +452,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofDouble = 12.0;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofDouble);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofDouble);
XCTAssertEqual(msg.oneofDouble, 12.0);
[orig release];
@@ -431,7 +464,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofBool = NO;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofBool);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofBool);
XCTAssertEqual(msg.oneofBool, NO);
[orig release];
@@ -441,7 +476,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofString = @"foo";
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofString);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofString);
XCTAssertEqualObjects(msg.oneofString, @"foo");
[orig release];
@@ -451,7 +488,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofBytes = [@"bar" dataUsingEncoding:NSUTF8StringEncoding];
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofBytes);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofBytes);
XCTAssertEqualObjects(msg.oneofBytes, [@"bar" dataUsingEncoding:NSUTF8StringEncoding]);
[orig release];
@@ -461,7 +500,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofGroup = group;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofGroup);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofGroup);
XCTAssertEqualObjects(msg.oneofGroup, group);
[orig release];
@@ -471,7 +512,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofMessage = subMessage;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofMessage);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofMessage);
XCTAssertEqualObjects(msg.oneofMessage, subMessage);
[orig release];
@@ -481,7 +524,9 @@ static NSData *DataFromCStr(const char *str) {
Message2 *orig = [[Message2 alloc] init];
orig.oneofEnum = Message2_Enum_Bar;
XCTAssertEqual(orig.oOneOfCase, Message2_O_OneOfCase_OneofEnum);
- Message2 *msg = [Message2 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message2 *msg = [Message2 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_OneofEnum);
XCTAssertEqual(msg.oneofEnum, Message2_Enum_Bar);
[orig release];
@@ -504,7 +549,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofInt32 = 1;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofInt32);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofInt32);
XCTAssertEqual(msg.oneofInt32, 1);
[orig release];
@@ -514,7 +561,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofInt64 = 2;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofInt64);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofInt64);
XCTAssertEqual(msg.oneofInt64, 2);
[orig release];
@@ -524,7 +573,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofUint32 = 3U;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofUint32);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofUint32);
XCTAssertEqual(msg.oneofUint32, 3U);
[orig release];
@@ -534,7 +585,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofUint64 = 4U;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofUint64);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofUint64);
XCTAssertEqual(msg.oneofUint64, 4U);
[orig release];
@@ -544,7 +597,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofSint32 = 5;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofSint32);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofSint32);
XCTAssertEqual(msg.oneofSint32, 5);
[orig release];
@@ -554,7 +609,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofSint64 = 6;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofSint64);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofSint64);
XCTAssertEqual(msg.oneofSint64, 6);
[orig release];
@@ -564,7 +621,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofFixed32 = 7U;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofFixed32);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofFixed32);
XCTAssertEqual(msg.oneofFixed32, 7U);
[orig release];
@@ -574,7 +633,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofFixed64 = 8U;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofFixed64);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofFixed64);
XCTAssertEqual(msg.oneofFixed64, 8U);
[orig release];
@@ -584,7 +645,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofSfixed32 = 9;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofSfixed32);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofSfixed32);
XCTAssertEqual(msg.oneofSfixed32, 9);
[orig release];
@@ -594,7 +657,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofSfixed64 = 10;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofSfixed64);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofSfixed64);
XCTAssertEqual(msg.oneofSfixed64, 10);
[orig release];
@@ -604,7 +669,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofFloat = 11.0f;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofFloat);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofFloat);
XCTAssertEqual(msg.oneofFloat, 11.0f);
[orig release];
@@ -614,7 +681,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofDouble = 12.0;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofDouble);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofDouble);
XCTAssertEqual(msg.oneofDouble, 12.0);
[orig release];
@@ -624,7 +693,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofBool = YES;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofBool);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofBool);
XCTAssertEqual(msg.oneofBool, YES);
[orig release];
@@ -634,7 +705,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofString = @"foo";
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofString);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofString);
XCTAssertEqualObjects(msg.oneofString, @"foo");
[orig release];
@@ -644,7 +717,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofBytes = [@"bar" dataUsingEncoding:NSUTF8StringEncoding];
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofBytes);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofBytes);
XCTAssertEqualObjects(msg.oneofBytes, [@"bar" dataUsingEncoding:NSUTF8StringEncoding]);
[orig release];
@@ -656,7 +731,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofMessage = subMessage;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofMessage);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofMessage);
XCTAssertEqualObjects(msg.oneofMessage, subMessage);
[orig release];
@@ -666,7 +743,9 @@ static NSData *DataFromCStr(const char *str) {
Message3 *orig = [[Message3 alloc] init];
orig.oneofEnum = Message2_Enum_Bar;
XCTAssertEqual(orig.oOneOfCase, Message3_O_OneOfCase_OneofEnum);
- Message3 *msg = [Message3 parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ Message3 *msg = [Message3 parseFromData:data error:NULL];
XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_OneofEnum);
XCTAssertEqual(msg.oneofEnum, Message2_Enum_Bar);
[orig release];
@@ -927,15 +1006,18 @@ static NSData *DataFromCStr(const char *str) {
[orig.unknownMapField setValue:Proto2MapEnumPlusExtra_EProto2MapEnumExtra
forKey:0];
- TestEnumMap *msg1 = [TestEnumMap parseFromData:[orig data] error:NULL];
+ NSData *data = [orig data];
+ XCTAssertNotNil(data);
+ TestEnumMap *msg1 = [TestEnumMap parseFromData:data error:NULL];
XCTAssertEqual(msg1.knownMapField.count, 1U);
int32_t val = -1;
XCTAssertTrue([msg1.knownMapField valueForKey:0 value:&val]);
XCTAssertEqual(val, Proto2MapEnum_Proto2MapEnumFoo);
XCTAssertEqual(msg1.unknownFields.countOfFields, 1U);
+ data = [msg1 data];
TestEnumMapPlusExtra *msg2 =
- [TestEnumMapPlusExtra parseFromData:[msg1 data] error:NULL];
+ [TestEnumMapPlusExtra parseFromData:data error:NULL];
val = -1;
XCTAssertEqual(msg2.knownMapField.count, 1U);
XCTAssertTrue([msg2.knownMapField valueForKey:0 value:&val]);
@@ -1006,6 +1088,7 @@ static NSData *DataFromCStr(const char *str) {
[msg.mapInt32Message setObject:val4 forKey:2035];
NSData *data = [msg data];
+ XCTAssertNotNil(data);
Message2 *msg2 = [[Message2 alloc] initWithData:data error:NULL];
XCTAssertNotEqual(msg2, msg); // Pointer comparison
diff --git a/objectivec/Tests/GPBMessageTests.m b/objectivec/Tests/GPBMessageTests.m
index cd0de8fc..7b37ca95 100644
--- a/objectivec/Tests/GPBMessageTests.m
+++ b/objectivec/Tests/GPBMessageTests.m
@@ -55,7 +55,6 @@
[message setOptionalInt32:1];
[message setOptionalString:@"foo"];
[message setOptionalForeignMessage:[ForeignMessage message]];
- message.repeatedStringArray = [NSMutableArray array];
[message.repeatedStringArray addObject:@"bar"];
return message;
}
@@ -67,7 +66,6 @@
ForeignMessage *foreignMessage = [ForeignMessage message];
[foreignMessage setC:3];
[message setOptionalForeignMessage:foreignMessage];
- message.repeatedStringArray = [NSMutableArray array];
[message.repeatedStringArray addObject:@"qux"];
return message;
}
@@ -76,7 +74,6 @@
TestAllTypes *message = [TestAllTypes message];
[message setOptionalInt64:2];
[message setOptionalString:@"baz"];
- message.repeatedStringArray = [NSMutableArray array];
[message.repeatedStringArray addObject:@"qux"];
return message;
}
@@ -89,7 +86,6 @@
ForeignMessage *foreignMessage = [ForeignMessage message];
[foreignMessage setC:3];
[message setOptionalForeignMessage:foreignMessage];
- message.repeatedStringArray = [NSMutableArray array];
[message.repeatedStringArray addObject:@"qux"];
[message.repeatedStringArray addObject:@"bar"];
return message;
@@ -102,7 +98,6 @@
[message setOptionalString:@"foo"];
ForeignMessage *foreignMessage = [ForeignMessage message];
[message setOptionalForeignMessage:foreignMessage];
- message.repeatedStringArray = [NSMutableArray array];
[message.repeatedStringArray addObject:@"qux"];
[message.repeatedStringArray addObject:@"bar"];
return message;
@@ -195,7 +190,9 @@
// Test merging from data.
result = [self mergeExtensionsDestination];
- [result mergeFromData:[[self mergeExtensionsSource] data]
+ NSData *data = [[self mergeExtensionsSource] data];
+ XCTAssertNotNil(data);
+ [result mergeFromData:data
extensionRegistry:[UnittestRoot extensionRegistry]];
resultData = [result data];
XCTAssertEqualObjects(resultData, mergeResultData);
@@ -246,7 +243,6 @@
[message setOptionalMessage:self.testRequiredInitialized];
XCTAssertTrue(message.initialized);
- message.repeatedMessageArray = [NSMutableArray array];
[message.repeatedMessageArray addObject:[TestRequired message]];
XCTAssertFalse(message.initialized);
@@ -298,7 +294,6 @@
- (void)testDataFromNestedUninitialized {
TestRequiredForeign *message = [TestRequiredForeign message];
[message setOptionalMessage:[TestRequired message]];
- message.repeatedMessageArray = [NSMutableArray array];
[message.repeatedMessageArray addObject:[TestRequired message]];
[message.repeatedMessageArray addObject:[TestRequired message]];
NSData *data = [message data];
@@ -317,7 +312,6 @@
TestRequiredForeign *message = [TestRequiredForeign message];
[message setOptionalMessage:[TestRequired message]];
- message.repeatedMessageArray = [NSMutableArray array];
[message.repeatedMessageArray addObject:[TestRequired message]];
[message.repeatedMessageArray addObject:[TestRequired message]];
@@ -1884,7 +1878,9 @@
XCTAssertEqual(msg.bar, EnumTestMsg_MyEnum_One);
XCTAssertEqual(msg.baz, EnumTestMsg_MyEnum_NegOne);
// Bounce to wire and back.
- EnumTestMsg *msgPrime = [EnumTestMsg parseFromData:[msg data] error:NULL];
+ NSData *data = [msg data];
+ XCTAssertNotNil(data);
+ EnumTestMsg *msgPrime = [EnumTestMsg parseFromData:data error:NULL];
XCTAssertEqualObjects(msgPrime, msg);
XCTAssertEqual(msgPrime.foo, EnumTestMsg_MyEnum_Zero);
XCTAssertEqual(msgPrime.bar, EnumTestMsg_MyEnum_One);
@@ -1896,7 +1892,9 @@
XCTAssertEqual(msg.bar, EnumTestMsg_MyEnum_Two);
XCTAssertEqual(msg.baz, EnumTestMsg_MyEnum_NegTwo);
// Bounce to wire and back.
- msgPrime = [EnumTestMsg parseFromData:[msg data] error:NULL];
+ data = [msg data];
+ XCTAssertNotNil(data);
+ msgPrime = [EnumTestMsg parseFromData:data error:NULL];
XCTAssertEqualObjects(msgPrime, msg);
XCTAssertEqual(msgPrime.foo, EnumTestMsg_MyEnum_Zero);
XCTAssertEqual(msgPrime.bar, EnumTestMsg_MyEnum_Two);
@@ -1917,7 +1915,9 @@
XCTAssertEqual([msg.mumbleArray valueAtIndex:3], EnumTestMsg_MyEnum_NegOne);
XCTAssertEqual([msg.mumbleArray valueAtIndex:4], EnumTestMsg_MyEnum_NegTwo);
// Bounce to wire and back.
- msgPrime = [EnumTestMsg parseFromData:[msg data] error:NULL];
+ data = [msg data];
+ XCTAssertNotNil(data);
+ msgPrime = [EnumTestMsg parseFromData:data error:NULL];
XCTAssertEqualObjects(msgPrime, msg);
XCTAssertEqual([msgPrime.mumbleArray valueAtIndex:0],
EnumTestMsg_MyEnum_Zero);
diff --git a/objectivec/Tests/GPBStringTests.m b/objectivec/Tests/GPBStringTests.m
deleted file mode 100644
index 802afa7d..00000000
--- a/objectivec/Tests/GPBStringTests.m
+++ /dev/null
@@ -1,513 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#import <XCTest/XCTest.h>
-
-#import "GPBCodedInputStream_PackagePrivate.h"
-#import "GPBTestUtilities.h"
-
-@interface TestClass : NSObject
-@property(nonatomic, retain) NSString *foo;
-@end
-
-@implementation TestClass
-@synthesize foo;
-@end
-
-@interface GPBStringTests : XCTestCase {
- NSMutableArray *nsStrings_;
- NSMutableArray *gpbStrings_;
-}
-
-@end
-
-@implementation GPBStringTests
-
-- (void)setUp {
- [super setUp];
- const char *strings[] = {
- "ascii string",
- "non-ascii string \xc3\xa9", // e with acute accent
- "\xe2\x99\xa1", // White Heart
- "mix \xe2\x99\xa4 string", // White Spade
-
- // Decomposed forms from http://www.unicode.org/reports/tr15/
- // 1.2 Singletons
- "\xe2\x84\xa8 = A\xcc\x8a = \xc3\x85", "\xe2\x84\xa6 = \xce\xa9",
-
- // 1.2 Canonical Composites
- "A\xcc\x8a = \xc3\x85",
- "o\xcc\x82 = \xc3\xb4",
-
- // 1.2 Multiple Combining Marks
- "s\xcc\xa3\xcc\x87 = \xe1\xb9\xa9",
- "\xe1\xb8\x8b\xcc\xa3 = d\xcc\xa3\xcc\x87 = \xe1\xb8\x8d \xcc\x87",
- "q\xcc\x87\xcc\xa3 = q\xcc\xa3\xcc\x87",
-
- // BOM
- "\xEF\xBB\xBF String with BOM",
- "String with \xEF\xBB\xBF in middle",
- "String with end bom \xEF\xBB\xBF",
- "\xEF\xBB\xBF\xe2\x99\xa1", // BOM White Heart
- "\xEF\xBB\xBF\xEF\xBB\xBF String with Two BOM",
-
- // Supplementary Plane
- "\xf0\x9d\x84\x9e", // MUSICAL SYMBOL G CLEF
-
- // Tags
- "\xf3\xa0\x80\x81", // Language Tag
-
- // Variation Selectors
- "\xf3\xa0\x84\x80", // VARIATION SELECTOR-17
-
- // Specials
- "\xef\xbb\xbf\xef\xbf\xbd\xef\xbf\xbf",
-
- // Left To Right/Right To Left
- // http://unicode.org/reports/tr9/
-
- // Hello! <RTL marker>!Merhaba<LTR marker>
- "Hello! \xE2\x80\x8F!\xd9\x85\xd8\xb1\xd8\xad\xd8\xa8\xd8\xa7\xE2\x80\x8E",
-
- "\xE2\x80\x8E LTR At Start",
- "LTR At End\xE2\x80\x8E",
- "\xE2\x80\x8F RTL At Start",
- "RTL At End\xE2\x80\x8F",
- "\xE2\x80\x8E\xE2\x80\x8E Double LTR \xE2\x80\x8E\xE2\x80\x8E",
- "\xE2\x80\x8F\xE2\x80\x8F Double RTL \xE2\x80\x8F\xE2\x80\x8F",
- "\xE2\x80\x8F\xE2\x80\x8E LTR-RTL LTR-RTL \xE2\x80\x8E\xE2\x80\x8F",
- };
- size_t stringsSize = GPBARRAYSIZE(strings);
- size_t numberUnicodeStrings = 17375;
- nsStrings_ = [[NSMutableArray alloc]
- initWithCapacity:stringsSize + numberUnicodeStrings];
- gpbStrings_ = [[NSMutableArray alloc]
- initWithCapacity:stringsSize + numberUnicodeStrings];
- for (size_t i = 0; i < stringsSize; ++i) {
- size_t length = strlen(strings[i]);
- NSString *nsString = [[NSString alloc] initWithBytes:strings[i]
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- GPBString *gpbString = GPBCreateGPBStringWithUTF8(strings[i], length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
-
- // Generate all UTF8 characters in a variety of strings
- // UTF8-1 - 1 Byte UTF 8 chars
- int length = 0x7F + 1;
- char *buffer = (char *)calloc(length, 1);
- for (int i = 0; i < length; ++i) {
- buffer[i] = (char)i;
- }
- NSString *nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- GPBString *gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
-
- // UTF8-2 - 2 Byte UTF 8 chars
- int pointLength = 0xbf - 0x80 + 1;
- length = pointLength * 2;
- buffer = (char *)calloc(length, 1);
- for (int i = 0xc2; i <= 0xdf; ++i) {
- char *bufferPtr = buffer;
- for (int j = 0x80; j <= 0xbf; ++j) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- free(buffer);
-
- // UTF8-3 - 3 Byte UTF 8 chars
- length = pointLength * 3;
- buffer = (char *)calloc(length, 1);
- for (int i = 0xa0; i <= 0xbf; ++i) {
- char *bufferPtr = buffer;
- for (int j = 0x80; j <= 0xbf; ++j) {
- (*bufferPtr++) = (char)0xE0;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- for (int i = 0xe1; i <= 0xec; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- for (int i = 0x80; i <= 0x9f; ++i) {
- char *bufferPtr = buffer;
- for (int j = 0x80; j <= 0xbf; ++j) {
- (*bufferPtr++) = (char)0xED;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- for (int i = 0xee; i <= 0xef; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- free(buffer);
-
- // UTF8-4 - 4 Byte UTF 8 chars
- length = pointLength * 4;
- buffer = (char *)calloc(length, 1);
- for (int i = 0x90; i <= 0xbf; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)0xF0;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- for (int i = 0xf1; i <= 0xf3; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- for (int k = 0x80; k <= 0xbf; ++k) {
- char *bufferPtr = buffer;
- for (int m = 0x80; m <= 0xbf; ++m) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- (*bufferPtr++) = (char)m;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- }
- for (int i = 0x80; i <= 0x8f; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)0xF4;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- free(buffer);
-}
-
-- (void)tearDown {
- [nsStrings_ release];
- [gpbStrings_ release];
- [super tearDown];
-}
-
-- (void)testLength {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqual([nsString length], [gpbString length], @"%@ %@", nsString,
- gpbString);
- ++i;
- }
-}
-
-- (void)testLengthOfBytesUsingEncoding {
- NSStringEncoding encodings[] = {
- NSUTF8StringEncoding,
- NSASCIIStringEncoding,
- NSISOLatin1StringEncoding,
- NSMacOSRomanStringEncoding,
- NSUTF16StringEncoding,
- NSUTF32StringEncoding,
- };
-
- for (size_t j = 0; j < GPBARRAYSIZE(encodings); ++j) {
- NSStringEncoding testEncoding = encodings[j];
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqual([nsString lengthOfBytesUsingEncoding:testEncoding],
- [gpbString lengthOfBytesUsingEncoding:testEncoding],
- @"%@ %@", nsString, gpbString);
- ++i;
- }
- }
-}
-
-- (void)testHash {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqual([nsString hash], [gpbString hash], @"%@ %@", nsString,
- gpbString);
- ++i;
- }
-}
-
-- (void)testEquality {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqualObjects(nsString, gpbString);
- ++i;
- }
-}
-
-- (void)testCharacterAtIndex {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- NSUInteger length = [nsString length];
- for (size_t j = 0; j < length; ++j) {
- unichar nsChar = [nsString characterAtIndex:j];
- unichar pbChar = [gpbString characterAtIndex:j];
- XCTAssertEqual(nsChar, pbChar, @"%@ %@ %zu", nsString, gpbString, j);
- }
- ++i;
- }
-}
-
-- (void)testCopy {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = [[gpbStrings_[i] copy] autorelease];
- XCTAssertEqualObjects(nsString, gpbString);
- ++i;
- }
-}
-
-- (void)testMutableCopy {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = [[gpbStrings_[i] mutableCopy] autorelease];
- XCTAssertEqualObjects(nsString, gpbString);
- ++i;
- }
-}
-
-- (void)testGetBytes {
- // Do an attempt at a reasonably exhaustive test of get bytes.
- // Get bytes with options other than 0 should always fall through to Apple
- // code so we don't bother testing that path.
- size_t i = 0;
- char pbBuffer[256];
- char nsBuffer[256];
- int count = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- for (int j = 0; j < 100; ++j) {
- // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
- // does not return reliable results if the maxLength argument is 0,
- // or range is 0,0.
- // Radar 16385183
- NSUInteger length = [nsString length];
- NSUInteger maxBufferCount = (arc4random() % (length + 3)) + 1;
- NSUInteger rangeStart = arc4random() % length;
- NSUInteger rangeLength = arc4random() % (length - rangeStart);
-
- NSRange range = NSMakeRange(rangeStart, rangeLength);
-
- NSStringEncoding encodings[] = {
- NSASCIIStringEncoding,
- NSUTF8StringEncoding,
- NSUTF16StringEncoding,
- };
-
- for (size_t k = 0; k < GPBARRAYSIZE(encodings); ++k) {
- NSStringEncoding encoding = encodings[k];
- NSUInteger pbUsedBufferCount = 0;
- NSUInteger nsUsedBufferCount = 0;
- NSRange pbLeftOver = NSMakeRange(0, 0);
- NSRange nsLeftOver = NSMakeRange(0, 0);
-
- BOOL pbGotBytes = [gpbString getBytes:pbBuffer
- maxLength:maxBufferCount
- usedLength:&pbUsedBufferCount
- encoding:encoding
- options:0
- range:range
- remainingRange:&pbLeftOver];
- BOOL nsGotBytes = [nsString getBytes:nsBuffer
- maxLength:maxBufferCount
- usedLength:&nsUsedBufferCount
- encoding:encoding
- options:0
- range:range
- remainingRange:&nsLeftOver];
- XCTAssertEqual(
- (bool)pbGotBytes, (bool)nsGotBytes,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- XCTAssertEqual(
- pbUsedBufferCount, nsUsedBufferCount,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- XCTAssertEqual(
- pbLeftOver.location, nsLeftOver.location,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- XCTAssertEqual(
- pbLeftOver.length, nsLeftOver.length,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- ++count;
- }
- }
- ++i;
- }
-}
-
-- (void)testLengthAndGetBytes {
- // This test exists as an attempt to ferret out a bug.
- // http://b/13516532
- size_t i = 0;
- char pbBuffer[256];
- char nsBuffer[256];
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i++];
- NSUInteger nsLength =
- [nsString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- NSUInteger pbLength =
- [gpbString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- XCTAssertEqual(nsLength, pbLength, @"%@ %@", nsString, gpbString);
- NSUInteger pbUsedBufferCount = 0;
- NSUInteger nsUsedBufferCount = 0;
- NSRange pbLeftOver = NSMakeRange(0, 0);
- NSRange nsLeftOver = NSMakeRange(0, 0);
- NSRange range = NSMakeRange(0, [gpbString length]);
- BOOL pbGotBytes = [gpbString getBytes:pbBuffer
- maxLength:sizeof(pbBuffer)
- usedLength:&pbUsedBufferCount
- encoding:NSUTF8StringEncoding
- options:0
- range:range
- remainingRange:&pbLeftOver];
- BOOL nsGotBytes = [nsString getBytes:nsBuffer
- maxLength:sizeof(nsBuffer)
- usedLength:&nsUsedBufferCount
- encoding:NSUTF8StringEncoding
- options:0
- range:range
- remainingRange:&nsLeftOver];
- XCTAssertTrue(pbGotBytes, @"%@", gpbString);
- XCTAssertTrue(nsGotBytes, @"%@", nsString);
- XCTAssertEqual(pbUsedBufferCount, pbLength, @"%@", gpbString);
- XCTAssertEqual(nsUsedBufferCount, nsLength, @"%@", nsString);
- }
-}
-
-@end
diff --git a/objectivec/Tests/GPBSwiftTests.swift b/objectivec/Tests/GPBSwiftTests.swift
index 5fbe74fd..36ed2a62 100644
--- a/objectivec/Tests/GPBSwiftTests.swift
+++ b/objectivec/Tests/GPBSwiftTests.swift
@@ -83,8 +83,8 @@ class GPBBridgeTests: XCTestCase {
XCTAssertEqual(msg.repeatedInt32Array.valueAtIndex(0), Int32(300))
XCTAssertEqual(msg.repeatedInt32Array.valueAtIndex(1), Int32(301))
XCTAssertEqual(msg.repeatedStringArray.count, Int(2))
- XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(0) as! String, "mno")
- XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(1) as! String, "pqr")
+ XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(0) as? String, "mno")
+ XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(1) as? String, "pqr")
XCTAssertEqual(msg.repeatedEnumArray.count, UInt(2))
XCTAssertEqual(msg.repeatedEnumArray.valueAtIndex(0), Message2_Enum.Bar.rawValue)
XCTAssertEqual(msg.repeatedEnumArray.valueAtIndex(1), Message2_Enum.Baz.rawValue)
@@ -96,8 +96,8 @@ class GPBBridgeTests: XCTestCase {
XCTAssertTrue(msg.mapInt32Int32.valueForKey(501, value:&intValue))
XCTAssertEqual(intValue, Int32(401))
XCTAssertEqual(msg.mapStringString.count, Int(2))
- XCTAssertEqual(msg.mapStringString.objectForKey("bar") as! String, "foo")
- XCTAssertEqual(msg.mapStringString.objectForKey("xyz") as! String, "abc")
+ XCTAssertEqual(msg.mapStringString.objectForKey("bar") as? String, "foo")
+ XCTAssertEqual(msg.mapStringString.objectForKey("xyz") as? String, "abc")
XCTAssertEqual(msg.mapInt32Enum.count, UInt(2))
XCTAssertTrue(msg.mapInt32Enum.valueForKey(600, value:&intValue))
XCTAssertEqual(intValue, Message2_Enum.Bar.rawValue)
@@ -173,8 +173,8 @@ class GPBBridgeTests: XCTestCase {
XCTAssertEqual(msg.repeatedInt32Array.valueAtIndex(0), Int32(300))
XCTAssertEqual(msg.repeatedInt32Array.valueAtIndex(1), Int32(301))
XCTAssertEqual(msg.repeatedStringArray.count, Int(2))
- XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(0) as! String, "mno")
- XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(1) as! String, "pqr")
+ XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(0) as? String, "mno")
+ XCTAssertEqual(msg.repeatedStringArray.objectAtIndex(1) as? String, "pqr")
XCTAssertEqual(msg.repeatedInt64Array.count, UInt(0))
XCTAssertEqual(msg.repeatedEnumArray.count, UInt(2))
XCTAssertEqual(msg.repeatedEnumArray.valueAtIndex(0), Message3_Enum.Bar.rawValue)
@@ -189,8 +189,8 @@ class GPBBridgeTests: XCTestCase {
XCTAssertTrue(msg.mapInt32Int32.valueForKey(501, value:&intValue))
XCTAssertEqual(intValue, Int32(401))
XCTAssertEqual(msg.mapStringString.count, Int(2))
- XCTAssertEqual(msg.mapStringString.objectForKey("bar") as! String, "foo")
- XCTAssertEqual(msg.mapStringString.objectForKey("xyz") as! String, "abc")
+ XCTAssertEqual(msg.mapStringString.objectForKey("bar") as? String, "foo")
+ XCTAssertEqual(msg.mapStringString.objectForKey("xyz") as? String, "abc")
XCTAssertEqual(msg.mapInt32Enum.count, UInt(2))
XCTAssertTrue(msg.mapInt32Enum.valueForKey(600, value:&intValue))
XCTAssertEqual(intValue, Message2_Enum.Bar.rawValue)
diff --git a/objectivec/Tests/GPBWireFormatTests.m b/objectivec/Tests/GPBWireFormatTests.m
index 3fab20b7..2a406f11 100644
--- a/objectivec/Tests/GPBWireFormatTests.m
+++ b/objectivec/Tests/GPBWireFormatTests.m
@@ -190,7 +190,6 @@ const int kUnknownTypeId = 1550055;
TestMessageSetExtension1* message = [TestMessageSetExtension1 message];
message.i = 123;
item.message = [message data];
- raw.itemArray = [NSMutableArray array];
[raw.itemArray addObject:item];
}
diff --git a/objectivec/Tests/UnitTests-Info.plist b/objectivec/Tests/UnitTests-Info.plist
index 65013556..460a7d93 100644
--- a/objectivec/Tests/UnitTests-Info.plist
+++ b/objectivec/Tests/UnitTests-Info.plist
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
- <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
diff --git a/objectivec/Tests/iOSTestHarness/Info.plist b/objectivec/Tests/iOSTestHarness/Info.plist
index 31ab1578..24bd333d 100644
--- a/objectivec/Tests/iOSTestHarness/Info.plist
+++ b/objectivec/Tests/iOSTestHarness/Info.plist
@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
- <string>com.google.${PRODUCT_NAME:rfc1034identifier}</string>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
diff --git a/objectivec/google/protobuf/Any.pbobjc.h b/objectivec/google/protobuf/Any.pbobjc.h
index 6511b8ed..9866b5b1 100644
--- a/objectivec/google/protobuf/Any.pbobjc.h
+++ b/objectivec/google/protobuf/Any.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
@@ -34,6 +34,7 @@ typedef GPB_ENUM(GPBAny_FieldNumber) {
// `Any` contains an arbitrary serialized message along with a URL
// that describes the type of the serialized message.
//
+//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
@@ -54,8 +55,8 @@ typedef GPB_ENUM(GPBAny_FieldNumber) {
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
-// `value` which holds the custom JSON in addition to the the `@type`
-// field. Example (for message [google.protobuf.Duration][google.protobuf.Duration]):
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
@@ -72,7 +73,7 @@ typedef GPB_ENUM(GPBAny_FieldNumber) {
// * If no schema is provided, `https` is assumed.
// * The last segment of the URL's path must represent the fully
// qualified name of the type (as in `path/google.protobuf.Duration`).
-// * An HTTP GET on the URL must yield a [google.protobuf.Type][google.protobuf.Type]
+// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
diff --git a/objectivec/google/protobuf/Api.pbobjc.h b/objectivec/google/protobuf/Api.pbobjc.h
index d6292cba..c3cf8e94 100644
--- a/objectivec/google/protobuf/Api.pbobjc.h
+++ b/objectivec/google/protobuf/Api.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
@@ -174,7 +174,6 @@ typedef GPB_ENUM(GPBMixin_FieldNumber) {
//
// package google.storage.v2;
// service Storage {
-// // (-- see AccessControl.GetAcl --)
// rpc GetAcl(GetAclRequest) returns (Acl);
//
// // Get a data record.
diff --git a/objectivec/google/protobuf/Descriptor.pbobjc.h b/objectivec/google/protobuf/Descriptor.pbobjc.h
index 70cb7447..9c43cfd2 100644
--- a/objectivec/google/protobuf/Descriptor.pbobjc.h
+++ b/objectivec/google/protobuf/Descriptor.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
@@ -1153,6 +1153,58 @@ typedef GPB_ENUM(GPBSourceCodeInfo_Location_FieldNumber) {
@end
+#pragma mark - GPBGeneratedCodeInfo
+
+typedef GPB_ENUM(GPBGeneratedCodeInfo_FieldNumber) {
+ GPBGeneratedCodeInfo_FieldNumber_AnnotationArray = 1,
+};
+
+// Describes the relationship between generated code and its original source
+// file. A GeneratedCodeInfo message is associated with only one generated
+// source file, but may contain references to different source .proto files.
+@interface GPBGeneratedCodeInfo : GPBMessage
+
+// An Annotation connects some span of text in generated code to an element
+// of its generating .proto file.
+// |annotationArray| contains |GPBGeneratedCodeInfo_Annotation|
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *annotationArray;
+@property(nonatomic, readonly) NSUInteger annotationArray_Count;
+
+@end
+
+#pragma mark - GPBGeneratedCodeInfo_Annotation
+
+typedef GPB_ENUM(GPBGeneratedCodeInfo_Annotation_FieldNumber) {
+ GPBGeneratedCodeInfo_Annotation_FieldNumber_PathArray = 1,
+ GPBGeneratedCodeInfo_Annotation_FieldNumber_SourceFile = 2,
+ GPBGeneratedCodeInfo_Annotation_FieldNumber_Begin = 3,
+ GPBGeneratedCodeInfo_Annotation_FieldNumber_End = 4,
+};
+
+@interface GPBGeneratedCodeInfo_Annotation : GPBMessage
+
+// Identifies the element in the original source .proto file. This field
+// is formatted the same as SourceCodeInfo.Location.path.
+@property(nonatomic, readwrite, strong, null_resettable) GPBInt32Array *pathArray;
+@property(nonatomic, readonly) NSUInteger pathArray_Count;
+
+// Identifies the filesystem path to the original source .proto.
+@property(nonatomic, readwrite) BOOL hasSourceFile;
+@property(nonatomic, readwrite, copy, null_resettable) NSString *sourceFile;
+
+// Identifies the starting offset in bytes in the generated code
+// that relates to the identified object.
+@property(nonatomic, readwrite) BOOL hasBegin;
+@property(nonatomic, readwrite) int32_t begin;
+
+// Identifies the ending offset in bytes in the generated code that
+// relates to the identified offset. The end offset should be one past
+// the last relevant byte (so the length of the text = end - begin).
+@property(nonatomic, readwrite) BOOL hasEnd;
+@property(nonatomic, readwrite) int32_t end;
+
+@end
+
NS_ASSUME_NONNULL_END
CF_EXTERN_C_END
diff --git a/objectivec/google/protobuf/Descriptor.pbobjc.m b/objectivec/google/protobuf/Descriptor.pbobjc.m
index e3e44c48..40309893 100644
--- a/objectivec/google/protobuf/Descriptor.pbobjc.m
+++ b/objectivec/google/protobuf/Descriptor.pbobjc.m
@@ -1434,7 +1434,11 @@ typedef struct GPBFileOptions__storage_ {
.offset = offsetof(GPBFileOptions__storage_, javananoUseDeprecatedPackage),
.defaultValue.valueBool = NO,
.dataTypeSpecific.className = NULL,
+ #if GPBOBJC_INCLUDE_FIELD_OPTIONS
+ .fieldOptions = "\000\000\000\002\030\001",
+ #else
.fieldOptions = NULL,
+ #endif // GPBOBJC_INCLUDE_FIELD_OPTIONS
},
{
.name = "uninterpretedOptionArray",
@@ -2441,5 +2445,150 @@ typedef struct GPBSourceCodeInfo_Location__storage_ {
@end
+#pragma mark - GPBGeneratedCodeInfo
+
+@implementation GPBGeneratedCodeInfo
+
+@dynamic annotationArray, annotationArray_Count;
+
+typedef struct GPBGeneratedCodeInfo__storage_ {
+ uint32_t _has_storage_[1];
+ NSMutableArray *annotationArray;
+} GPBGeneratedCodeInfo__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+ static GPBDescriptor *descriptor = nil;
+ if (!descriptor) {
+ static GPBMessageFieldDescription fields[] = {
+ {
+ .name = "annotationArray",
+ .number = GPBGeneratedCodeInfo_FieldNumber_AnnotationArray,
+ .hasIndex = GPBNoHasBit,
+ .flags = GPBFieldRepeated,
+ .dataType = GPBDataTypeMessage,
+ .offset = offsetof(GPBGeneratedCodeInfo__storage_, annotationArray),
+ .defaultValue.valueMessage = nil,
+ .dataTypeSpecific.className = GPBStringifySymbol(GPBGeneratedCodeInfo_Annotation),
+ .fieldOptions = NULL,
+ },
+ };
+ GPBDescriptor *localDescriptor =
+ [GPBDescriptor allocDescriptorForClass:[GPBGeneratedCodeInfo class]
+ rootClass:[GPBDescriptorRoot class]
+ file:GPBDescriptorRoot_FileDescriptor()
+ fields:fields
+ fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription)
+ oneofs:NULL
+ oneofCount:0
+ enums:NULL
+ enumCount:0
+ ranges:NULL
+ rangeCount:0
+ storageSize:sizeof(GPBGeneratedCodeInfo__storage_)
+ wireFormat:NO];
+ NSAssert(descriptor == nil, @"Startup recursed!");
+ descriptor = localDescriptor;
+ }
+ return descriptor;
+}
+
+@end
+
+#pragma mark - GPBGeneratedCodeInfo_Annotation
+
+@implementation GPBGeneratedCodeInfo_Annotation
+
+@dynamic pathArray, pathArray_Count;
+@dynamic hasSourceFile, sourceFile;
+@dynamic hasBegin, begin;
+@dynamic hasEnd, end;
+
+typedef struct GPBGeneratedCodeInfo_Annotation__storage_ {
+ uint32_t _has_storage_[1];
+ int32_t begin;
+ int32_t end;
+ GPBInt32Array *pathArray;
+ NSString *sourceFile;
+} GPBGeneratedCodeInfo_Annotation__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+ static GPBDescriptor *descriptor = nil;
+ if (!descriptor) {
+ static GPBMessageFieldDescription fields[] = {
+ {
+ .name = "pathArray",
+ .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_PathArray,
+ .hasIndex = GPBNoHasBit,
+ .flags = GPBFieldRepeated | GPBFieldPacked,
+ .dataType = GPBDataTypeInt32,
+ .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, pathArray),
+ .defaultValue.valueMessage = nil,
+ .dataTypeSpecific.className = NULL,
+ #if GPBOBJC_INCLUDE_FIELD_OPTIONS
+ .fieldOptions = "\000\000\000\002\020\001",
+ #else
+ .fieldOptions = NULL,
+ #endif // GPBOBJC_INCLUDE_FIELD_OPTIONS
+ },
+ {
+ .name = "sourceFile",
+ .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_SourceFile,
+ .hasIndex = 1,
+ .flags = GPBFieldOptional,
+ .dataType = GPBDataTypeString,
+ .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, sourceFile),
+ .defaultValue.valueString = nil,
+ .dataTypeSpecific.className = NULL,
+ .fieldOptions = NULL,
+ },
+ {
+ .name = "begin",
+ .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_Begin,
+ .hasIndex = 2,
+ .flags = GPBFieldOptional,
+ .dataType = GPBDataTypeInt32,
+ .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, begin),
+ .defaultValue.valueInt32 = 0,
+ .dataTypeSpecific.className = NULL,
+ .fieldOptions = NULL,
+ },
+ {
+ .name = "end",
+ .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_End,
+ .hasIndex = 3,
+ .flags = GPBFieldOptional,
+ .dataType = GPBDataTypeInt32,
+ .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, end),
+ .defaultValue.valueInt32 = 0,
+ .dataTypeSpecific.className = NULL,
+ .fieldOptions = NULL,
+ },
+ };
+ GPBDescriptor *localDescriptor =
+ [GPBDescriptor allocDescriptorForClass:[GPBGeneratedCodeInfo_Annotation class]
+ rootClass:[GPBDescriptorRoot class]
+ file:GPBDescriptorRoot_FileDescriptor()
+ fields:fields
+ fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription)
+ oneofs:NULL
+ oneofCount:0
+ enums:NULL
+ enumCount:0
+ ranges:NULL
+ rangeCount:0
+ storageSize:sizeof(GPBGeneratedCodeInfo_Annotation__storage_)
+ wireFormat:NO];
+ NSAssert(descriptor == nil, @"Startup recursed!");
+ descriptor = localDescriptor;
+ }
+ return descriptor;
+}
+
+@end
+
// @@protoc_insertion_point(global_scope)
diff --git a/objectivec/google/protobuf/Duration.pbobjc.h b/objectivec/google/protobuf/Duration.pbobjc.h
index 9ecba197..b592640b 100644
--- a/objectivec/google/protobuf/Duration.pbobjc.h
+++ b/objectivec/google/protobuf/Duration.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Empty.pbobjc.h b/objectivec/google/protobuf/Empty.pbobjc.h
index ff7f384a..bace614d 100644
--- a/objectivec/google/protobuf/Empty.pbobjc.h
+++ b/objectivec/google/protobuf/Empty.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/FieldMask.pbobjc.h b/objectivec/google/protobuf/FieldMask.pbobjc.h
index 0cdbd574..4e4ec387 100644
--- a/objectivec/google/protobuf/FieldMask.pbobjc.h
+++ b/objectivec/google/protobuf/FieldMask.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
@@ -61,7 +61,7 @@ typedef GPB_ENUM(GPBFieldMask_FieldNumber) {
// z: 8
//
// The result will not contain specific values for fields x,y and z
-// (there value will be set to the default, and omitted in proto text
+// (their value will be set to the default, and omitted in proto text
// output):
//
//
diff --git a/objectivec/google/protobuf/SourceContext.pbobjc.h b/objectivec/google/protobuf/SourceContext.pbobjc.h
index 6bc57149..8480db1d 100644
--- a/objectivec/google/protobuf/SourceContext.pbobjc.h
+++ b/objectivec/google/protobuf/SourceContext.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Struct.pbobjc.h b/objectivec/google/protobuf/Struct.pbobjc.h
index 7513cf90..f40414fa 100644
--- a/objectivec/google/protobuf/Struct.pbobjc.h
+++ b/objectivec/google/protobuf/Struct.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Timestamp.pbobjc.h b/objectivec/google/protobuf/Timestamp.pbobjc.h
index a1f68bd2..79b24ec6 100644
--- a/objectivec/google/protobuf/Timestamp.pbobjc.h
+++ b/objectivec/google/protobuf/Timestamp.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
diff --git a/objectivec/google/protobuf/Type.pbobjc.h b/objectivec/google/protobuf/Type.pbobjc.h
index 44ef05c5..e4c7a251 100644
--- a/objectivec/google/protobuf/Type.pbobjc.h
+++ b/objectivec/google/protobuf/Type.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
@@ -18,13 +18,13 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Enum GPBSyntax
-// Syntax specifies the syntax in which a service element was defined.
+// The syntax in which a protocol buffer element is defined.
typedef GPB_ENUM(GPBSyntax) {
GPBSyntax_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
- // Syntax "proto2"
+ // Syntax `proto2`.
GPBSyntax_SyntaxProto2 = 0,
- // Syntax "proto3"
+ // Syntax `proto3`.
GPBSyntax_SyntaxProto3 = 1,
};
@@ -34,7 +34,7 @@ BOOL GPBSyntax_IsValidValue(int32_t value);
#pragma mark - Enum GPBField_Kind
-// Kind represents a basic field type.
+// Basic field types.
typedef GPB_ENUM(GPBField_Kind) {
GPBField_Kind_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
// Field type unknown.
@@ -67,7 +67,7 @@ typedef GPB_ENUM(GPBField_Kind) {
// Field type string.
GPBField_Kind_TypeString = 9,
- // Field type group (deprecated proto2 type)
+ // Field type group. Proto2 syntax only, and deprecated.
GPBField_Kind_TypeGroup = 10,
// Field type message.
@@ -101,17 +101,16 @@ BOOL GPBField_Kind_IsValidValue(int32_t value);
#pragma mark - Enum GPBField_Cardinality
-// Cardinality represents whether a field is optional, required, or
-// repeated.
+// Whether a field is optional, required, or repeated.
typedef GPB_ENUM(GPBField_Cardinality) {
GPBField_Cardinality_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
- // The field cardinality is unknown. Typically an error condition.
+ // For fields with unknown cardinality.
GPBField_Cardinality_CardinalityUnknown = 0,
// For optional fields.
GPBField_Cardinality_CardinalityOptional = 1,
- // For required fields. Not used for proto3.
+ // For required fields. Proto2 syntax only.
GPBField_Cardinality_CardinalityRequired = 2,
// For repeated fields.
@@ -144,7 +143,7 @@ typedef GPB_ENUM(GPBType_FieldNumber) {
GPBType_FieldNumber_Syntax = 6,
};
-// A light-weight descriptor for a proto message type.
+// A protocol buffer message type.
@interface GPBType : GPBMessage
// The fully qualified message name.
@@ -155,12 +154,12 @@ typedef GPB_ENUM(GPBType_FieldNumber) {
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *fieldsArray;
@property(nonatomic, readonly) NSUInteger fieldsArray_Count;
-// The list of oneof definitions.
+// The list of types appearing in `oneof` definitions in this type.
// |oneofsArray| contains |NSString|
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *oneofsArray;
@property(nonatomic, readonly) NSUInteger oneofsArray_Count;
-// The proto options.
+// The protocol buffer options.
// |optionsArray| contains |GPBOption|
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
@property(nonatomic, readonly) NSUInteger optionsArray_Count;
@@ -189,41 +188,46 @@ typedef GPB_ENUM(GPBField_FieldNumber) {
GPBField_FieldNumber_Packed = 8,
GPBField_FieldNumber_OptionsArray = 9,
GPBField_FieldNumber_JsonName = 10,
+ GPBField_FieldNumber_DefaultValue = 11,
};
-// Field represents a single field of a message type.
+// A single field of a message type.
@interface GPBField : GPBMessage
-// The field kind.
+// The field type.
@property(nonatomic, readwrite) GPBField_Kind kind;
-// The field cardinality, i.e. optional/required/repeated.
+// The field cardinality.
@property(nonatomic, readwrite) GPBField_Cardinality cardinality;
-// The proto field number.
+// The field number.
@property(nonatomic, readwrite) int32_t number;
// The field name.
@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
-// The type URL (without the scheme) when the type is MESSAGE or ENUM,
-// such as `type.googleapis.com/google.protobuf.Empty`.
+// The field type URL, without the scheme, for message or enumeration
+// types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
@property(nonatomic, readwrite, copy, null_resettable) NSString *typeURL;
-// Index in Type.oneofs. Starts at 1. Zero means no oneof mapping.
+// The index of the field type in `Type.oneofs`, for message or enumeration
+// types. The first type has index 1; zero means the type is not in the list.
@property(nonatomic, readwrite) int32_t oneofIndex;
// Whether to use alternative packed wire representation.
@property(nonatomic, readwrite) BOOL packed;
-// The proto options.
+// The protocol buffer options.
// |optionsArray| contains |GPBOption|
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
@property(nonatomic, readonly) NSUInteger optionsArray_Count;
-// The JSON name for this field.
+// The field JSON name.
@property(nonatomic, readwrite, copy, null_resettable) NSString *jsonName;
+// The string value of the default value of this field. Proto2 syntax only.
+@property(nonatomic, readwrite, copy, null_resettable) NSString *defaultValue;
+
@end
int32_t GPBField_Kind_RawValue(GPBField *message);
@@ -253,7 +257,7 @@ typedef GPB_ENUM(GPBEnum_FieldNumber) {
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *enumvalueArray;
@property(nonatomic, readonly) NSUInteger enumvalueArray_Count;
-// Proto options for the enum type.
+// Protocol buffer options.
// |optionsArray| contains |GPBOption|
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
@property(nonatomic, readonly) NSUInteger optionsArray_Count;
@@ -287,7 +291,7 @@ typedef GPB_ENUM(GPBEnumValue_FieldNumber) {
// Enum value number.
@property(nonatomic, readwrite) int32_t number;
-// Proto options for the enum value.
+// Protocol buffer options.
// |optionsArray| contains |GPBOption|
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *optionsArray;
@property(nonatomic, readonly) NSUInteger optionsArray_Count;
@@ -301,13 +305,14 @@ typedef GPB_ENUM(GPBOption_FieldNumber) {
GPBOption_FieldNumber_Value = 2,
};
-// Proto option attached to messages/fields/enums etc.
+// A protocol buffer option, which can be attached to a message, field,
+// enumeration, etc.
@interface GPBOption : GPBMessage
-// Proto option name.
+// The option's name. For example, `"java_package"`.
@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
-// Proto option value.
+// The option's value. For example, `"com.google.protobuf"`.
@property(nonatomic, readwrite) BOOL hasValue;
@property(nonatomic, readwrite, strong, null_resettable) GPBAny *value;
diff --git a/objectivec/google/protobuf/Type.pbobjc.m b/objectivec/google/protobuf/Type.pbobjc.m
index 4485e904..b4e0a5f6 100644
--- a/objectivec/google/protobuf/Type.pbobjc.m
+++ b/objectivec/google/protobuf/Type.pbobjc.m
@@ -208,6 +208,7 @@ void SetGPBType_Syntax_RawValue(GPBType *message, int32_t value) {
@dynamic packed;
@dynamic optionsArray, optionsArray_Count;
@dynamic jsonName;
+@dynamic defaultValue;
typedef struct GPBField__storage_ {
uint32_t _has_storage_[1];
@@ -220,6 +221,7 @@ typedef struct GPBField__storage_ {
NSString *typeURL;
NSMutableArray *optionsArray;
NSString *jsonName;
+ NSString *defaultValue;
} GPBField__storage_;
// This method is threadsafe because it is initially called
@@ -327,6 +329,17 @@ typedef struct GPBField__storage_ {
.dataTypeSpecific.className = NULL,
.fieldOptions = NULL,
},
+ {
+ .name = "defaultValue",
+ .number = GPBField_FieldNumber_DefaultValue,
+ .hasIndex = 9,
+ .flags = GPBFieldOptional,
+ .dataType = GPBDataTypeString,
+ .offset = offsetof(GPBField__storage_, defaultValue),
+ .defaultValue.valueString = nil,
+ .dataTypeSpecific.className = NULL,
+ .fieldOptions = NULL,
+ },
};
static GPBMessageEnumDescription enums[] = {
{ .enumDescriptorFunc = GPBField_Kind_EnumDescriptor },
diff --git a/objectivec/google/protobuf/Wrappers.pbobjc.h b/objectivec/google/protobuf/Wrappers.pbobjc.h
index a9a260bc..580945c4 100644
--- a/objectivec/google/protobuf/Wrappers.pbobjc.h
+++ b/objectivec/google/protobuf/Wrappers.pbobjc.h
@@ -4,7 +4,7 @@
#import "GPBProtocolBuffers.h"
#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000
-#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.
+#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.
#endif
// @@protoc_insertion_point(imports)
diff --git a/post_process_dist.sh b/post_process_dist.sh
index 6d5b85d3..82736bd0 100755
--- a/post_process_dist.sh
+++ b/post_process_dist.sh
@@ -27,7 +27,7 @@ fi
set -ex
-LANGUAGES="cpp csharp java javanano objectivec python ruby"
+LANGUAGES="cpp csharp java javanano js objectivec python ruby"
BASENAME=`basename $1 .tar.gz`
VERSION=${BASENAME:9}
diff --git a/protobuf.bzl b/protobuf.bzl
index 86c6f251..f674a6c6 100644
--- a/protobuf.bzl
+++ b/protobuf.bzl
@@ -1,13 +1,19 @@
# -*- mode: python; -*- PYTHON-PREPROCESSING-REQUIRED
+def _GetPath(ctx, path):
+ if ctx.label.workspace_root:
+ return ctx.label.workspace_root + '/' + path
+ else:
+ return path
+
def _GenDir(ctx):
if not ctx.attr.includes:
- return ""
+ return ctx.label.workspace_root
if not ctx.attr.includes[0]:
- return ctx.label.package
+ return _GetPath(ctx, ctx.label.package)
if not ctx.label.package:
- return ctx.attr.includes[0]
- return ctx.label.package + '/' + ctx.attr.includes[0]
+ return _GetPath(ctx, ctx.attr.includes[0])
+ return _GetPath(ctx, ctx.label.package + '/' + ctx.attr.includes[0])
def _CcOuts(srcs):
return [s[:-len(".proto")] + ".pb.h" for s in srcs] + \
@@ -36,8 +42,6 @@ def _RelativeOutputPath(path, include):
return path[len(PACKAGE_NAME)+1:]
-
-
def _proto_gen_impl(ctx):
"""General implementation for generating protos"""
srcs = ctx.files.srcs
@@ -141,6 +145,7 @@ def cc_proto_library(
deps=[s + "_genproto" for s in deps],
includes=includes,
protoc=protoc,
+ visibility=["//visibility:public"],
)
# An empty cc_library to make rule dependency consistent.
native.cc_library(
@@ -157,6 +162,7 @@ def cc_proto_library(
protoc=protoc,
gen_cc=1,
outs=outs,
+ visibility=["//visibility:public"],
)
if default_runtime and not default_runtime in cc_libs:
@@ -245,6 +251,7 @@ def py_proto_library(
protoc=protoc,
gen_py=1,
outs=outs,
+ visibility=["//visibility:public"],
)
if include != None:
diff --git a/protoc-artifacts/README.md b/protoc-artifacts/README.md
index 06091cfe..4320f651 100644
--- a/protoc-artifacts/README.md
+++ b/protoc-artifacts/README.md
@@ -55,7 +55,7 @@ read [this page](http://central.sonatype.org/pages/apache-maven.html) on how to
configure GPG and Sonatype account.
You need to perform the deployment for every platform that you want to
-suppport. DO NOT close the staging repository until you have done the
+support. DO NOT close the staging repository until you have done the
deployment for all platforms. Currently the following platforms are supported:
- Linux (x86_32 and x86_64)
- Windows (x86_32 and x86_64) with
@@ -134,7 +134,7 @@ stored:
```
### Tested build environments
-We have succesfully built artifacts on the following environments:
+We have successfully built artifacts on the following environments:
- Linux x86_32 and x86_64:
- Centos 6.6 (within Docker 1.6.1)
- Ubuntu 14.04.2 64-bit
diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml
index cdf17707..e9728a06 100644
--- a/protoc-artifacts/pom.xml
+++ b/protoc-artifacts/pom.xml
@@ -10,7 +10,7 @@
</parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protoc</artifactId>
- <version>3.0.0-beta-1</version>
+ <version>3.0.0-beta-2</version>
<packaging>pom</packaging>
<name>Protobuf Compiler</name>
<description>
diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py
index ed88480f..533821c1 100755
--- a/python/google/protobuf/__init__.py
+++ b/python/google/protobuf/__init__.py
@@ -30,4 +30,4 @@
# Copyright 2007 Google Inc. All Rights Reserved.
-__version__ = '3.0.0a4'
+__version__ = '3.0.0b2'
diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py
index 2bf36532..5f613c88 100755
--- a/python/google/protobuf/descriptor.py
+++ b/python/google/protobuf/descriptor.py
@@ -786,25 +786,33 @@ class FileDescriptor(DescriptorBase):
message_types_by_name: Dict of message names of their descriptors.
enum_types_by_name: Dict of enum names and their descriptors.
extensions_by_name: Dict of extension names and their descriptors.
+ pool: the DescriptorPool this descriptor belongs to. When not passed to the
+ constructor, the global default pool is used.
"""
if _USE_C_DESCRIPTORS:
_C_DESCRIPTOR_CLASS = _message.FileDescriptor
def __new__(cls, name, package, options=None, serialized_pb=None,
- dependencies=None, syntax=None):
+ dependencies=None, syntax=None, pool=None):
# FileDescriptor() is called from various places, not only from generated
# files, to register dynamic proto files and messages.
if serialized_pb:
+ # TODO(amauryfa): use the pool passed as argument. This will work only
+ # for C++-implemented DescriptorPools.
return _message.default_pool.AddSerializedFile(serialized_pb)
else:
return super(FileDescriptor, cls).__new__(cls)
def __init__(self, name, package, options=None, serialized_pb=None,
- dependencies=None, syntax=None):
+ dependencies=None, syntax=None, pool=None):
"""Constructor."""
super(FileDescriptor, self).__init__(options, 'FileOptions')
+ if pool is None:
+ from google.protobuf import descriptor_pool
+ pool = descriptor_pool.Default()
+ self.pool = pool
self.message_types_by_name = {}
self.name = name
self.package = package
diff --git a/python/google/protobuf/descriptor_database.py b/python/google/protobuf/descriptor_database.py
index b10021e9..1333f996 100644
--- a/python/google/protobuf/descriptor_database.py
+++ b/python/google/protobuf/descriptor_database.py
@@ -65,6 +65,7 @@ class DescriptorDatabase(object):
raise DescriptorDatabaseConflictingDefinitionError(
'%s already added, but with different descriptor.' % proto_name)
+ # Add the top-level Message, Enum and Extension descriptors to the index.
package = file_desc_proto.package
for message in file_desc_proto.message_type:
self._file_desc_protos_by_symbol.update(
@@ -72,6 +73,9 @@ class DescriptorDatabase(object):
for enum in file_desc_proto.enum_type:
self._file_desc_protos_by_symbol[
'.'.join((package, enum.name))] = file_desc_proto
+ for extension in file_desc_proto.extension:
+ self._file_desc_protos_by_symbol[
+ '.'.join((package, extension.name))] = file_desc_proto
def FindFileByName(self, name):
"""Finds the file descriptor proto by file name.
diff --git a/python/google/protobuf/descriptor_pool.py b/python/google/protobuf/descriptor_pool.py
index 6a1b4b5e..3e80795c 100644
--- a/python/google/protobuf/descriptor_pool.py
+++ b/python/google/protobuf/descriptor_pool.py
@@ -83,6 +83,12 @@ def _NormalizeFullyQualifiedName(name):
class DescriptorPool(object):
"""A collection of protobufs dynamically constructed by descriptor protos."""
+ if _USE_C_DESCRIPTORS:
+
+ def __new__(cls, descriptor_db=None):
+ # pylint: disable=protected-access
+ return descriptor._message.DescriptorPool(descriptor_db)
+
def __init__(self, descriptor_db=None):
"""Initializes a Pool of proto buffs.
@@ -264,6 +270,39 @@ class DescriptorPool(object):
self.FindFileContainingSymbol(full_name)
return self._enum_descriptors[full_name]
+ def FindFieldByName(self, full_name):
+ """Loads the named field descriptor from the pool.
+
+ Args:
+ full_name: The full name of the field descriptor to load.
+
+ Returns:
+ The field descriptor for the named field.
+ """
+ full_name = _NormalizeFullyQualifiedName(full_name)
+ message_name, _, field_name = full_name.rpartition('.')
+ message_descriptor = self.FindMessageTypeByName(message_name)
+ return message_descriptor.fields_by_name[field_name]
+
+ def FindExtensionByName(self, full_name):
+ """Loads the named extension descriptor from the pool.
+
+ Args:
+ full_name: The full name of the extension descriptor to load.
+
+ Returns:
+ A FieldDescriptor, describing the named extension.
+ """
+ full_name = _NormalizeFullyQualifiedName(full_name)
+ message_name, _, extension_name = full_name.rpartition('.')
+ try:
+ # Most extensions are nested inside a message.
+ scope = self.FindMessageTypeByName(message_name)
+ except KeyError:
+ # Some extensions are defined at file scope.
+ scope = self.FindFileContainingSymbol(full_name)
+ return scope.extensions_by_name[extension_name]
+
def _ConvertFileProtoToFileDescriptor(self, file_proto):
"""Creates a FileDescriptor from a proto or returns a cached copy.
@@ -282,6 +321,7 @@ class DescriptorPool(object):
direct_deps = [self.FindFileByName(n) for n in file_proto.dependency]
file_descriptor = descriptor.FileDescriptor(
+ pool=self,
name=file_proto.name,
package=file_proto.package,
syntax=file_proto.syntax,
@@ -598,10 +638,24 @@ class DescriptorPool(object):
field_desc.default_value = text_encoding.CUnescape(
field_proto.default_value)
else:
+ # All other types are of the "int" type.
field_desc.default_value = int(field_proto.default_value)
else:
field_desc.has_default_value = False
- field_desc.default_value = None
+ if (field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE or
+ field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT):
+ field_desc.default_value = 0.0
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING:
+ field_desc.default_value = u''
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL:
+ field_desc.default_value = False
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM:
+ field_desc.default_value = field_desc.enum_type.values[0].number
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES:
+ field_desc.default_value = b''
+ else:
+ # All other types are of the "int" type.
+ field_desc.default_value = 0
field_desc.type = field_proto.type
@@ -680,3 +734,16 @@ class DescriptorPool(object):
def _PrefixWithDot(name):
return name if name.startswith('.') else '.%s' % name
+
+
+if _USE_C_DESCRIPTORS:
+ # TODO(amauryfa): This pool could be constructed from Python code, when we
+ # support a flag like 'use_cpp_generated_pool=True'.
+ # pylint: disable=protected-access
+ _DEFAULT = descriptor._message.default_pool
+else:
+ _DEFAULT = DescriptorPool()
+
+
+def Default():
+ return _DEFAULT
diff --git a/python/google/protobuf/internal/any_test.proto b/python/google/protobuf/internal/any_test.proto
new file mode 100644
index 00000000..cd641ca0
--- /dev/null
+++ b/python/google/protobuf/internal/any_test.proto
@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: jieluo@google.com (Jie Luo)
+
+syntax = "proto3";
+
+package google.protobuf.internal;
+
+import "google/protobuf/any.proto";
+
+message TestAny {
+ google.protobuf.Any value = 1;
+ int32 int_value = 2;
+}
diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py
index 9c8275eb..97cdd848 100755
--- a/python/google/protobuf/internal/containers.py
+++ b/python/google/protobuf/internal/containers.py
@@ -464,6 +464,9 @@ class ScalarMap(MutableMapping):
return val
def __contains__(self, item):
+ # We check the key's type to match the strong-typing flavor of the API.
+ # Also this makes it easier to match the behavior of the C++ implementation.
+ self._key_checker.CheckValue(item)
return item in self._values
# We need to override this explicitly, because our defaultdict-like behavior
@@ -491,10 +494,20 @@ class ScalarMap(MutableMapping):
def __iter__(self):
return iter(self._values)
+ def __repr__(self):
+ return repr(self._values)
+
def MergeFrom(self, other):
self._values.update(other._values)
self._message_listener.Modified()
+ def InvalidateIterators(self):
+ # It appears that the only way to reliably invalidate iterators to
+ # self._values is to ensure that its size changes.
+ original = self._values
+ self._values = original.copy()
+ original[None] = None
+
# This is defined in the abstract base, but we can do it much more cheaply.
def clear(self):
self._values.clear()
@@ -576,12 +589,22 @@ class MessageMap(MutableMapping):
def __iter__(self):
return iter(self._values)
+ def __repr__(self):
+ return repr(self._values)
+
def MergeFrom(self, other):
for key in other:
self[key].MergeFrom(other[key])
# self._message_listener.Modified() not required here, because
# mutations to submessages already propagate.
+ def InvalidateIterators(self):
+ # It appears that the only way to reliably invalidate iterators to
+ # self._values is to ensure that its size changes.
+ original = self._values
+ self._values = original.copy()
+ original[None] = None
+
# This is defined in the abstract base, but we can do it much more cheaply.
def clear(self):
self._values.clear()
diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py
index da9a78db..f1d6bf99 100644
--- a/python/google/protobuf/internal/descriptor_pool_test.py
+++ b/python/google/protobuf/internal/descriptor_pool_test.py
@@ -40,6 +40,8 @@ try:
import unittest2 as unittest
except ImportError:
import unittest
+from google.protobuf import unittest_import_pb2
+from google.protobuf import unittest_import_public_pb2
from google.protobuf import unittest_pb2
from google.protobuf import descriptor_pb2
from google.protobuf.internal import api_implementation
@@ -51,13 +53,17 @@ from google.protobuf.internal import test_util
from google.protobuf import descriptor
from google.protobuf import descriptor_database
from google.protobuf import descriptor_pool
+from google.protobuf import message_factory
from google.protobuf import symbol_database
class DescriptorPoolTest(unittest.TestCase):
+ def CreatePool(self):
+ return descriptor_pool.DescriptorPool()
+
def setUp(self):
- self.pool = descriptor_pool.DescriptorPool()
+ self.pool = self.CreatePool()
self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString(
factory_test1_pb2.DESCRIPTOR.serialized_pb)
self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString(
@@ -89,7 +95,7 @@ class DescriptorPoolTest(unittest.TestCase):
'google.protobuf.python.internal.Factory1Message')
self.assertIsInstance(file_desc1, descriptor.FileDescriptor)
self.assertEqual('google/protobuf/internal/factory_test1.proto',
- file_desc1.name)
+ file_desc1.name)
self.assertEqual('google.protobuf.python.internal', file_desc1.package)
self.assertIn('Factory1Message', file_desc1.message_types_by_name)
@@ -97,7 +103,7 @@ class DescriptorPoolTest(unittest.TestCase):
'google.protobuf.python.internal.Factory2Message')
self.assertIsInstance(file_desc2, descriptor.FileDescriptor)
self.assertEqual('google/protobuf/internal/factory_test2.proto',
- file_desc2.name)
+ file_desc2.name)
self.assertEqual('google.protobuf.python.internal', file_desc2.package)
self.assertIn('Factory2Message', file_desc2.message_types_by_name)
@@ -111,7 +117,7 @@ class DescriptorPoolTest(unittest.TestCase):
self.assertIsInstance(msg1, descriptor.Descriptor)
self.assertEqual('Factory1Message', msg1.name)
self.assertEqual('google.protobuf.python.internal.Factory1Message',
- msg1.full_name)
+ msg1.full_name)
self.assertEqual(None, msg1.containing_type)
nested_msg1 = msg1.nested_types[0]
@@ -132,7 +138,7 @@ class DescriptorPoolTest(unittest.TestCase):
self.assertIsInstance(msg2, descriptor.Descriptor)
self.assertEqual('Factory2Message', msg2.name)
self.assertEqual('google.protobuf.python.internal.Factory2Message',
- msg2.full_name)
+ msg2.full_name)
self.assertIsNone(msg2.containing_type)
nested_msg2 = msg2.nested_types[0]
@@ -223,6 +229,37 @@ class DescriptorPoolTest(unittest.TestCase):
with self.assertRaises(KeyError):
self.pool.FindEnumTypeByName('Does not exist')
+ def testFindFieldByName(self):
+ field = self.pool.FindFieldByName(
+ 'google.protobuf.python.internal.Factory1Message.list_value')
+ self.assertEqual(field.name, 'list_value')
+ self.assertEqual(field.label, field.LABEL_REPEATED)
+ with self.assertRaises(KeyError):
+ self.pool.FindFieldByName('Does not exist')
+
+ def testFindExtensionByName(self):
+ # An extension defined in a message.
+ extension = self.pool.FindExtensionByName(
+ 'google.protobuf.python.internal.Factory2Message.one_more_field')
+ self.assertEqual(extension.name, 'one_more_field')
+ # An extension defined at file scope.
+ extension = self.pool.FindExtensionByName(
+ 'google.protobuf.python.internal.another_field')
+ self.assertEqual(extension.name, 'another_field')
+ self.assertEqual(extension.number, 1002)
+ with self.assertRaises(KeyError):
+ self.pool.FindFieldByName('Does not exist')
+
+ def testExtensionsAreNotFields(self):
+ with self.assertRaises(KeyError):
+ self.pool.FindFieldByName('google.protobuf.python.internal.another_field')
+ with self.assertRaises(KeyError):
+ self.pool.FindFieldByName(
+ 'google.protobuf.python.internal.Factory2Message.one_more_field')
+ with self.assertRaises(KeyError):
+ self.pool.FindExtensionByName(
+ 'google.protobuf.python.internal.Factory1Message.list_value')
+
def testUserDefinedDB(self):
db = descriptor_database.DescriptorDatabase()
self.pool = descriptor_pool.DescriptorPool(db)
@@ -231,8 +268,7 @@ class DescriptorPoolTest(unittest.TestCase):
self.testFindMessageTypeByName()
def testAddSerializedFile(self):
- db = descriptor_database.DescriptorDatabase()
- self.pool = descriptor_pool.DescriptorPool(db)
+ self.pool = descriptor_pool.DescriptorPool()
self.pool.AddSerializedFile(self.factory_test1_fd.SerializeToString())
self.pool.AddSerializedFile(self.factory_test2_fd.SerializeToString())
self.testFindMessageTypeByName()
@@ -274,6 +310,56 @@ class DescriptorPoolTest(unittest.TestCase):
'google/protobuf/internal/descriptor_pool_test1.proto')
_CheckDefaultValue(file_descriptor)
+ def testDefaultValueForCustomMessages(self):
+ """Check the value returned by non-existent fields."""
+ def _CheckValueAndType(value, expected_value, expected_type):
+ self.assertEqual(value, expected_value)
+ self.assertIsInstance(value, expected_type)
+
+ def _CheckDefaultValues(msg):
+ try:
+ int64 = long
+ except NameError: # Python3
+ int64 = int
+ try:
+ unicode_type = unicode
+ except NameError: # Python3
+ unicode_type = str
+ _CheckValueAndType(msg.optional_int32, 0, int)
+ _CheckValueAndType(msg.optional_uint64, 0, (int64, int))
+ _CheckValueAndType(msg.optional_float, 0, (float, int))
+ _CheckValueAndType(msg.optional_double, 0, (float, int))
+ _CheckValueAndType(msg.optional_bool, False, bool)
+ _CheckValueAndType(msg.optional_string, u'', unicode_type)
+ _CheckValueAndType(msg.optional_bytes, b'', bytes)
+ _CheckValueAndType(msg.optional_nested_enum, msg.FOO, int)
+ # First for the generated message
+ _CheckDefaultValues(unittest_pb2.TestAllTypes())
+ # Then for a message built with from the DescriptorPool.
+ pool = descriptor_pool.DescriptorPool()
+ pool.Add(descriptor_pb2.FileDescriptorProto.FromString(
+ unittest_import_public_pb2.DESCRIPTOR.serialized_pb))
+ pool.Add(descriptor_pb2.FileDescriptorProto.FromString(
+ unittest_import_pb2.DESCRIPTOR.serialized_pb))
+ pool.Add(descriptor_pb2.FileDescriptorProto.FromString(
+ unittest_pb2.DESCRIPTOR.serialized_pb))
+ message_class = message_factory.MessageFactory(pool).GetPrototype(
+ pool.FindMessageTypeByName(
+ unittest_pb2.TestAllTypes.DESCRIPTOR.full_name))
+ _CheckDefaultValues(message_class())
+
+
+@unittest.skipIf(api_implementation.Type() != 'cpp',
+ 'explicit tests of the C++ implementation')
+class CppDescriptorPoolTest(DescriptorPoolTest):
+ # TODO(amauryfa): remove when descriptor_pool.DescriptorPool() creates true
+ # C++ descriptor pool object for C++ implementation.
+
+ def CreatePool(self):
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf.pyext import _message
+ return _message.DescriptorPool()
+
class ProtoFile(object):
@@ -468,6 +554,8 @@ class AddDescriptorTest(unittest.TestCase):
pool.FindFileContainingSymbol(
prefix + 'protobuf_unittest.TestAllTypes.NestedMessage').name)
+ @unittest.skipIf(api_implementation.Type() == 'cpp',
+ 'With the cpp implementation, Add() must be called first')
def testMessage(self):
self._TestMessage('')
self._TestMessage('.')
@@ -502,10 +590,14 @@ class AddDescriptorTest(unittest.TestCase):
pool.FindFileContainingSymbol(
prefix + 'protobuf_unittest.TestAllTypes.NestedEnum').name)
+ @unittest.skipIf(api_implementation.Type() == 'cpp',
+ 'With the cpp implementation, Add() must be called first')
def testEnum(self):
self._TestEnum('')
self._TestEnum('.')
+ @unittest.skipIf(api_implementation.Type() == 'cpp',
+ 'With the cpp implementation, Add() must be called first')
def testFile(self):
pool = descriptor_pool.DescriptorPool()
pool.AddFileDescriptor(unittest_pb2.DESCRIPTOR)
@@ -520,6 +612,76 @@ class AddDescriptorTest(unittest.TestCase):
pool.FindFileContainingSymbol(
'protobuf_unittest.TestAllTypes')
+ def _GetDescriptorPoolClass(self):
+ # Test with both implementations of descriptor pools.
+ if api_implementation.Type() == 'cpp':
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf.pyext import _message
+ return _message.DescriptorPool
+ else:
+ return descriptor_pool.DescriptorPool
+
+ def testEmptyDescriptorPool(self):
+ # Check that an empty DescriptorPool() contains no message.
+ pool = self._GetDescriptorPoolClass()()
+ proto_file_name = descriptor_pb2.DESCRIPTOR.name
+ self.assertRaises(KeyError, pool.FindFileByName, proto_file_name)
+ # Add the above file to the pool
+ file_descriptor = descriptor_pb2.FileDescriptorProto()
+ descriptor_pb2.DESCRIPTOR.CopyToProto(file_descriptor)
+ pool.Add(file_descriptor)
+ # Now it exists.
+ self.assertTrue(pool.FindFileByName(proto_file_name))
+
+ def testCustomDescriptorPool(self):
+ # Create a new pool, and add a file descriptor.
+ pool = self._GetDescriptorPoolClass()()
+ file_desc = descriptor_pb2.FileDescriptorProto(
+ name='some/file.proto', package='package')
+ file_desc.message_type.add(name='Message')
+ pool.Add(file_desc)
+ self.assertEqual(pool.FindFileByName('some/file.proto').name,
+ 'some/file.proto')
+ self.assertEqual(pool.FindMessageTypeByName('package.Message').name,
+ 'Message')
+
+
+@unittest.skipIf(
+ api_implementation.Type() != 'cpp',
+ 'default_pool is only supported by the C++ implementation')
+class DefaultPoolTest(unittest.TestCase):
+
+ def testFindMethods(self):
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf.pyext import _message
+ pool = _message.default_pool
+ self.assertIs(
+ pool.FindFileByName('google/protobuf/unittest.proto'),
+ unittest_pb2.DESCRIPTOR)
+ self.assertIs(
+ pool.FindMessageTypeByName('protobuf_unittest.TestAllTypes'),
+ unittest_pb2.TestAllTypes.DESCRIPTOR)
+ self.assertIs(
+ pool.FindFieldByName('protobuf_unittest.TestAllTypes.optional_int32'),
+ unittest_pb2.TestAllTypes.DESCRIPTOR.fields_by_name['optional_int32'])
+ self.assertIs(
+ pool.FindExtensionByName('protobuf_unittest.optional_int32_extension'),
+ unittest_pb2.DESCRIPTOR.extensions_by_name['optional_int32_extension'])
+ self.assertIs(
+ pool.FindEnumTypeByName('protobuf_unittest.ForeignEnum'),
+ unittest_pb2.ForeignEnum.DESCRIPTOR)
+ self.assertIs(
+ pool.FindOneofByName('protobuf_unittest.TestAllTypes.oneof_field'),
+ unittest_pb2.TestAllTypes.DESCRIPTOR.oneofs_by_name['oneof_field'])
+
+ def testAddFileDescriptor(self):
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf.pyext import _message
+ pool = _message.default_pool
+ file_desc = descriptor_pb2.FileDescriptorProto(name='some/file.proto')
+ pool.Add(file_desc)
+ pool.AddSerializedFile(file_desc.SerializeToString())
+
TEST1_FILE = ProtoFile(
'google/protobuf/internal/descriptor_pool_test1.proto',
diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py
index 99afee63..fee09a56 100755
--- a/python/google/protobuf/internal/descriptor_test.py
+++ b/python/google/protobuf/internal/descriptor_test.py
@@ -47,6 +47,7 @@ from google.protobuf import descriptor_pb2
from google.protobuf.internal import api_implementation
from google.protobuf.internal import test_util
from google.protobuf import descriptor
+from google.protobuf import descriptor_pool
from google.protobuf import symbol_database
from google.protobuf import text_format
@@ -75,9 +76,9 @@ class DescriptorTest(unittest.TestCase):
enum_proto.value.add(name='FOREIGN_BAR', number=5)
enum_proto.value.add(name='FOREIGN_BAZ', number=6)
- descriptor_pool = symbol_database.Default().pool
- descriptor_pool.Add(file_proto)
- self.my_file = descriptor_pool.FindFileByName(file_proto.name)
+ self.pool = self.GetDescriptorPool()
+ self.pool.Add(file_proto)
+ self.my_file = self.pool.FindFileByName(file_proto.name)
self.my_message = self.my_file.message_types_by_name[message_proto.name]
self.my_enum = self.my_message.enum_types_by_name[enum_proto.name]
@@ -97,6 +98,9 @@ class DescriptorTest(unittest.TestCase):
self.my_method
])
+ def GetDescriptorPool(self):
+ return symbol_database.Default().pool
+
def testEnumValueName(self):
self.assertEqual(self.my_message.EnumValueName('ForeignEnum', 4),
'FOREIGN_FOO')
@@ -393,6 +397,9 @@ class DescriptorTest(unittest.TestCase):
def testFileDescriptor(self):
self.assertEqual(self.my_file.name, 'some/filename/some.proto')
self.assertEqual(self.my_file.package, 'protobuf_unittest')
+ self.assertEqual(self.my_file.pool, self.pool)
+ # Generated modules also belong to the default pool.
+ self.assertEqual(unittest_pb2.DESCRIPTOR.pool, descriptor_pool.Default())
@unittest.skipIf(
api_implementation.Type() != 'cpp' or api_implementation.Version() != 2,
@@ -407,6 +414,13 @@ class DescriptorTest(unittest.TestCase):
message_descriptor.fields.append(None)
+class NewDescriptorTest(DescriptorTest):
+ """Redo the same tests as above, but with a separate DescriptorPool."""
+
+ def GetDescriptorPool(self):
+ return descriptor_pool.DescriptorPool()
+
+
class GeneratedDescriptorTest(unittest.TestCase):
"""Tests for the properties of descriptors in generated code."""
diff --git a/python/google/protobuf/internal/json_format_test.py b/python/google/protobuf/internal/json_format_test.py
index 69197865..49e96a46 100644
--- a/python/google/protobuf/internal/json_format_test.py
+++ b/python/google/protobuf/internal/json_format_test.py
@@ -42,6 +42,13 @@ try:
import unittest2 as unittest
except ImportError:
import unittest
+from google.protobuf import any_pb2
+from google.protobuf import duration_pb2
+from google.protobuf import field_mask_pb2
+from google.protobuf import struct_pb2
+from google.protobuf import timestamp_pb2
+from google.protobuf import wrappers_pb2
+from google.protobuf.internal import well_known_types
from google.protobuf import json_format
from google.protobuf.util import json_format_proto3_pb2
@@ -269,15 +276,15 @@ class JsonFormatTest(JsonFormatBase):
'}'))
parsed_message = json_format_proto3_pb2.TestTimestamp()
self.CheckParseBack(message, parsed_message)
- text = (r'{"value": "1972-01-01T01:00:00.01+08:00",'
+ text = (r'{"value": "1970-01-01T00:00:00.01+08:00",'
r'"repeatedValue":['
- r' "1972-01-01T01:00:00.01+08:30",'
- r' "1972-01-01T01:00:00.01-01:23"]}')
+ r' "1970-01-01T00:00:00.01+08:30",'
+ r' "1970-01-01T00:00:00.01-01:23"]}')
json_format.Parse(text, parsed_message)
- self.assertEqual(parsed_message.value.seconds, 63104400)
+ self.assertEqual(parsed_message.value.seconds, -8 * 3600)
self.assertEqual(parsed_message.value.nanos, 10000000)
- self.assertEqual(parsed_message.repeated_value[0].seconds, 63106200)
- self.assertEqual(parsed_message.repeated_value[1].seconds, 63070620)
+ self.assertEqual(parsed_message.repeated_value[0].seconds, -8.5 * 3600)
+ self.assertEqual(parsed_message.repeated_value[1].seconds, 3600 + 23 * 60)
def testDurationMessage(self):
message = json_format_proto3_pb2.TestDuration()
@@ -325,6 +332,7 @@ class JsonFormatTest(JsonFormatBase):
message.bytes_value.value = b''
message.repeated_bool_value.add().value = True
message.repeated_bool_value.add().value = False
+ message.repeated_int32_value.add()
self.assertEqual(
json.loads(json_format.MessageToJson(message, True)),
json.loads('{\n'
@@ -333,7 +341,7 @@ class JsonFormatTest(JsonFormatBase):
' "stringValue": "",'
' "bytesValue": "",'
' "repeatedBoolValue": [true, false],'
- ' "repeatedInt32Value": [],'
+ ' "repeatedInt32Value": [0],'
' "repeatedUint32Value": [],'
' "repeatedFloatValue": [],'
' "repeatedDoubleValue": [],'
@@ -345,11 +353,192 @@ class JsonFormatTest(JsonFormatBase):
parsed_message = json_format_proto3_pb2.TestWrapper()
self.CheckParseBack(message, parsed_message)
+ def testStructMessage(self):
+ message = json_format_proto3_pb2.TestStruct()
+ message.value['name'] = 'Jim'
+ message.value['age'] = 10
+ message.value['attend'] = True
+ message.value['email'] = None
+ message.value.get_or_create_struct('address')['city'] = 'SFO'
+ message.value['address']['house_number'] = 1024
+ struct_list = message.value.get_or_create_list('list')
+ struct_list.extend([6, 'seven', True, False, None])
+ struct_list.add_struct()['subkey2'] = 9
+ message.repeated_value.add()['age'] = 11
+ message.repeated_value.add()
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, False)),
+ json.loads(
+ '{'
+ ' "value": {'
+ ' "address": {'
+ ' "city": "SFO", '
+ ' "house_number": 1024'
+ ' }, '
+ ' "age": 10, '
+ ' "name": "Jim", '
+ ' "attend": true, '
+ ' "email": null, '
+ ' "list": [6, "seven", true, false, null, {"subkey2": 9}]'
+ ' },'
+ ' "repeatedValue": [{"age": 11}, {}]'
+ '}'))
+ parsed_message = json_format_proto3_pb2.TestStruct()
+ self.CheckParseBack(message, parsed_message)
+
+ def testValueMessage(self):
+ message = json_format_proto3_pb2.TestValue()
+ message.value.string_value = 'hello'
+ message.repeated_value.add().number_value = 11.1
+ message.repeated_value.add().bool_value = False
+ message.repeated_value.add().null_value = 0
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, False)),
+ json.loads(
+ '{'
+ ' "value": "hello",'
+ ' "repeatedValue": [11.1, false, null]'
+ '}'))
+ parsed_message = json_format_proto3_pb2.TestValue()
+ self.CheckParseBack(message, parsed_message)
+ # Can't parse back if the Value message is not set.
+ message.repeated_value.add()
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, False)),
+ json.loads(
+ '{'
+ ' "value": "hello",'
+ ' "repeatedValue": [11.1, false, null, null]'
+ '}'))
+
+ def testListValueMessage(self):
+ message = json_format_proto3_pb2.TestListValue()
+ message.value.values.add().number_value = 11.1
+ message.value.values.add().null_value = 0
+ message.value.values.add().bool_value = True
+ message.value.values.add().string_value = 'hello'
+ message.value.values.add().struct_value['name'] = 'Jim'
+ message.repeated_value.add().values.add().number_value = 1
+ message.repeated_value.add()
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, False)),
+ json.loads(
+ '{"value": [11.1, null, true, "hello", {"name": "Jim"}]\n,'
+ '"repeatedValue": [[1], []]}'))
+ parsed_message = json_format_proto3_pb2.TestListValue()
+ self.CheckParseBack(message, parsed_message)
+
+ def testAnyMessage(self):
+ message = json_format_proto3_pb2.TestAny()
+ value1 = json_format_proto3_pb2.MessageType()
+ value2 = json_format_proto3_pb2.MessageType()
+ value1.value = 1234
+ value2.value = 5678
+ message.value.Pack(value1)
+ message.repeated_value.add().Pack(value1)
+ message.repeated_value.add().Pack(value2)
+ message.repeated_value.add()
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "repeatedValue": [ {\n'
+ ' "@type": "type.googleapis.com/proto3.MessageType",\n'
+ ' "value": 1234\n'
+ ' }, {\n'
+ ' "@type": "type.googleapis.com/proto3.MessageType",\n'
+ ' "value": 5678\n'
+ ' },\n'
+ ' {}],\n'
+ ' "value": {\n'
+ ' "@type": "type.googleapis.com/proto3.MessageType",\n'
+ ' "value": 1234\n'
+ ' }\n'
+ '}\n'))
+ parsed_message = json_format_proto3_pb2.TestAny()
+ self.CheckParseBack(message, parsed_message)
+
+ def testWellKnownInAnyMessage(self):
+ message = any_pb2.Any()
+ int32_value = wrappers_pb2.Int32Value()
+ int32_value.value = 1234
+ message.Pack(int32_value)
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "@type": \"type.googleapis.com/google.protobuf.Int32Value\",\n'
+ ' "value": 1234\n'
+ '}\n'))
+ parsed_message = any_pb2.Any()
+ self.CheckParseBack(message, parsed_message)
+
+ timestamp = timestamp_pb2.Timestamp()
+ message.Pack(timestamp)
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "@type": "type.googleapis.com/google.protobuf.Timestamp",\n'
+ ' "value": "1970-01-01T00:00:00Z"\n'
+ '}\n'))
+ self.CheckParseBack(message, parsed_message)
+
+ duration = duration_pb2.Duration()
+ duration.seconds = 1
+ message.Pack(duration)
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "@type": "type.googleapis.com/google.protobuf.Duration",\n'
+ ' "value": "1s"\n'
+ '}\n'))
+ self.CheckParseBack(message, parsed_message)
+
+ field_mask = field_mask_pb2.FieldMask()
+ field_mask.paths.append('foo.bar')
+ field_mask.paths.append('bar')
+ message.Pack(field_mask)
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "@type": "type.googleapis.com/google.protobuf.FieldMask",\n'
+ ' "value": "foo.bar,bar"\n'
+ '}\n'))
+ self.CheckParseBack(message, parsed_message)
+
+ struct_message = struct_pb2.Struct()
+ struct_message['name'] = 'Jim'
+ message.Pack(struct_message)
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "@type": "type.googleapis.com/google.protobuf.Struct",\n'
+ ' "value": {"name": "Jim"}\n'
+ '}\n'))
+ self.CheckParseBack(message, parsed_message)
+
+ nested_any = any_pb2.Any()
+ int32_value.value = 5678
+ nested_any.Pack(int32_value)
+ message.Pack(nested_any)
+ self.assertEqual(
+ json.loads(json_format.MessageToJson(message, True)),
+ json.loads(
+ '{\n'
+ ' "@type": "type.googleapis.com/google.protobuf.Any",\n'
+ ' "value": {\n'
+ ' "@type": "type.googleapis.com/google.protobuf.Int32Value",\n'
+ ' "value": 5678\n'
+ ' }\n'
+ '}\n'))
+ self.CheckParseBack(message, parsed_message)
+
def testParseNull(self):
message = json_format_proto3_pb2.TestMessage()
- message.repeated_int32_value.append(1)
- message.repeated_int32_value.append(2)
- message.repeated_int32_value.append(3)
parsed_message = json_format_proto3_pb2.TestMessage()
self.FillAllFields(parsed_message)
json_format.Parse('{"int32Value": null, '
@@ -363,7 +552,7 @@ class JsonFormatTest(JsonFormatBase):
'"bytesValue": null,'
'"messageValue": null,'
'"enumValue": null,'
- '"repeatedInt32Value": [1, 2, null, 3],'
+ '"repeatedInt32Value": null,'
'"repeatedInt64Value": null,'
'"repeatedUint32Value": null,'
'"repeatedUint64Value": null,'
@@ -377,6 +566,13 @@ class JsonFormatTest(JsonFormatBase):
'}',
parsed_message)
self.assertEqual(message, parsed_message)
+ self.assertRaisesRegexp(
+ json_format.ParseError,
+ 'Failed to parse repeatedInt32Value field: '
+ 'null is not allowed to be used as an element in a repeated field.',
+ json_format.Parse,
+ '{"repeatedInt32Value":[1, null]}',
+ parsed_message)
def testNanFloat(self):
message = json_format_proto3_pb2.TestMessage()
@@ -389,7 +585,7 @@ class JsonFormatTest(JsonFormatBase):
def testParseEmptyText(self):
self.CheckError('',
- r'Failed to load JSON: (Expecting value)|(No JSON)')
+ r'Failed to load JSON: (Expecting value)|(No JSON).')
def testParseBadEnumValue(self):
self.CheckError(
@@ -414,7 +610,7 @@ class JsonFormatTest(JsonFormatBase):
if sys.version_info < (2, 7):
return
self.CheckError('{"int32Value": 1,\n"int32Value":2}',
- 'Failed to load JSON: duplicate key int32Value')
+ 'Failed to load JSON: duplicate key int32Value.')
def testInvalidBoolValue(self):
self.CheckError('{"boolValue": 1}',
@@ -431,39 +627,43 @@ class JsonFormatTest(JsonFormatBase):
json_format.Parse, text, message)
self.CheckError('{"int32Value": 012345}',
(r'Failed to load JSON: Expecting \'?,\'? delimiter: '
- r'line 1'))
+ r'line 1.'))
self.CheckError('{"int32Value": 1.0}',
'Failed to parse int32Value field: '
- 'Couldn\'t parse integer: 1.0')
+ 'Couldn\'t parse integer: 1.0.')
self.CheckError('{"int32Value": " 1 "}',
'Failed to parse int32Value field: '
- 'Couldn\'t parse integer: " 1 "')
+ 'Couldn\'t parse integer: " 1 ".')
+ self.CheckError('{"int32Value": "1 "}',
+ 'Failed to parse int32Value field: '
+ 'Couldn\'t parse integer: "1 ".')
self.CheckError('{"int32Value": 12345678901234567890}',
'Failed to parse int32Value field: Value out of range: '
- '12345678901234567890')
+ '12345678901234567890.')
self.CheckError('{"int32Value": 1e5}',
'Failed to parse int32Value field: '
- 'Couldn\'t parse integer: 100000.0')
+ 'Couldn\'t parse integer: 100000.0.')
self.CheckError('{"uint32Value": -1}',
- 'Failed to parse uint32Value field: Value out of range: -1')
+ 'Failed to parse uint32Value field: '
+ 'Value out of range: -1.')
def testInvalidFloatValue(self):
self.CheckError('{"floatValue": "nan"}',
'Failed to parse floatValue field: Couldn\'t '
- 'parse float "nan", use "NaN" instead')
+ 'parse float "nan", use "NaN" instead.')
def testInvalidBytesValue(self):
self.CheckError('{"bytesValue": "AQI"}',
- 'Failed to parse bytesValue field: Incorrect padding')
+ 'Failed to parse bytesValue field: Incorrect padding.')
self.CheckError('{"bytesValue": "AQI*"}',
- 'Failed to parse bytesValue field: Incorrect padding')
+ 'Failed to parse bytesValue field: Incorrect padding.')
def testInvalidMap(self):
message = json_format_proto3_pb2.TestMap()
text = '{"int32Map": {"null": 2, "2": 3}}'
self.assertRaisesRegexp(
json_format.ParseError,
- 'Failed to parse int32Map field: Couldn\'t parse integer: "null"',
+ 'Failed to parse int32Map field: invalid literal',
json_format.Parse, text, message)
text = '{"int32Map": {1: 2, "2": 3}}'
self.assertRaisesRegexp(
@@ -474,7 +674,7 @@ class JsonFormatTest(JsonFormatBase):
text = '{"boolMap": {"null": 1}}'
self.assertRaisesRegexp(
json_format.ParseError,
- 'Failed to parse boolMap field: Expect "true" or "false", not null.',
+ 'Failed to parse boolMap field: Expected "true" or "false", not null.',
json_format.Parse, text, message)
if sys.version_info < (2, 7):
return
@@ -490,30 +690,29 @@ class JsonFormatTest(JsonFormatBase):
self.assertRaisesRegexp(
json_format.ParseError,
'time data \'10000-01-01T00:00:00\' does not match'
- ' format \'%Y-%m-%dT%H:%M:%S\'',
+ ' format \'%Y-%m-%dT%H:%M:%S\'.',
json_format.Parse, text, message)
text = '{"value": "1970-01-01T00:00:00.0123456789012Z"}'
self.assertRaisesRegexp(
- json_format.ParseError,
- 'Failed to parse value field: Failed to parse Timestamp: '
+ well_known_types.ParseError,
'nanos 0123456789012 more than 9 fractional digits.',
json_format.Parse, text, message)
text = '{"value": "1972-01-01T01:00:00.01+08"}'
self.assertRaisesRegexp(
- json_format.ParseError,
- (r'Failed to parse value field: Invalid timezone offset value: \+08'),
+ well_known_types.ParseError,
+ (r'Invalid timezone offset value: \+08.'),
json_format.Parse, text, message)
# Time smaller than minimum time.
text = '{"value": "0000-01-01T00:00:00Z"}'
self.assertRaisesRegexp(
json_format.ParseError,
- 'Failed to parse value field: year is out of range',
+ 'Failed to parse value field: year is out of range.',
json_format.Parse, text, message)
# Time bigger than maxinum time.
message.value.seconds = 253402300800
self.assertRaisesRegexp(
- json_format.SerializeToJsonError,
- 'Failed to serialize value field: year is out of range',
+ OverflowError,
+ 'date value out of range',
json_format.MessageToJson, message)
def testInvalidOneof(self):
@@ -525,6 +724,45 @@ class JsonFormatTest(JsonFormatBase):
' should not have multiple "oneof_value" oneof fields.',
json_format.Parse, text, message)
+ def testInvalidListValue(self):
+ message = json_format_proto3_pb2.TestListValue()
+ text = '{"value": 1234}'
+ self.assertRaisesRegexp(
+ json_format.ParseError,
+ r'Failed to parse value field: ListValue must be in \[\] which is 1234',
+ json_format.Parse, text, message)
+
+ def testInvalidStruct(self):
+ message = json_format_proto3_pb2.TestStruct()
+ text = '{"value": 1234}'
+ self.assertRaisesRegexp(
+ json_format.ParseError,
+ 'Failed to parse value field: Struct must be in a dict which is 1234',
+ json_format.Parse, text, message)
+
+ def testInvalidAny(self):
+ message = any_pb2.Any()
+ text = '{"@type": "type.googleapis.com/google.protobuf.Int32Value"}'
+ self.assertRaisesRegexp(
+ KeyError,
+ 'value',
+ json_format.Parse, text, message)
+ text = '{"value": 1234}'
+ self.assertRaisesRegexp(
+ json_format.ParseError,
+ '@type is missing when parsing any message.',
+ json_format.Parse, text, message)
+ text = '{"@type": "type.googleapis.com/MessageNotExist", "value": 1234}'
+ self.assertRaisesRegexp(
+ TypeError,
+ 'Can not find message descriptor by type_url: '
+ 'type.googleapis.com/MessageNotExist.',
+ json_format.Parse, text, message)
+ # Only last part is to be used.
+ text = (r'{"@type": "incorrect.googleapis.com/google.protobuf.Int32Value",'
+ r'"value": 1234}')
+ json_format.Parse(text, message)
+
if __name__ == '__main__':
unittest.main()
diff --git a/python/google/protobuf/internal/message_factory_test.py b/python/google/protobuf/internal/message_factory_test.py
index d760b898..2fbe5ea7 100644
--- a/python/google/protobuf/internal/message_factory_test.py
+++ b/python/google/protobuf/internal/message_factory_test.py
@@ -45,6 +45,7 @@ from google.protobuf import descriptor_database
from google.protobuf import descriptor_pool
from google.protobuf import message_factory
+
class MessageFactoryTest(unittest.TestCase):
def setUp(self):
@@ -104,8 +105,8 @@ class MessageFactoryTest(unittest.TestCase):
def testGetMessages(self):
# performed twice because multiple calls with the same input must be allowed
for _ in range(2):
- messages = message_factory.GetMessages([self.factory_test2_fd,
- self.factory_test1_fd])
+ messages = message_factory.GetMessages([self.factory_test1_fd,
+ self.factory_test2_fd])
self.assertTrue(
set(['google.protobuf.python.internal.Factory2Message',
'google.protobuf.python.internal.Factory1Message'],
@@ -116,7 +117,7 @@ class MessageFactoryTest(unittest.TestCase):
set(['google.protobuf.python.internal.Factory2Message.one_more_field',
'google.protobuf.python.internal.another_field'],
).issubset(
- set(messages['google.protobuf.python.internal.Factory1Message']
+ set(messages['google.protobuf.python.internal.Factory1Message']
._extensions_by_name.keys())))
factory_msg1 = messages['google.protobuf.python.internal.Factory1Message']
msg1 = messages['google.protobuf.python.internal.Factory1Message']()
diff --git a/python/google/protobuf/internal/message_set_extensions.proto b/python/google/protobuf/internal/message_set_extensions.proto
index 702c8d07..14e5f193 100644
--- a/python/google/protobuf/internal/message_set_extensions.proto
+++ b/python/google/protobuf/internal/message_set_extensions.proto
@@ -54,6 +54,14 @@ message TestMessageSetExtension2 {
optional string str = 25;
}
+message TestMessageSetExtension3 {
+ optional string text = 35;
+}
+
+extend TestMessageSet {
+ optional TestMessageSetExtension3 message_set_extension3 = 98418655;
+}
+
// This message was used to generate
// //net/proto2/python/internal/testdata/message_set_message, but is commented
// out since it must not actually exist in code, to simulate an "unknown"
diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py
index 13c3caa6..d03f2d25 100755
--- a/python/google/protobuf/internal/message_test.py
+++ b/python/google/protobuf/internal/message_test.py
@@ -60,6 +60,7 @@ from google.protobuf.internal import _parameterized
from google.protobuf import map_unittest_pb2
from google.protobuf import unittest_pb2
from google.protobuf import unittest_proto3_arena_pb2
+from google.protobuf.internal import any_test_pb2
from google.protobuf.internal import api_implementation
from google.protobuf.internal import packed_field_test_pb2
from google.protobuf.internal import test_util
@@ -1279,12 +1280,13 @@ class Proto3Test(unittest.TestCase):
self.assertIsInstance(msg.map_string_string['abc'], six.text_type)
- # Accessing an unset key still throws TypeError of the type of the key
+ # Accessing an unset key still throws TypeError if the type of the key
# is incorrect.
with self.assertRaises(TypeError):
msg.map_string_string[123]
- self.assertFalse(123 in msg.map_string_string)
+ with self.assertRaises(TypeError):
+ 123 in msg.map_string_string
def testMapGet(self):
# Need to test that get() properly returns the default, even though the dict
@@ -1591,31 +1593,49 @@ class Proto3Test(unittest.TestCase):
# For the C++ implementation this tests the correctness of
# ScalarMapContainer::Release()
msg = map_unittest_pb2.TestMap()
- map = msg.map_int32_int32
+ int32_map = msg.map_int32_int32
- map[2] = 4
- map[3] = 6
- map[4] = 8
+ int32_map[2] = 4
+ int32_map[3] = 6
+ int32_map[4] = 8
msg.ClearField('map_int32_int32')
+ self.assertEqual(b'', msg.SerializeToString())
matching_dict = {2: 4, 3: 6, 4: 8}
- self.assertMapIterEquals(map.items(), matching_dict)
+ self.assertMapIterEquals(int32_map.items(), matching_dict)
- def testMapIterValidAfterFieldCleared(self):
- # Map iterator needs to work even if field is cleared.
+ def testMessageMapValidAfterFieldCleared(self):
+ # Map needs to work even if field is cleared.
# For the C++ implementation this tests the correctness of
# ScalarMapContainer::Release()
msg = map_unittest_pb2.TestMap()
+ int32_foreign_message = msg.map_int32_foreign_message
- msg.map_int32_int32[2] = 4
- msg.map_int32_int32[3] = 6
- msg.map_int32_int32[4] = 8
+ int32_foreign_message[2].c = 5
- it = msg.map_int32_int32.items()
+ msg.ClearField('map_int32_foreign_message')
+ self.assertEqual(b'', msg.SerializeToString())
+ self.assertTrue(2 in int32_foreign_message.keys())
+
+ def testMapIterInvalidatedByClearField(self):
+ # Map iterator is invalidated when field is cleared.
+ # But this case does need to not crash the interpreter.
+ # For the C++ implementation this tests the correctness of
+ # ScalarMapContainer::Release()
+ msg = map_unittest_pb2.TestMap()
+
+ it = iter(msg.map_int32_int32)
msg.ClearField('map_int32_int32')
- matching_dict = {2: 4, 3: 6, 4: 8}
- self.assertMapIterEquals(it, matching_dict)
+ with self.assertRaises(RuntimeError):
+ for _ in it:
+ pass
+
+ it = iter(msg.map_int32_foreign_message)
+ msg.ClearField('map_int32_foreign_message')
+ with self.assertRaises(RuntimeError):
+ for _ in it:
+ pass
def testMapDelete(self):
msg = map_unittest_pb2.TestMap()
@@ -1646,6 +1666,37 @@ class Proto3Test(unittest.TestCase):
msg.map_string_foreign_message['foo'].c = 5
self.assertEqual(0, len(msg.FindInitializationErrors()))
+ def testAnyMessage(self):
+ # Creates and sets message.
+ msg = any_test_pb2.TestAny()
+ msg_descriptor = msg.DESCRIPTOR
+ all_types = unittest_pb2.TestAllTypes()
+ all_descriptor = all_types.DESCRIPTOR
+ all_types.repeated_string.append(u'\u00fc\ua71f')
+ # Packs to Any.
+ msg.value.Pack(all_types)
+ self.assertEqual(msg.value.type_url,
+ 'type.googleapis.com/%s' % all_descriptor.full_name)
+ self.assertEqual(msg.value.value,
+ all_types.SerializeToString())
+ # Tests Is() method.
+ self.assertTrue(msg.value.Is(all_descriptor))
+ self.assertFalse(msg.value.Is(msg_descriptor))
+ # Unpacks Any.
+ unpacked_message = unittest_pb2.TestAllTypes()
+ self.assertTrue(msg.value.Unpack(unpacked_message))
+ self.assertEqual(all_types, unpacked_message)
+ # Unpacks to different type.
+ self.assertFalse(msg.value.Unpack(msg))
+ # Only Any messages have Pack method.
+ try:
+ msg.Pack(all_types)
+ except AttributeError:
+ pass
+ else:
+ raise AttributeError('%s should not have Pack method.' %
+ msg_descriptor.full_name)
+
class ValidTypeNamesTest(unittest.TestCase):
diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py
index 2b87f704..87f60666 100755
--- a/python/google/protobuf/internal/python_message.py
+++ b/python/google/protobuf/internal/python_message.py
@@ -65,6 +65,7 @@ from google.protobuf.internal import encoder
from google.protobuf.internal import enum_type_wrapper
from google.protobuf.internal import message_listener as message_listener_mod
from google.protobuf.internal import type_checkers
+from google.protobuf.internal import well_known_types
from google.protobuf.internal import wire_format
from google.protobuf import descriptor as descriptor_mod
from google.protobuf import message as message_mod
@@ -72,6 +73,7 @@ from google.protobuf import symbol_database
from google.protobuf import text_format
_FieldDescriptor = descriptor_mod.FieldDescriptor
+_AnyFullTypeName = 'google.protobuf.Any'
class GeneratedProtocolMessageType(type):
@@ -127,6 +129,8 @@ class GeneratedProtocolMessageType(type):
Newly-allocated class.
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
+ if descriptor.full_name in well_known_types.WKTBASES:
+ bases += (well_known_types.WKTBASES[descriptor.full_name],)
_AddClassAttributesForNestedExtensions(descriptor, dictionary)
_AddSlots(descriptor, dictionary)
@@ -261,7 +265,6 @@ def _IsMessageSetExtension(field):
field.containing_type.has_options and
field.containing_type.GetOptions().message_set_wire_format and
field.type == _FieldDescriptor.TYPE_MESSAGE and
- field.message_type == field.extension_scope and
field.label == _FieldDescriptor.LABEL_OPTIONAL)
@@ -543,7 +546,8 @@ def _GetFieldByName(message_descriptor, field_name):
try:
return message_descriptor.fields_by_name[field_name]
except KeyError:
- raise ValueError('Protocol message has no "%s" field.' % field_name)
+ raise ValueError('Protocol message %s has no "%s" field.' %
+ (message_descriptor.name, field_name))
def _AddPropertiesForFields(descriptor, cls):
@@ -848,9 +852,15 @@ def _AddClearFieldMethod(message_descriptor, cls):
else:
return
except KeyError:
- raise ValueError('Protocol message has no "%s" field.' % field_name)
+ raise ValueError('Protocol message %s() has no "%s" field.' %
+ (message_descriptor.name, field_name))
if field in self._fields:
+ # To match the C++ implementation, we need to invalidate iterators
+ # for map fields when ClearField() happens.
+ if hasattr(self._fields[field], 'InvalidateIterators'):
+ self._fields[field].InvalidateIterators()
+
# Note: If the field is a sub-message, its listener will still point
# at us. That's fine, because the worst than can happen is that it
# will call _Modified() and invalidate our byte size. Big deal.
@@ -904,7 +914,19 @@ def _AddHasExtensionMethod(cls):
return extension_handle in self._fields
cls.HasExtension = HasExtension
-def _UnpackAny(msg):
+def _InternalUnpackAny(msg):
+ """Unpacks Any message and returns the unpacked message.
+
+ This internal method is differnt from public Any Unpack method which takes
+ the target message as argument. _InternalUnpackAny method does not have
+ target message type and need to find the message type in descriptor pool.
+
+ Args:
+ msg: An Any message to be unpacked.
+
+ Returns:
+ The unpacked message.
+ """
type_url = msg.type_url
db = symbol_database.Default()
@@ -935,9 +957,9 @@ def _AddEqualsMethod(message_descriptor, cls):
if self is other:
return True
- if self.DESCRIPTOR.full_name == "google.protobuf.Any":
- any_a = _UnpackAny(self)
- any_b = _UnpackAny(other)
+ if self.DESCRIPTOR.full_name == _AnyFullTypeName:
+ any_a = _InternalUnpackAny(self)
+ any_b = _InternalUnpackAny(other)
if any_a and any_b:
return any_a == any_b
@@ -962,6 +984,13 @@ def _AddStrMethod(message_descriptor, cls):
cls.__str__ = __str__
+def _AddReprMethod(message_descriptor, cls):
+ """Helper for _AddMessageMethods()."""
+ def __repr__(self):
+ return text_format.MessageToString(self)
+ cls.__repr__ = __repr__
+
+
def _AddUnicodeMethod(unused_message_descriptor, cls):
"""Helper for _AddMessageMethods()."""
@@ -1270,6 +1299,7 @@ def _AddMessageMethods(message_descriptor, cls):
_AddClearMethod(message_descriptor, cls)
_AddEqualsMethod(message_descriptor, cls)
_AddStrMethod(message_descriptor, cls)
+ _AddReprMethod(message_descriptor, cls)
_AddUnicodeMethod(message_descriptor, cls)
_AddSetListenerMethod(cls)
_AddByteSizeMethod(message_descriptor, cls)
@@ -1280,6 +1310,7 @@ def _AddMessageMethods(message_descriptor, cls):
_AddMergeFromMethod(cls)
_AddWhichOneofMethod(message_descriptor, cls)
+
def _AddPrivateHelperMethods(message_descriptor, cls):
"""Adds implementation of private helper methods to cls."""
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index 8621d61e..752f2f5d 100755
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -2394,8 +2394,10 @@ class SerializationTest(unittest.TestCase):
extension_message2 = message_set_extensions_pb2.TestMessageSetExtension2
extension1 = extension_message1.message_set_extension
extension2 = extension_message2.message_set_extension
+ extension3 = message_set_extensions_pb2.message_set_extension3
proto.Extensions[extension1].i = 123
proto.Extensions[extension2].str = 'foo'
+ proto.Extensions[extension3].text = 'bar'
# Serialize using the MessageSet wire format (this is specified in the
# .proto file).
@@ -2407,7 +2409,7 @@ class SerializationTest(unittest.TestCase):
self.assertEqual(
len(serialized),
raw.MergeFromString(serialized))
- self.assertEqual(2, len(raw.item))
+ self.assertEqual(3, len(raw.item))
message1 = message_set_extensions_pb2.TestMessageSetExtension1()
self.assertEqual(
@@ -2421,6 +2423,12 @@ class SerializationTest(unittest.TestCase):
message2.MergeFromString(raw.item[1].message))
self.assertEqual('foo', message2.str)
+ message3 = message_set_extensions_pb2.TestMessageSetExtension3()
+ self.assertEqual(
+ len(raw.item[2].message),
+ message3.MergeFromString(raw.item[2].message))
+ self.assertEqual('bar', message3.text)
+
# Deserialize using the MessageSet wire format.
proto2 = message_set_extensions_pb2.TestMessageSet()
self.assertEqual(
@@ -2428,6 +2436,7 @@ class SerializationTest(unittest.TestCase):
proto2.MergeFromString(serialized))
self.assertEqual(123, proto2.Extensions[extension1].i)
self.assertEqual('foo', proto2.Extensions[extension2].str)
+ self.assertEqual('bar', proto2.Extensions[extension3].text)
# Check byte size.
self.assertEqual(proto2.ByteSize(), len(serialized))
@@ -2757,9 +2766,10 @@ class SerializationTest(unittest.TestCase):
def testInitArgsUnknownFieldName(self):
def InitalizeEmptyMessageWithExtraKeywordArg():
unused_proto = unittest_pb2.TestEmptyMessage(unknown='unknown')
- self._CheckRaises(ValueError,
- InitalizeEmptyMessageWithExtraKeywordArg,
- 'Protocol message has no "unknown" field.')
+ self._CheckRaises(
+ ValueError,
+ InitalizeEmptyMessageWithExtraKeywordArg,
+ 'Protocol message TestEmptyMessage has no "unknown" field.')
def testInitRequiredKwargs(self):
proto = unittest_pb2.TestRequired(a=1, b=1, c=1)
diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py
index cca0ee63..0e14556c 100755
--- a/python/google/protobuf/internal/text_format_test.py
+++ b/python/google/protobuf/internal/text_format_test.py
@@ -51,8 +51,22 @@ from google.protobuf import unittest_pb2
from google.protobuf import unittest_proto3_arena_pb2
from google.protobuf.internal import api_implementation
from google.protobuf.internal import test_util
+from google.protobuf.internal import message_set_extensions_pb2
from google.protobuf import text_format
+
+# Low-level nuts-n-bolts tests.
+class SimpleTextFormatTests(unittest.TestCase):
+
+ # The members of _QUOTES are formatted into a regexp template that
+ # expects single characters. Therefore it's an error (in addition to being
+ # non-sensical in the first place) to try to specify a "quote mark" that is
+ # more than one character.
+ def TestQuoteMarksAreSingleChars(self):
+ for quote in text_format._QUOTES:
+ self.assertEqual(1, len(quote))
+
+
# Base class with some common functionality.
class TextFormatBase(unittest.TestCase):
@@ -287,6 +301,19 @@ class TextFormatTest(TextFormatBase):
self.assertEqual(u'one', message.repeated_string[0])
self.assertEqual(u'two', message.repeated_string[1])
+ def testParseRepeatedScalarShortFormat(self, message_module):
+ message = message_module.TestAllTypes()
+ text = ('repeated_int64: [100, 200];\n'
+ 'repeated_int64: 300,\n'
+ 'repeated_string: ["one", "two"];\n')
+ text_format.Parse(text, message)
+
+ self.assertEqual(100, message.repeated_int64[0])
+ self.assertEqual(200, message.repeated_int64[1])
+ self.assertEqual(300, message.repeated_int64[2])
+ self.assertEqual(u'one', message.repeated_string[0])
+ self.assertEqual(u'two', message.repeated_string[1])
+
def testParseEmptyText(self, message_module):
message = message_module.TestAllTypes()
text = ''
@@ -301,7 +328,7 @@ class TextFormatTest(TextFormatBase):
def testParseSingleWord(self, message_module):
message = message_module.TestAllTypes()
text = 'foo'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
(r'1:1 : Message type "\w+.TestAllTypes" has no field named '
r'"foo".'),
@@ -310,7 +337,7 @@ class TextFormatTest(TextFormatBase):
def testParseUnknownField(self, message_module):
message = message_module.TestAllTypes()
text = 'unknown_field: 8\n'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
(r'1:1 : Message type "\w+.TestAllTypes" has no field named '
r'"unknown_field".'),
@@ -319,7 +346,7 @@ class TextFormatTest(TextFormatBase):
def testParseBadEnumValue(self, message_module):
message = message_module.TestAllTypes()
text = 'optional_nested_enum: BARR'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
(r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
r'has no value named BARR.'),
@@ -327,7 +354,7 @@ class TextFormatTest(TextFormatBase):
message = message_module.TestAllTypes()
text = 'optional_nested_enum: 100'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
(r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
r'has no value with number 100.'),
@@ -336,7 +363,7 @@ class TextFormatTest(TextFormatBase):
def testParseBadIntValue(self, message_module):
message = message_module.TestAllTypes()
text = 'optional_int32: bork'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
('1:17 : Couldn\'t parse integer: bork'),
text_format.Parse, text, message)
@@ -553,6 +580,15 @@ class Proto2Tests(TextFormatBase):
' }\n'
'}\n')
+ message = message_set_extensions_pb2.TestMessageSet()
+ ext = message_set_extensions_pb2.message_set_extension3
+ message.Extensions[ext].text = 'bar'
+ self.CompareToGoldenText(
+ text_format.MessageToString(message),
+ '[google.protobuf.internal.TestMessageSetExtension3] {\n'
+ ' text: \"bar\"\n'
+ '}\n')
+
def testPrintMessageSetAsOneLine(self):
message = unittest_mset_pb2.TestMessageSetContainer()
ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
@@ -627,15 +663,125 @@ class Proto2Tests(TextFormatBase):
text_format.Parse(ascii_text, parsed_message)
self.assertEqual(message, parsed_message)
+ def testParseAllowedUnknownExtension(self):
+ # Skip over unknown extension correctly.
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ text = ('message_set {\n'
+ ' [unknown_extension] {\n'
+ ' i: 23\n'
+ ' [nested_unknown_ext]: {\n'
+ ' i: 23\n'
+ ' test: "test_string"\n'
+ ' floaty_float: -0.315\n'
+ ' num: -inf\n'
+ ' multiline_str: "abc"\n'
+ ' "def"\n'
+ ' "xyz."\n'
+ ' [nested_unknown_ext]: <\n'
+ ' i: 23\n'
+ ' i: 24\n'
+ ' pointfloat: .3\n'
+ ' test: "test_string"\n'
+ ' floaty_float: -0.315\n'
+ ' num: -inf\n'
+ ' long_string: "test" "test2" \n'
+ ' >\n'
+ ' }\n'
+ ' }\n'
+ ' [unknown_extension]: 5\n'
+ '}\n')
+ text_format.Parse(text, message, allow_unknown_extension=True)
+ golden = 'message_set {\n}\n'
+ self.CompareToGoldenText(text_format.MessageToString(message), golden)
+
+ # Catch parse errors in unknown extension.
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ malformed = ('message_set {\n'
+ ' [unknown_extension] {\n'
+ ' i:\n' # Missing value.
+ ' }\n'
+ '}\n')
+ six.assertRaisesRegex(self,
+ text_format.ParseError,
+ 'Invalid field value: }',
+ text_format.Parse, malformed, message,
+ allow_unknown_extension=True)
+
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ malformed = ('message_set {\n'
+ ' [unknown_extension] {\n'
+ ' str: "malformed string\n' # Missing closing quote.
+ ' }\n'
+ '}\n')
+ six.assertRaisesRegex(self,
+ text_format.ParseError,
+ 'Invalid field value: "',
+ text_format.Parse, malformed, message,
+ allow_unknown_extension=True)
+
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ malformed = ('message_set {\n'
+ ' [unknown_extension] {\n'
+ ' str: "malformed\n multiline\n string\n'
+ ' }\n'
+ '}\n')
+ six.assertRaisesRegex(self,
+ text_format.ParseError,
+ 'Invalid field value: "',
+ text_format.Parse, malformed, message,
+ allow_unknown_extension=True)
+
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ malformed = ('message_set {\n'
+ ' [malformed_extension] <\n'
+ ' i: -5\n'
+ ' \n' # Missing '>' here.
+ '}\n')
+ six.assertRaisesRegex(self,
+ text_format.ParseError,
+ '5:1 : Expected ">".',
+ text_format.Parse, malformed, message,
+ allow_unknown_extension=True)
+
+ # Don't allow unknown fields with allow_unknown_extension=True.
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ malformed = ('message_set {\n'
+ ' unknown_field: true\n'
+ ' \n' # Missing '>' here.
+ '}\n')
+ six.assertRaisesRegex(self,
+ text_format.ParseError,
+ ('2:3 : Message type '
+ '"proto2_wireformat_unittest.TestMessageSet" has no'
+ ' field named "unknown_field".'),
+ text_format.Parse, malformed, message,
+ allow_unknown_extension=True)
+
+ # Parse known extension correcty.
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ text = ('message_set {\n'
+ ' [protobuf_unittest.TestMessageSetExtension1] {\n'
+ ' i: 23\n'
+ ' }\n'
+ ' [protobuf_unittest.TestMessageSetExtension2] {\n'
+ ' str: \"foo\"\n'
+ ' }\n'
+ '}\n')
+ text_format.Parse(text, message, allow_unknown_extension=True)
+ ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
+ ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
+ self.assertEqual(23, message.message_set.Extensions[ext1].i)
+ self.assertEqual('foo', message.message_set.Extensions[ext2].str)
+
def testParseBadExtension(self):
message = unittest_pb2.TestAllExtensions()
text = '[unknown_extension]: 8\n'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
'1:2 : Extension "unknown_extension" not registered.',
text_format.Parse, text, message)
message = unittest_pb2.TestAllTypes()
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
'extensions.'),
@@ -654,7 +800,7 @@ class Proto2Tests(TextFormatBase):
message = unittest_pb2.TestAllExtensions()
text = ('[protobuf_unittest.optional_int32_extension]: 42 '
'[protobuf_unittest.optional_int32_extension]: 67')
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
'should not have multiple '
@@ -665,7 +811,7 @@ class Proto2Tests(TextFormatBase):
message = unittest_pb2.TestAllTypes()
text = ('optional_nested_message { bb: 1 } '
'optional_nested_message { bb: 2 }')
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
'should not have multiple "bb" fields.'),
@@ -675,7 +821,7 @@ class Proto2Tests(TextFormatBase):
message = unittest_pb2.TestAllTypes()
text = ('optional_int32: 42 '
'optional_int32: 67')
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError,
('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
'have multiple "optional_int32" fields.'),
@@ -684,11 +830,11 @@ class Proto2Tests(TextFormatBase):
def testParseGroupNotClosed(self):
message = unittest_pb2.TestAllTypes()
text = 'RepeatedGroup: <'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError, '1:16 : Expected ">".',
text_format.Parse, text, message)
text = 'RepeatedGroup: {'
- six.assertRaisesRegex(self,
+ six.assertRaisesRegex(self,
text_format.ParseError, '1:16 : Expected "}".',
text_format.Parse, text, message)
diff --git a/python/google/protobuf/internal/well_known_types.py b/python/google/protobuf/internal/well_known_types.py
new file mode 100644
index 00000000..d35fcc5f
--- /dev/null
+++ b/python/google/protobuf/internal/well_known_types.py
@@ -0,0 +1,720 @@
+# Protocol Buffers - Google's data interchange format
+# Copyright 2008 Google Inc. All rights reserved.
+# https://developers.google.com/protocol-buffers/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Contains well known classes.
+
+This files defines well known classes which need extra maintenance including:
+ - Any
+ - Duration
+ - FieldMask
+ - Struct
+ - Timestamp
+"""
+
+__author__ = 'jieluo@google.com (Jie Luo)'
+
+from datetime import datetime
+from datetime import timedelta
+import six
+
+from google.protobuf.descriptor import FieldDescriptor
+
+_TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S'
+_NANOS_PER_SECOND = 1000000000
+_NANOS_PER_MILLISECOND = 1000000
+_NANOS_PER_MICROSECOND = 1000
+_MILLIS_PER_SECOND = 1000
+_MICROS_PER_SECOND = 1000000
+_SECONDS_PER_DAY = 24 * 3600
+
+
+class Error(Exception):
+ """Top-level module error."""
+
+
+class ParseError(Error):
+ """Thrown in case of parsing error."""
+
+
+class Any(object):
+ """Class for Any Message type."""
+
+ def Pack(self, msg, type_url_prefix='type.googleapis.com/'):
+ """Packs the specified message into current Any message."""
+ if len(type_url_prefix) < 1 or type_url_prefix[-1] != '/':
+ self.type_url = '%s/%s' % (type_url_prefix, msg.DESCRIPTOR.full_name)
+ else:
+ self.type_url = '%s%s' % (type_url_prefix, msg.DESCRIPTOR.full_name)
+ self.value = msg.SerializeToString()
+
+ def Unpack(self, msg):
+ """Unpacks the current Any message into specified message."""
+ descriptor = msg.DESCRIPTOR
+ if not self.Is(descriptor):
+ return False
+ msg.ParseFromString(self.value)
+ return True
+
+ def Is(self, descriptor):
+ """Checks if this Any represents the given protobuf type."""
+ # Only last part is to be used: b/25630112
+ return self.type_url.split('/')[-1] == descriptor.full_name
+
+
+class Timestamp(object):
+ """Class for Timestamp message type."""
+
+ def ToJsonString(self):
+ """Converts Timestamp to RFC 3339 date string format.
+
+ Returns:
+ A string converted from timestamp. The string is always Z-normalized
+ and uses 3, 6 or 9 fractional digits as required to represent the
+ exact time. Example of the return format: '1972-01-01T10:00:20.021Z'
+ """
+ nanos = self.nanos % _NANOS_PER_SECOND
+ total_sec = self.seconds + (self.nanos - nanos) // _NANOS_PER_SECOND
+ seconds = total_sec % _SECONDS_PER_DAY
+ days = (total_sec - seconds) // _SECONDS_PER_DAY
+ dt = datetime(1970, 1, 1) + timedelta(days, seconds)
+
+ result = dt.isoformat()
+ if (nanos % 1e9) == 0:
+ # If there are 0 fractional digits, the fractional
+ # point '.' should be omitted when serializing.
+ return result + 'Z'
+ if (nanos % 1e6) == 0:
+ # Serialize 3 fractional digits.
+ return result + '.%03dZ' % (nanos / 1e6)
+ if (nanos % 1e3) == 0:
+ # Serialize 6 fractional digits.
+ return result + '.%06dZ' % (nanos / 1e3)
+ # Serialize 9 fractional digits.
+ return result + '.%09dZ' % nanos
+
+ def FromJsonString(self, value):
+ """Parse a RFC 3339 date string format to Timestamp.
+
+ Args:
+ value: A date string. Any fractional digits (or none) and any offset are
+ accepted as long as they fit into nano-seconds precision.
+ Example of accepted format: '1972-01-01T10:00:20.021-05:00'
+
+ Raises:
+ ParseError: On parsing problems.
+ """
+ timezone_offset = value.find('Z')
+ if timezone_offset == -1:
+ timezone_offset = value.find('+')
+ if timezone_offset == -1:
+ timezone_offset = value.rfind('-')
+ if timezone_offset == -1:
+ raise ParseError(
+ 'Failed to parse timestamp: missing valid timezone offset.')
+ time_value = value[0:timezone_offset]
+ # Parse datetime and nanos.
+ point_position = time_value.find('.')
+ if point_position == -1:
+ second_value = time_value
+ nano_value = ''
+ else:
+ second_value = time_value[:point_position]
+ nano_value = time_value[point_position + 1:]
+ date_object = datetime.strptime(second_value, _TIMESTAMPFOMAT)
+ td = date_object - datetime(1970, 1, 1)
+ seconds = td.seconds + td.days * _SECONDS_PER_DAY
+ if len(nano_value) > 9:
+ raise ParseError(
+ 'Failed to parse Timestamp: nanos {0} more than '
+ '9 fractional digits.'.format(nano_value))
+ if nano_value:
+ nanos = round(float('0.' + nano_value) * 1e9)
+ else:
+ nanos = 0
+ # Parse timezone offsets.
+ if value[timezone_offset] == 'Z':
+ if len(value) != timezone_offset + 1:
+ raise ParseError('Failed to parse timestamp: invalid trailing'
+ ' data {0}.'.format(value))
+ else:
+ timezone = value[timezone_offset:]
+ pos = timezone.find(':')
+ if pos == -1:
+ raise ParseError(
+ 'Invalid timezone offset value: {0}.'.format(timezone))
+ if timezone[0] == '+':
+ seconds -= (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60
+ else:
+ seconds += (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60
+ # Set seconds and nanos
+ self.seconds = int(seconds)
+ self.nanos = int(nanos)
+
+ def GetCurrentTime(self):
+ """Get the current UTC into Timestamp."""
+ self.FromDatetime(datetime.utcnow())
+
+ def ToNanoseconds(self):
+ """Converts Timestamp to nanoseconds since epoch."""
+ return self.seconds * _NANOS_PER_SECOND + self.nanos
+
+ def ToMicroseconds(self):
+ """Converts Timestamp to microseconds since epoch."""
+ return (self.seconds * _MICROS_PER_SECOND +
+ self.nanos // _NANOS_PER_MICROSECOND)
+
+ def ToMilliseconds(self):
+ """Converts Timestamp to milliseconds since epoch."""
+ return (self.seconds * _MILLIS_PER_SECOND +
+ self.nanos // _NANOS_PER_MILLISECOND)
+
+ def ToSeconds(self):
+ """Converts Timestamp to seconds since epoch."""
+ return self.seconds
+
+ def FromNanoseconds(self, nanos):
+ """Converts nanoseconds since epoch to Timestamp."""
+ self.seconds = nanos // _NANOS_PER_SECOND
+ self.nanos = nanos % _NANOS_PER_SECOND
+
+ def FromMicroseconds(self, micros):
+ """Converts microseconds since epoch to Timestamp."""
+ self.seconds = micros // _MICROS_PER_SECOND
+ self.nanos = (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND
+
+ def FromMilliseconds(self, millis):
+ """Converts milliseconds since epoch to Timestamp."""
+ self.seconds = millis // _MILLIS_PER_SECOND
+ self.nanos = (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND
+
+ def FromSeconds(self, seconds):
+ """Converts seconds since epoch to Timestamp."""
+ self.seconds = seconds
+ self.nanos = 0
+
+ def ToDatetime(self):
+ """Converts Timestamp to datetime."""
+ return datetime.utcfromtimestamp(
+ self.seconds + self.nanos / float(_NANOS_PER_SECOND))
+
+ def FromDatetime(self, dt):
+ """Converts datetime to Timestamp."""
+ td = dt - datetime(1970, 1, 1)
+ self.seconds = td.seconds + td.days * _SECONDS_PER_DAY
+ self.nanos = td.microseconds * _NANOS_PER_MICROSECOND
+
+
+class Duration(object):
+ """Class for Duration message type."""
+
+ def ToJsonString(self):
+ """Converts Duration to string format.
+
+ Returns:
+ A string converted from self. The string format will contains
+ 3, 6, or 9 fractional digits depending on the precision required to
+ represent the exact Duration value. For example: "1s", "1.010s",
+ "1.000000100s", "-3.100s"
+ """
+ if self.seconds < 0 or self.nanos < 0:
+ result = '-'
+ seconds = - self.seconds + int((0 - self.nanos) // 1e9)
+ nanos = (0 - self.nanos) % 1e9
+ else:
+ result = ''
+ seconds = self.seconds + int(self.nanos // 1e9)
+ nanos = self.nanos % 1e9
+ result += '%d' % seconds
+ if (nanos % 1e9) == 0:
+ # If there are 0 fractional digits, the fractional
+ # point '.' should be omitted when serializing.
+ return result + 's'
+ if (nanos % 1e6) == 0:
+ # Serialize 3 fractional digits.
+ return result + '.%03ds' % (nanos / 1e6)
+ if (nanos % 1e3) == 0:
+ # Serialize 6 fractional digits.
+ return result + '.%06ds' % (nanos / 1e3)
+ # Serialize 9 fractional digits.
+ return result + '.%09ds' % nanos
+
+ def FromJsonString(self, value):
+ """Converts a string to Duration.
+
+ Args:
+ value: A string to be converted. The string must end with 's'. Any
+ fractional digits (or none) are accepted as long as they fit into
+ precision. For example: "1s", "1.01s", "1.0000001s", "-3.100s
+
+ Raises:
+ ParseError: On parsing problems.
+ """
+ if len(value) < 1 or value[-1] != 's':
+ raise ParseError(
+ 'Duration must end with letter "s": {0}.'.format(value))
+ try:
+ pos = value.find('.')
+ if pos == -1:
+ self.seconds = int(value[:-1])
+ self.nanos = 0
+ else:
+ self.seconds = int(value[:pos])
+ if value[0] == '-':
+ self.nanos = int(round(float('-0{0}'.format(value[pos: -1])) *1e9))
+ else:
+ self.nanos = int(round(float('0{0}'.format(value[pos: -1])) *1e9))
+ except ValueError:
+ raise ParseError(
+ 'Couldn\'t parse duration: {0}.'.format(value))
+
+ def ToNanoseconds(self):
+ """Converts a Duration to nanoseconds."""
+ return self.seconds * _NANOS_PER_SECOND + self.nanos
+
+ def ToMicroseconds(self):
+ """Converts a Duration to microseconds."""
+ micros = _RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND)
+ return self.seconds * _MICROS_PER_SECOND + micros
+
+ def ToMilliseconds(self):
+ """Converts a Duration to milliseconds."""
+ millis = _RoundTowardZero(self.nanos, _NANOS_PER_MILLISECOND)
+ return self.seconds * _MILLIS_PER_SECOND + millis
+
+ def ToSeconds(self):
+ """Converts a Duration to seconds."""
+ return self.seconds
+
+ def FromNanoseconds(self, nanos):
+ """Converts nanoseconds to Duration."""
+ self._NormalizeDuration(nanos // _NANOS_PER_SECOND,
+ nanos % _NANOS_PER_SECOND)
+
+ def FromMicroseconds(self, micros):
+ """Converts microseconds to Duration."""
+ self._NormalizeDuration(
+ micros // _MICROS_PER_SECOND,
+ (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND)
+
+ def FromMilliseconds(self, millis):
+ """Converts milliseconds to Duration."""
+ self._NormalizeDuration(
+ millis // _MILLIS_PER_SECOND,
+ (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND)
+
+ def FromSeconds(self, seconds):
+ """Converts seconds to Duration."""
+ self.seconds = seconds
+ self.nanos = 0
+
+ def ToTimedelta(self):
+ """Converts Duration to timedelta."""
+ return timedelta(
+ seconds=self.seconds, microseconds=_RoundTowardZero(
+ self.nanos, _NANOS_PER_MICROSECOND))
+
+ def FromTimedelta(self, td):
+ """Convertd timedelta to Duration."""
+ self._NormalizeDuration(td.seconds + td.days * _SECONDS_PER_DAY,
+ td.microseconds * _NANOS_PER_MICROSECOND)
+
+ def _NormalizeDuration(self, seconds, nanos):
+ """Set Duration by seconds and nonas."""
+ # Force nanos to be negative if the duration is negative.
+ if seconds < 0 and nanos > 0:
+ seconds += 1
+ nanos -= _NANOS_PER_SECOND
+ self.seconds = seconds
+ self.nanos = nanos
+
+
+def _RoundTowardZero(value, divider):
+ """Truncates the remainder part after division."""
+ # For some languanges, the sign of the remainder is implementation
+ # dependent if any of the operands is negative. Here we enforce
+ # "rounded toward zero" semantics. For example, for (-5) / 2 an
+ # implementation may give -3 as the result with the remainder being
+ # 1. This function ensures we always return -2 (closer to zero).
+ result = value // divider
+ remainder = value % divider
+ if result < 0 and remainder > 0:
+ return result + 1
+ else:
+ return result
+
+
+class FieldMask(object):
+ """Class for FieldMask message type."""
+
+ def ToJsonString(self):
+ """Converts FieldMask to string according to proto3 JSON spec."""
+ return ','.join(self.paths)
+
+ def FromJsonString(self, value):
+ """Converts string to FieldMask according to proto3 JSON spec."""
+ self.Clear()
+ for path in value.split(','):
+ self.paths.append(path)
+
+ def IsValidForDescriptor(self, message_descriptor):
+ """Checks whether the FieldMask is valid for Message Descriptor."""
+ for path in self.paths:
+ if not _IsValidPath(message_descriptor, path):
+ return False
+ return True
+
+ def AllFieldsFromDescriptor(self, message_descriptor):
+ """Gets all direct fields of Message Descriptor to FieldMask."""
+ self.Clear()
+ for field in message_descriptor.fields:
+ self.paths.append(field.name)
+
+ def CanonicalFormFromMask(self, mask):
+ """Converts a FieldMask to the canonical form.
+
+ Removes paths that are covered by another path. For example,
+ "foo.bar" is covered by "foo" and will be removed if "foo"
+ is also in the FieldMask. Then sorts all paths in alphabetical order.
+
+ Args:
+ mask: The original FieldMask to be converted.
+ """
+ tree = _FieldMaskTree(mask)
+ tree.ToFieldMask(self)
+
+ def Union(self, mask1, mask2):
+ """Merges mask1 and mask2 into this FieldMask."""
+ _CheckFieldMaskMessage(mask1)
+ _CheckFieldMaskMessage(mask2)
+ tree = _FieldMaskTree(mask1)
+ tree.MergeFromFieldMask(mask2)
+ tree.ToFieldMask(self)
+
+ def Intersect(self, mask1, mask2):
+ """Intersects mask1 and mask2 into this FieldMask."""
+ _CheckFieldMaskMessage(mask1)
+ _CheckFieldMaskMessage(mask2)
+ tree = _FieldMaskTree(mask1)
+ intersection = _FieldMaskTree()
+ for path in mask2.paths:
+ tree.IntersectPath(path, intersection)
+ intersection.ToFieldMask(self)
+
+ def MergeMessage(
+ self, source, destination,
+ replace_message_field=False, replace_repeated_field=False):
+ """Merges fields specified in FieldMask from source to destination.
+
+ Args:
+ source: Source message.
+ destination: The destination message to be merged into.
+ replace_message_field: Replace message field if True. Merge message
+ field if False.
+ replace_repeated_field: Replace repeated field if True. Append
+ elements of repeated field if False.
+ """
+ tree = _FieldMaskTree(self)
+ tree.MergeMessage(
+ source, destination, replace_message_field, replace_repeated_field)
+
+
+def _IsValidPath(message_descriptor, path):
+ """Checks whether the path is valid for Message Descriptor."""
+ parts = path.split('.')
+ last = parts.pop()
+ for name in parts:
+ field = message_descriptor.fields_by_name[name]
+ if (field is None or
+ field.label == FieldDescriptor.LABEL_REPEATED or
+ field.type != FieldDescriptor.TYPE_MESSAGE):
+ return False
+ message_descriptor = field.message_type
+ return last in message_descriptor.fields_by_name
+
+
+def _CheckFieldMaskMessage(message):
+ """Raises ValueError if message is not a FieldMask."""
+ message_descriptor = message.DESCRIPTOR
+ if (message_descriptor.name != 'FieldMask' or
+ message_descriptor.file.name != 'google/protobuf/field_mask.proto'):
+ raise ValueError('Message {0} is not a FieldMask.'.format(
+ message_descriptor.full_name))
+
+
+class _FieldMaskTree(object):
+ """Represents a FieldMask in a tree structure.
+
+ For example, given a FieldMask "foo.bar,foo.baz,bar.baz",
+ the FieldMaskTree will be:
+ [_root] -+- foo -+- bar
+ | |
+ | +- baz
+ |
+ +- bar --- baz
+ In the tree, each leaf node represents a field path.
+ """
+
+ def __init__(self, field_mask=None):
+ """Initializes the tree by FieldMask."""
+ self._root = {}
+ if field_mask:
+ self.MergeFromFieldMask(field_mask)
+
+ def MergeFromFieldMask(self, field_mask):
+ """Merges a FieldMask to the tree."""
+ for path in field_mask.paths:
+ self.AddPath(path)
+
+ def AddPath(self, path):
+ """Adds a field path into the tree.
+
+ If the field path to add is a sub-path of an existing field path
+ in the tree (i.e., a leaf node), it means the tree already matches
+ the given path so nothing will be added to the tree. If the path
+ matches an existing non-leaf node in the tree, that non-leaf node
+ will be turned into a leaf node with all its children removed because
+ the path matches all the node's children. Otherwise, a new path will
+ be added.
+
+ Args:
+ path: The field path to add.
+ """
+ node = self._root
+ for name in path.split('.'):
+ if name not in node:
+ node[name] = {}
+ elif not node[name]:
+ # Pre-existing empty node implies we already have this entire tree.
+ return
+ node = node[name]
+ # Remove any sub-trees we might have had.
+ node.clear()
+
+ def ToFieldMask(self, field_mask):
+ """Converts the tree to a FieldMask."""
+ field_mask.Clear()
+ _AddFieldPaths(self._root, '', field_mask)
+
+ def IntersectPath(self, path, intersection):
+ """Calculates the intersection part of a field path with this tree.
+
+ Args:
+ path: The field path to calculates.
+ intersection: The out tree to record the intersection part.
+ """
+ node = self._root
+ for name in path.split('.'):
+ if name not in node:
+ return
+ elif not node[name]:
+ intersection.AddPath(path)
+ return
+ node = node[name]
+ intersection.AddLeafNodes(path, node)
+
+ def AddLeafNodes(self, prefix, node):
+ """Adds leaf nodes begin with prefix to this tree."""
+ if not node:
+ self.AddPath(prefix)
+ for name in node:
+ child_path = prefix + '.' + name
+ self.AddLeafNodes(child_path, node[name])
+
+ def MergeMessage(
+ self, source, destination,
+ replace_message, replace_repeated):
+ """Merge all fields specified by this tree from source to destination."""
+ _MergeMessage(
+ self._root, source, destination, replace_message, replace_repeated)
+
+
+def _StrConvert(value):
+ """Converts value to str if it is not."""
+ # This file is imported by c extension and some methods like ClearField
+ # requires string for the field name. py2/py3 has different text
+ # type and may use unicode.
+ if not isinstance(value, str):
+ return value.encode('utf-8')
+ return value
+
+
+def _MergeMessage(
+ node, source, destination, replace_message, replace_repeated):
+ """Merge all fields specified by a sub-tree from source to destination."""
+ source_descriptor = source.DESCRIPTOR
+ for name in node:
+ child = node[name]
+ field = source_descriptor.fields_by_name[name]
+ if field is None:
+ raise ValueError('Error: Can\'t find field {0} in message {1}.'.format(
+ name, source_descriptor.full_name))
+ if child:
+ # Sub-paths are only allowed for singular message fields.
+ if (field.label == FieldDescriptor.LABEL_REPEATED or
+ field.cpp_type != FieldDescriptor.CPPTYPE_MESSAGE):
+ raise ValueError('Error: Field {0} in message {1} is not a singular '
+ 'message field and cannot have sub-fields.'.format(
+ name, source_descriptor.full_name))
+ _MergeMessage(
+ child, getattr(source, name), getattr(destination, name),
+ replace_message, replace_repeated)
+ continue
+ if field.label == FieldDescriptor.LABEL_REPEATED:
+ if replace_repeated:
+ destination.ClearField(_StrConvert(name))
+ repeated_source = getattr(source, name)
+ repeated_destination = getattr(destination, name)
+ if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE:
+ for item in repeated_source:
+ repeated_destination.add().MergeFrom(item)
+ else:
+ repeated_destination.extend(repeated_source)
+ else:
+ if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE:
+ if replace_message:
+ destination.ClearField(_StrConvert(name))
+ if source.HasField(name):
+ getattr(destination, name).MergeFrom(getattr(source, name))
+ else:
+ setattr(destination, name, getattr(source, name))
+
+
+def _AddFieldPaths(node, prefix, field_mask):
+ """Adds the field paths descended from node to field_mask."""
+ if not node:
+ field_mask.paths.append(prefix)
+ return
+ for name in sorted(node):
+ if prefix:
+ child_path = prefix + '.' + name
+ else:
+ child_path = name
+ _AddFieldPaths(node[name], child_path, field_mask)
+
+
+_INT_OR_FLOAT = six.integer_types + (float,)
+
+
+def _SetStructValue(struct_value, value):
+ if value is None:
+ struct_value.null_value = 0
+ elif isinstance(value, bool):
+ # Note: this check must come before the number check because in Python
+ # True and False are also considered numbers.
+ struct_value.bool_value = value
+ elif isinstance(value, six.string_types):
+ struct_value.string_value = value
+ elif isinstance(value, _INT_OR_FLOAT):
+ struct_value.number_value = value
+ else:
+ raise ValueError('Unexpected type')
+
+
+def _GetStructValue(struct_value):
+ which = struct_value.WhichOneof('kind')
+ if which == 'struct_value':
+ return struct_value.struct_value
+ elif which == 'null_value':
+ return None
+ elif which == 'number_value':
+ return struct_value.number_value
+ elif which == 'string_value':
+ return struct_value.string_value
+ elif which == 'bool_value':
+ return struct_value.bool_value
+ elif which == 'list_value':
+ return struct_value.list_value
+ elif which is None:
+ raise ValueError('Value not set')
+
+
+class Struct(object):
+ """Class for Struct message type."""
+
+ __slots__ = []
+
+ def __getitem__(self, key):
+ return _GetStructValue(self.fields[key])
+
+ def __setitem__(self, key, value):
+ _SetStructValue(self.fields[key], value)
+
+ def get_or_create_list(self, key):
+ """Returns a list for this key, creating if it didn't exist already."""
+ return self.fields[key].list_value
+
+ def get_or_create_struct(self, key):
+ """Returns a struct for this key, creating if it didn't exist already."""
+ return self.fields[key].struct_value
+
+ # TODO(haberman): allow constructing/merging from dict.
+
+
+class ListValue(object):
+ """Class for ListValue message type."""
+
+ def __len__(self):
+ return len(self.values)
+
+ def append(self, value):
+ _SetStructValue(self.values.add(), value)
+
+ def extend(self, elem_seq):
+ for value in elem_seq:
+ self.append(value)
+
+ def __getitem__(self, index):
+ """Retrieves item by the specified index."""
+ return _GetStructValue(self.values.__getitem__(index))
+
+ def __setitem__(self, index, value):
+ _SetStructValue(self.values.__getitem__(index), value)
+
+ def items(self):
+ for i in range(len(self)):
+ yield self[i]
+
+ def add_struct(self):
+ """Appends and returns a struct value as the next value in the list."""
+ return self.values.add().struct_value
+
+ def add_list(self):
+ """Appends and returns a list value as the next value in the list."""
+ return self.values.add().list_value
+
+
+WKTBASES = {
+ 'google.protobuf.Any': Any,
+ 'google.protobuf.Duration': Duration,
+ 'google.protobuf.FieldMask': FieldMask,
+ 'google.protobuf.ListValue': ListValue,
+ 'google.protobuf.Struct': Struct,
+ 'google.protobuf.Timestamp': Timestamp,
+}
diff --git a/python/google/protobuf/internal/well_known_types_test.py b/python/google/protobuf/internal/well_known_types_test.py
new file mode 100644
index 00000000..6acbee22
--- /dev/null
+++ b/python/google/protobuf/internal/well_known_types_test.py
@@ -0,0 +1,636 @@
+#! /usr/bin/env python
+#
+# Protocol Buffers - Google's data interchange format
+# Copyright 2008 Google Inc. All rights reserved.
+# https://developers.google.com/protocol-buffers/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Test for google.protobuf.internal.well_known_types."""
+
+__author__ = 'jieluo@google.com (Jie Luo)'
+
+from datetime import datetime
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+from google.protobuf import any_pb2
+from google.protobuf import duration_pb2
+from google.protobuf import field_mask_pb2
+from google.protobuf import struct_pb2
+from google.protobuf import timestamp_pb2
+from google.protobuf import unittest_pb2
+from google.protobuf.internal import any_test_pb2
+from google.protobuf.internal import test_util
+from google.protobuf.internal import well_known_types
+from google.protobuf import descriptor
+from google.protobuf import text_format
+
+
+class TimeUtilTestBase(unittest.TestCase):
+
+ def CheckTimestampConversion(self, message, text):
+ self.assertEqual(text, message.ToJsonString())
+ parsed_message = timestamp_pb2.Timestamp()
+ parsed_message.FromJsonString(text)
+ self.assertEqual(message, parsed_message)
+
+ def CheckDurationConversion(self, message, text):
+ self.assertEqual(text, message.ToJsonString())
+ parsed_message = duration_pb2.Duration()
+ parsed_message.FromJsonString(text)
+ self.assertEqual(message, parsed_message)
+
+
+class TimeUtilTest(TimeUtilTestBase):
+
+ def testTimestampSerializeAndParse(self):
+ message = timestamp_pb2.Timestamp()
+ # Generated output should contain 3, 6, or 9 fractional digits.
+ message.seconds = 0
+ message.nanos = 0
+ self.CheckTimestampConversion(message, '1970-01-01T00:00:00Z')
+ message.nanos = 10000000
+ self.CheckTimestampConversion(message, '1970-01-01T00:00:00.010Z')
+ message.nanos = 10000
+ self.CheckTimestampConversion(message, '1970-01-01T00:00:00.000010Z')
+ message.nanos = 10
+ self.CheckTimestampConversion(message, '1970-01-01T00:00:00.000000010Z')
+ # Test min timestamps.
+ message.seconds = -62135596800
+ message.nanos = 0
+ self.CheckTimestampConversion(message, '0001-01-01T00:00:00Z')
+ # Test max timestamps.
+ message.seconds = 253402300799
+ message.nanos = 999999999
+ self.CheckTimestampConversion(message, '9999-12-31T23:59:59.999999999Z')
+ # Test negative timestamps.
+ message.seconds = -1
+ self.CheckTimestampConversion(message, '1969-12-31T23:59:59.999999999Z')
+
+ # Parsing accepts an fractional digits as long as they fit into nano
+ # precision.
+ message.FromJsonString('1970-01-01T00:00:00.1Z')
+ self.assertEqual(0, message.seconds)
+ self.assertEqual(100000000, message.nanos)
+ # Parsing accpets offsets.
+ message.FromJsonString('1970-01-01T00:00:00-08:00')
+ self.assertEqual(8 * 3600, message.seconds)
+ self.assertEqual(0, message.nanos)
+
+ def testDurationSerializeAndParse(self):
+ message = duration_pb2.Duration()
+ # Generated output should contain 3, 6, or 9 fractional digits.
+ message.seconds = 0
+ message.nanos = 0
+ self.CheckDurationConversion(message, '0s')
+ message.nanos = 10000000
+ self.CheckDurationConversion(message, '0.010s')
+ message.nanos = 10000
+ self.CheckDurationConversion(message, '0.000010s')
+ message.nanos = 10
+ self.CheckDurationConversion(message, '0.000000010s')
+
+ # Test min and max
+ message.seconds = 315576000000
+ message.nanos = 999999999
+ self.CheckDurationConversion(message, '315576000000.999999999s')
+ message.seconds = -315576000000
+ message.nanos = -999999999
+ self.CheckDurationConversion(message, '-315576000000.999999999s')
+
+ # Parsing accepts an fractional digits as long as they fit into nano
+ # precision.
+ message.FromJsonString('0.1s')
+ self.assertEqual(100000000, message.nanos)
+ message.FromJsonString('0.0000001s')
+ self.assertEqual(100, message.nanos)
+
+ def testTimestampIntegerConversion(self):
+ message = timestamp_pb2.Timestamp()
+ message.FromNanoseconds(1)
+ self.assertEqual('1970-01-01T00:00:00.000000001Z',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToNanoseconds())
+
+ message.FromNanoseconds(-1)
+ self.assertEqual('1969-12-31T23:59:59.999999999Z',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToNanoseconds())
+
+ message.FromMicroseconds(1)
+ self.assertEqual('1970-01-01T00:00:00.000001Z',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToMicroseconds())
+
+ message.FromMicroseconds(-1)
+ self.assertEqual('1969-12-31T23:59:59.999999Z',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToMicroseconds())
+
+ message.FromMilliseconds(1)
+ self.assertEqual('1970-01-01T00:00:00.001Z',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToMilliseconds())
+
+ message.FromMilliseconds(-1)
+ self.assertEqual('1969-12-31T23:59:59.999Z',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToMilliseconds())
+
+ message.FromSeconds(1)
+ self.assertEqual('1970-01-01T00:00:01Z',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToSeconds())
+
+ message.FromSeconds(-1)
+ self.assertEqual('1969-12-31T23:59:59Z',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToSeconds())
+
+ message.FromNanoseconds(1999)
+ self.assertEqual(1, message.ToMicroseconds())
+ # For negative values, Timestamp will be rounded down.
+ # For example, "1969-12-31T23:59:59.5Z" (i.e., -0.5s) rounded to seconds
+ # will be "1969-12-31T23:59:59Z" (i.e., -1s) rather than
+ # "1970-01-01T00:00:00Z" (i.e., 0s).
+ message.FromNanoseconds(-1999)
+ self.assertEqual(-2, message.ToMicroseconds())
+
+ def testDurationIntegerConversion(self):
+ message = duration_pb2.Duration()
+ message.FromNanoseconds(1)
+ self.assertEqual('0.000000001s',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToNanoseconds())
+
+ message.FromNanoseconds(-1)
+ self.assertEqual('-0.000000001s',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToNanoseconds())
+
+ message.FromMicroseconds(1)
+ self.assertEqual('0.000001s',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToMicroseconds())
+
+ message.FromMicroseconds(-1)
+ self.assertEqual('-0.000001s',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToMicroseconds())
+
+ message.FromMilliseconds(1)
+ self.assertEqual('0.001s',
+ message.ToJsonString())
+ self.assertEqual(1, message.ToMilliseconds())
+
+ message.FromMilliseconds(-1)
+ self.assertEqual('-0.001s',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToMilliseconds())
+
+ message.FromSeconds(1)
+ self.assertEqual('1s', message.ToJsonString())
+ self.assertEqual(1, message.ToSeconds())
+
+ message.FromSeconds(-1)
+ self.assertEqual('-1s',
+ message.ToJsonString())
+ self.assertEqual(-1, message.ToSeconds())
+
+ # Test truncation behavior.
+ message.FromNanoseconds(1999)
+ self.assertEqual(1, message.ToMicroseconds())
+
+ # For negative values, Duration will be rounded towards 0.
+ message.FromNanoseconds(-1999)
+ self.assertEqual(-1, message.ToMicroseconds())
+
+ def testDatetimeConverison(self):
+ message = timestamp_pb2.Timestamp()
+ dt = datetime(1970, 1, 1)
+ message.FromDatetime(dt)
+ self.assertEqual(dt, message.ToDatetime())
+
+ message.FromMilliseconds(1999)
+ self.assertEqual(datetime(1970, 1, 1, 0, 0, 1, 999000),
+ message.ToDatetime())
+
+ def testTimedeltaConversion(self):
+ message = duration_pb2.Duration()
+ message.FromNanoseconds(1999999999)
+ td = message.ToTimedelta()
+ self.assertEqual(1, td.seconds)
+ self.assertEqual(999999, td.microseconds)
+
+ message.FromNanoseconds(-1999999999)
+ td = message.ToTimedelta()
+ self.assertEqual(-1, td.days)
+ self.assertEqual(86398, td.seconds)
+ self.assertEqual(1, td.microseconds)
+
+ message.FromMicroseconds(-1)
+ td = message.ToTimedelta()
+ self.assertEqual(-1, td.days)
+ self.assertEqual(86399, td.seconds)
+ self.assertEqual(999999, td.microseconds)
+ converted_message = duration_pb2.Duration()
+ converted_message.FromTimedelta(td)
+ self.assertEqual(message, converted_message)
+
+ def testInvalidTimestamp(self):
+ message = timestamp_pb2.Timestamp()
+ self.assertRaisesRegexp(
+ ValueError,
+ 'time data \'10000-01-01T00:00:00\' does not match'
+ ' format \'%Y-%m-%dT%H:%M:%S\'',
+ message.FromJsonString, '10000-01-01T00:00:00.00Z')
+ self.assertRaisesRegexp(
+ well_known_types.ParseError,
+ 'nanos 0123456789012 more than 9 fractional digits.',
+ message.FromJsonString,
+ '1970-01-01T00:00:00.0123456789012Z')
+ self.assertRaisesRegexp(
+ well_known_types.ParseError,
+ (r'Invalid timezone offset value: \+08.'),
+ message.FromJsonString,
+ '1972-01-01T01:00:00.01+08',)
+ self.assertRaisesRegexp(
+ ValueError,
+ 'year is out of range',
+ message.FromJsonString,
+ '0000-01-01T00:00:00Z')
+ message.seconds = 253402300800
+ self.assertRaisesRegexp(
+ OverflowError,
+ 'date value out of range',
+ message.ToJsonString)
+
+ def testInvalidDuration(self):
+ message = duration_pb2.Duration()
+ self.assertRaisesRegexp(
+ well_known_types.ParseError,
+ 'Duration must end with letter "s": 1.',
+ message.FromJsonString, '1')
+ self.assertRaisesRegexp(
+ well_known_types.ParseError,
+ 'Couldn\'t parse duration: 1...2s.',
+ message.FromJsonString, '1...2s')
+
+
+class FieldMaskTest(unittest.TestCase):
+
+ def testStringFormat(self):
+ mask = field_mask_pb2.FieldMask()
+ self.assertEqual('', mask.ToJsonString())
+ mask.paths.append('foo')
+ self.assertEqual('foo', mask.ToJsonString())
+ mask.paths.append('bar')
+ self.assertEqual('foo,bar', mask.ToJsonString())
+
+ mask.FromJsonString('')
+ self.assertEqual('', mask.ToJsonString())
+ mask.FromJsonString('foo')
+ self.assertEqual(['foo'], mask.paths)
+ mask.FromJsonString('foo,bar')
+ self.assertEqual(['foo', 'bar'], mask.paths)
+
+ def testDescriptorToFieldMask(self):
+ mask = field_mask_pb2.FieldMask()
+ msg_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
+ mask.AllFieldsFromDescriptor(msg_descriptor)
+ self.assertEqual(75, len(mask.paths))
+ self.assertTrue(mask.IsValidForDescriptor(msg_descriptor))
+ for field in msg_descriptor.fields:
+ self.assertTrue(field.name in mask.paths)
+ mask.paths.append('optional_nested_message.bb')
+ self.assertTrue(mask.IsValidForDescriptor(msg_descriptor))
+ mask.paths.append('repeated_nested_message.bb')
+ self.assertFalse(mask.IsValidForDescriptor(msg_descriptor))
+
+ def testCanonicalFrom(self):
+ mask = field_mask_pb2.FieldMask()
+ out_mask = field_mask_pb2.FieldMask()
+ # Paths will be sorted.
+ mask.FromJsonString('baz.quz,bar,foo')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('bar,baz.quz,foo', out_mask.ToJsonString())
+ # Duplicated paths will be removed.
+ mask.FromJsonString('foo,bar,foo')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('bar,foo', out_mask.ToJsonString())
+ # Sub-paths of other paths will be removed.
+ mask.FromJsonString('foo.b1,bar.b1,foo.b2,bar')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('bar,foo.b1,foo.b2', out_mask.ToJsonString())
+
+ # Test more deeply nested cases.
+ mask.FromJsonString(
+ 'foo.bar.baz1,foo.bar.baz2.quz,foo.bar.baz2')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('foo.bar.baz1,foo.bar.baz2',
+ out_mask.ToJsonString())
+ mask.FromJsonString(
+ 'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('foo.bar.baz1,foo.bar.baz2',
+ out_mask.ToJsonString())
+ mask.FromJsonString(
+ 'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo.bar')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('foo.bar', out_mask.ToJsonString())
+ mask.FromJsonString(
+ 'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo')
+ out_mask.CanonicalFormFromMask(mask)
+ self.assertEqual('foo', out_mask.ToJsonString())
+
+ def testUnion(self):
+ mask1 = field_mask_pb2.FieldMask()
+ mask2 = field_mask_pb2.FieldMask()
+ out_mask = field_mask_pb2.FieldMask()
+ mask1.FromJsonString('foo,baz')
+ mask2.FromJsonString('bar,quz')
+ out_mask.Union(mask1, mask2)
+ self.assertEqual('bar,baz,foo,quz', out_mask.ToJsonString())
+ # Overlap with duplicated paths.
+ mask1.FromJsonString('foo,baz.bb')
+ mask2.FromJsonString('baz.bb,quz')
+ out_mask.Union(mask1, mask2)
+ self.assertEqual('baz.bb,foo,quz', out_mask.ToJsonString())
+ # Overlap with paths covering some other paths.
+ mask1.FromJsonString('foo.bar.baz,quz')
+ mask2.FromJsonString('foo.bar,bar')
+ out_mask.Union(mask1, mask2)
+ self.assertEqual('bar,foo.bar,quz', out_mask.ToJsonString())
+
+ def testIntersect(self):
+ mask1 = field_mask_pb2.FieldMask()
+ mask2 = field_mask_pb2.FieldMask()
+ out_mask = field_mask_pb2.FieldMask()
+ # Test cases without overlapping.
+ mask1.FromJsonString('foo,baz')
+ mask2.FromJsonString('bar,quz')
+ out_mask.Intersect(mask1, mask2)
+ self.assertEqual('', out_mask.ToJsonString())
+ # Overlap with duplicated paths.
+ mask1.FromJsonString('foo,baz.bb')
+ mask2.FromJsonString('baz.bb,quz')
+ out_mask.Intersect(mask1, mask2)
+ self.assertEqual('baz.bb', out_mask.ToJsonString())
+ # Overlap with paths covering some other paths.
+ mask1.FromJsonString('foo.bar.baz,quz')
+ mask2.FromJsonString('foo.bar,bar')
+ out_mask.Intersect(mask1, mask2)
+ self.assertEqual('foo.bar.baz', out_mask.ToJsonString())
+ mask1.FromJsonString('foo.bar,bar')
+ mask2.FromJsonString('foo.bar.baz,quz')
+ out_mask.Intersect(mask1, mask2)
+ self.assertEqual('foo.bar.baz', out_mask.ToJsonString())
+
+ def testMergeMessage(self):
+ # Test merge one field.
+ src = unittest_pb2.TestAllTypes()
+ test_util.SetAllFields(src)
+ for field in src.DESCRIPTOR.fields:
+ if field.containing_oneof:
+ continue
+ field_name = field.name
+ dst = unittest_pb2.TestAllTypes()
+ # Only set one path to mask.
+ mask = field_mask_pb2.FieldMask()
+ mask.paths.append(field_name)
+ mask.MergeMessage(src, dst)
+ # The expected result message.
+ msg = unittest_pb2.TestAllTypes()
+ if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
+ repeated_src = getattr(src, field_name)
+ repeated_msg = getattr(msg, field_name)
+ if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
+ for item in repeated_src:
+ repeated_msg.add().CopyFrom(item)
+ else:
+ repeated_msg.extend(repeated_src)
+ elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
+ getattr(msg, field_name).CopyFrom(getattr(src, field_name))
+ else:
+ setattr(msg, field_name, getattr(src, field_name))
+ # Only field specified in mask is merged.
+ self.assertEqual(msg, dst)
+
+ # Test merge nested fields.
+ nested_src = unittest_pb2.NestedTestAllTypes()
+ nested_dst = unittest_pb2.NestedTestAllTypes()
+ nested_src.child.payload.optional_int32 = 1234
+ nested_src.child.child.payload.optional_int32 = 5678
+ mask = field_mask_pb2.FieldMask()
+ mask.FromJsonString('child.payload')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertEqual(1234, nested_dst.child.payload.optional_int32)
+ self.assertEqual(0, nested_dst.child.child.payload.optional_int32)
+
+ mask.FromJsonString('child.child.payload')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertEqual(1234, nested_dst.child.payload.optional_int32)
+ self.assertEqual(5678, nested_dst.child.child.payload.optional_int32)
+
+ nested_dst.Clear()
+ mask.FromJsonString('child.child.payload')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertEqual(0, nested_dst.child.payload.optional_int32)
+ self.assertEqual(5678, nested_dst.child.child.payload.optional_int32)
+
+ nested_dst.Clear()
+ mask.FromJsonString('child')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertEqual(1234, nested_dst.child.payload.optional_int32)
+ self.assertEqual(5678, nested_dst.child.child.payload.optional_int32)
+
+ # Test MergeOptions.
+ nested_dst.Clear()
+ nested_dst.child.payload.optional_int64 = 4321
+ # Message fields will be merged by default.
+ mask.FromJsonString('child.payload')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertEqual(1234, nested_dst.child.payload.optional_int32)
+ self.assertEqual(4321, nested_dst.child.payload.optional_int64)
+ # Change the behavior to replace message fields.
+ mask.FromJsonString('child.payload')
+ mask.MergeMessage(nested_src, nested_dst, True, False)
+ self.assertEqual(1234, nested_dst.child.payload.optional_int32)
+ self.assertEqual(0, nested_dst.child.payload.optional_int64)
+
+ # By default, fields missing in source are not cleared in destination.
+ nested_dst.payload.optional_int32 = 1234
+ self.assertTrue(nested_dst.HasField('payload'))
+ mask.FromJsonString('payload')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertTrue(nested_dst.HasField('payload'))
+ # But they are cleared when replacing message fields.
+ nested_dst.Clear()
+ nested_dst.payload.optional_int32 = 1234
+ mask.FromJsonString('payload')
+ mask.MergeMessage(nested_src, nested_dst, True, False)
+ self.assertFalse(nested_dst.HasField('payload'))
+
+ nested_src.payload.repeated_int32.append(1234)
+ nested_dst.payload.repeated_int32.append(5678)
+ # Repeated fields will be appended by default.
+ mask.FromJsonString('payload.repeated_int32')
+ mask.MergeMessage(nested_src, nested_dst)
+ self.assertEqual(2, len(nested_dst.payload.repeated_int32))
+ self.assertEqual(5678, nested_dst.payload.repeated_int32[0])
+ self.assertEqual(1234, nested_dst.payload.repeated_int32[1])
+ # Change the behavior to replace repeated fields.
+ mask.FromJsonString('payload.repeated_int32')
+ mask.MergeMessage(nested_src, nested_dst, False, True)
+ self.assertEqual(1, len(nested_dst.payload.repeated_int32))
+ self.assertEqual(1234, nested_dst.payload.repeated_int32[0])
+
+
+class StructTest(unittest.TestCase):
+
+ def testStruct(self):
+ struct = struct_pb2.Struct()
+ struct_class = struct.__class__
+
+ struct['key1'] = 5
+ struct['key2'] = 'abc'
+ struct['key3'] = True
+ struct.get_or_create_struct('key4')['subkey'] = 11.0
+ struct_list = struct.get_or_create_list('key5')
+ struct_list.extend([6, 'seven', True, False, None])
+ struct_list.add_struct()['subkey2'] = 9
+
+ self.assertTrue(isinstance(struct, well_known_types.Struct))
+ self.assertEquals(5, struct['key1'])
+ self.assertEquals('abc', struct['key2'])
+ self.assertIs(True, struct['key3'])
+ self.assertEquals(11, struct['key4']['subkey'])
+ inner_struct = struct_class()
+ inner_struct['subkey2'] = 9
+ self.assertEquals([6, 'seven', True, False, None, inner_struct],
+ list(struct['key5'].items()))
+
+ serialized = struct.SerializeToString()
+
+ struct2 = struct_pb2.Struct()
+ struct2.ParseFromString(serialized)
+
+ self.assertEquals(struct, struct2)
+
+ self.assertTrue(isinstance(struct2, well_known_types.Struct))
+ self.assertEquals(5, struct2['key1'])
+ self.assertEquals('abc', struct2['key2'])
+ self.assertIs(True, struct2['key3'])
+ self.assertEquals(11, struct2['key4']['subkey'])
+ self.assertEquals([6, 'seven', True, False, None, inner_struct],
+ list(struct2['key5'].items()))
+
+ struct_list = struct2['key5']
+ self.assertEquals(6, struct_list[0])
+ self.assertEquals('seven', struct_list[1])
+ self.assertEquals(True, struct_list[2])
+ self.assertEquals(False, struct_list[3])
+ self.assertEquals(None, struct_list[4])
+ self.assertEquals(inner_struct, struct_list[5])
+
+ struct_list[1] = 7
+ self.assertEquals(7, struct_list[1])
+
+ struct_list.add_list().extend([1, 'two', True, False, None])
+ self.assertEquals([1, 'two', True, False, None],
+ list(struct_list[6].items()))
+
+ text_serialized = str(struct)
+ struct3 = struct_pb2.Struct()
+ text_format.Merge(text_serialized, struct3)
+ self.assertEquals(struct, struct3)
+
+ struct.get_or_create_struct('key3')['replace'] = 12
+ self.assertEquals(12, struct['key3']['replace'])
+
+
+class AnyTest(unittest.TestCase):
+
+ def testAnyMessage(self):
+ # Creates and sets message.
+ msg = any_test_pb2.TestAny()
+ msg_descriptor = msg.DESCRIPTOR
+ all_types = unittest_pb2.TestAllTypes()
+ all_descriptor = all_types.DESCRIPTOR
+ all_types.repeated_string.append(u'\u00fc\ua71f')
+ # Packs to Any.
+ msg.value.Pack(all_types)
+ self.assertEqual(msg.value.type_url,
+ 'type.googleapis.com/%s' % all_descriptor.full_name)
+ self.assertEqual(msg.value.value,
+ all_types.SerializeToString())
+ # Tests Is() method.
+ self.assertTrue(msg.value.Is(all_descriptor))
+ self.assertFalse(msg.value.Is(msg_descriptor))
+ # Unpacks Any.
+ unpacked_message = unittest_pb2.TestAllTypes()
+ self.assertTrue(msg.value.Unpack(unpacked_message))
+ self.assertEqual(all_types, unpacked_message)
+ # Unpacks to different type.
+ self.assertFalse(msg.value.Unpack(msg))
+ # Only Any messages have Pack method.
+ try:
+ msg.Pack(all_types)
+ except AttributeError:
+ pass
+ else:
+ raise AttributeError('%s should not have Pack method.' %
+ msg_descriptor.full_name)
+
+ def testPackWithCustomTypeUrl(self):
+ submessage = any_test_pb2.TestAny()
+ submessage.int_value = 12345
+ msg = any_pb2.Any()
+ # Pack with a custom type URL prefix.
+ msg.Pack(submessage, 'type.myservice.com')
+ self.assertEqual(msg.type_url,
+ 'type.myservice.com/%s' % submessage.DESCRIPTOR.full_name)
+ # Pack with a custom type URL prefix ending with '/'.
+ msg.Pack(submessage, 'type.myservice.com/')
+ self.assertEqual(msg.type_url,
+ 'type.myservice.com/%s' % submessage.DESCRIPTOR.full_name)
+ # Pack with an empty type URL prefix.
+ msg.Pack(submessage, '')
+ self.assertEqual(msg.type_url,
+ '/%s' % submessage.DESCRIPTOR.full_name)
+ # Test unpacking the type.
+ unpacked_message = any_test_pb2.TestAny()
+ self.assertTrue(msg.Unpack(unpacked_message))
+ self.assertEqual(submessage, unpacked_message)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/python/google/protobuf/json_format.py b/python/google/protobuf/json_format.py
index d95557d7..23382bdb 100644
--- a/python/google/protobuf/json_format.py
+++ b/python/google/protobuf/json_format.py
@@ -28,22 +28,30 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Contains routines for printing protocol messages in JSON format."""
+"""Contains routines for printing protocol messages in JSON format.
+
+Simple usage example:
+
+ # Create a proto object and serialize it to a json format string.
+ message = my_proto_pb2.MyMessage(foo='bar')
+ json_string = json_format.MessageToJson(message)
+
+ # Parse a json format string to proto object.
+ message = json_format.Parse(json_string, my_proto_pb2.MyMessage())
+"""
__author__ = 'jieluo@google.com (Jie Luo)'
import base64
-from datetime import datetime
import json
import math
-import re
+import six
import sys
from google.protobuf import descriptor
+from google.protobuf import symbol_database
_TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S'
-_NUMBER = re.compile(u'[0-9+-][0-9e.+-]*')
-_INTEGER = re.compile(u'[0-9+-]')
_INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32,
descriptor.FieldDescriptor.CPPTYPE_UINT32,
descriptor.FieldDescriptor.CPPTYPE_INT64,
@@ -52,17 +60,20 @@ _INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64,
descriptor.FieldDescriptor.CPPTYPE_UINT64])
_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
-if str is bytes:
- _UNICODETYPE = unicode
-else:
- _UNICODETYPE = str
+_INFINITY = 'Infinity'
+_NEG_INFINITY = '-Infinity'
+_NAN = 'NaN'
+
+
+class Error(Exception):
+ """Top-level module error for json_format."""
-class SerializeToJsonError(Exception):
+class SerializeToJsonError(Error):
"""Thrown if serialization to JSON fails."""
-class ParseError(Exception):
+class ParseError(Error):
"""Thrown in case of parsing error."""
@@ -86,15 +97,15 @@ def MessageToJson(message, including_default_value_fields=False):
def _MessageToJsonObject(message, including_default_value_fields):
"""Converts message to an object according to Proto3 JSON Specification."""
message_descriptor = message.DESCRIPTOR
- if _IsTimestampMessage(message_descriptor):
- return _TimestampMessageToJsonObject(message)
- if _IsDurationMessage(message_descriptor):
- return _DurationMessageToJsonObject(message)
- if _IsFieldMaskMessage(message_descriptor):
- return _FieldMaskMessageToJsonObject(message)
+ full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
return _WrapperMessageToJsonObject(message)
- return _RegularMessageToJsonObject(message, including_default_value_fields)
+ if full_name in _WKTJSONMETHODS:
+ return _WKTJSONMETHODS[full_name][0](
+ message, including_default_value_fields)
+ js = {}
+ return _RegularMessageToJsonObject(
+ message, js, including_default_value_fields)
def _IsMapEntry(field):
@@ -103,16 +114,17 @@ def _IsMapEntry(field):
field.message_type.GetOptions().map_entry)
-def _RegularMessageToJsonObject(message, including_default_value_fields):
+def _RegularMessageToJsonObject(message, js, including_default_value_fields):
"""Converts normal message according to Proto3 JSON Specification."""
- js = {}
fields = message.ListFields()
+ include_default = including_default_value_fields
try:
for field, value in fields:
name = field.camelcase_name
if _IsMapEntry(field):
# Convert a map field.
+ v_field = field.message_type.fields_by_name['value']
js_map = {}
for key in value:
if isinstance(key, bool):
@@ -122,20 +134,15 @@ def _RegularMessageToJsonObject(message, including_default_value_fields):
recorded_key = 'false'
else:
recorded_key = key
- js_map[recorded_key] = _ConvertFieldToJsonObject(
- field.message_type.fields_by_name['value'],
- value[key], including_default_value_fields)
+ js_map[recorded_key] = _FieldToJsonObject(
+ v_field, value[key], including_default_value_fields)
js[name] = js_map
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
# Convert a repeated field.
- repeated = []
- for element in value:
- repeated.append(_ConvertFieldToJsonObject(
- field, element, including_default_value_fields))
- js[name] = repeated
+ js[name] = [_FieldToJsonObject(field, k, include_default)
+ for k in value]
else:
- js[name] = _ConvertFieldToJsonObject(
- field, value, including_default_value_fields)
+ js[name] = _FieldToJsonObject(field, value, include_default)
# Serialize default value if including_default_value_fields is True.
if including_default_value_fields:
@@ -155,16 +162,16 @@ def _RegularMessageToJsonObject(message, including_default_value_fields):
elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
js[name] = []
else:
- js[name] = _ConvertFieldToJsonObject(field, field.default_value)
+ js[name] = _FieldToJsonObject(field, field.default_value)
except ValueError as e:
raise SerializeToJsonError(
- 'Failed to serialize {0} field: {1}'.format(field.name, e))
+ 'Failed to serialize {0} field: {1}.'.format(field.name, e))
return js
-def _ConvertFieldToJsonObject(
+def _FieldToJsonObject(
field, value, including_default_value_fields=False):
"""Converts field value according to Proto3 JSON Specification."""
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
@@ -183,93 +190,91 @@ def _ConvertFieldToJsonObject(
else:
return value
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
- if value:
- return True
- else:
- return False
+ return bool(value)
elif field.cpp_type in _INT64_TYPES:
return str(value)
elif field.cpp_type in _FLOAT_TYPES:
if math.isinf(value):
if value < 0.0:
- return '-Infinity'
+ return _NEG_INFINITY
else:
- return 'Infinity'
+ return _INFINITY
if math.isnan(value):
- return 'NaN'
+ return _NAN
return value
-def _IsTimestampMessage(message_descriptor):
- return (message_descriptor.name == 'Timestamp' and
- message_descriptor.file.name == 'google/protobuf/timestamp.proto')
-
-
-def _TimestampMessageToJsonObject(message):
- """Converts Timestamp message according to Proto3 JSON Specification."""
- nanos = message.nanos % 1e9
- dt = datetime.utcfromtimestamp(
- message.seconds + (message.nanos - nanos) / 1e9)
- result = dt.isoformat()
- if (nanos % 1e9) == 0:
- # If there are 0 fractional digits, the fractional
- # point '.' should be omitted when serializing.
- return result + 'Z'
- if (nanos % 1e6) == 0:
- # Serialize 3 fractional digits.
- return result + '.%03dZ' % (nanos / 1e6)
- if (nanos % 1e3) == 0:
- # Serialize 6 fractional digits.
- return result + '.%06dZ' % (nanos / 1e3)
- # Serialize 9 fractional digits.
- return result + '.%09dZ' % nanos
-
-
-def _IsDurationMessage(message_descriptor):
- return (message_descriptor.name == 'Duration' and
- message_descriptor.file.name == 'google/protobuf/duration.proto')
-
-
-def _DurationMessageToJsonObject(message):
- """Converts Duration message according to Proto3 JSON Specification."""
- if message.seconds < 0 or message.nanos < 0:
- result = '-'
- seconds = - message.seconds + int((0 - message.nanos) / 1e9)
- nanos = (0 - message.nanos) % 1e9
+def _AnyMessageToJsonObject(message, including_default):
+ """Converts Any message according to Proto3 JSON Specification."""
+ if not message.ListFields():
+ return {}
+ js = {}
+ type_url = message.type_url
+ js['@type'] = type_url
+ sub_message = _CreateMessageFromTypeUrl(type_url)
+ sub_message.ParseFromString(message.value)
+ message_descriptor = sub_message.DESCRIPTOR
+ full_name = message_descriptor.full_name
+ if _IsWrapperMessage(message_descriptor):
+ js['value'] = _WrapperMessageToJsonObject(sub_message)
+ return js
+ if full_name in _WKTJSONMETHODS:
+ js['value'] = _WKTJSONMETHODS[full_name][0](sub_message, including_default)
+ return js
+ return _RegularMessageToJsonObject(sub_message, js, including_default)
+
+
+def _CreateMessageFromTypeUrl(type_url):
+ # TODO(jieluo): Should add a way that users can register the type resolver
+ # instead of the default one.
+ db = symbol_database.Default()
+ type_name = type_url.split('/')[-1]
+ try:
+ message_descriptor = db.pool.FindMessageTypeByName(type_name)
+ except KeyError:
+ raise TypeError(
+ 'Can not find message descriptor by type_url: {0}.'.format(type_url))
+ message_class = db.GetPrototype(message_descriptor)
+ return message_class()
+
+
+def _GenericMessageToJsonObject(message, unused_including_default):
+ """Converts message by ToJsonString according to Proto3 JSON Specification."""
+ # Duration, Timestamp and FieldMask have ToJsonString method to do the
+ # convert. Users can also call the method directly.
+ return message.ToJsonString()
+
+
+def _ValueMessageToJsonObject(message, unused_including_default=False):
+ """Converts Value message according to Proto3 JSON Specification."""
+ which = message.WhichOneof('kind')
+ # If the Value message is not set treat as null_value when serialize
+ # to JSON. The parse back result will be different from original message.
+ if which is None or which == 'null_value':
+ return None
+ if which == 'list_value':
+ return _ListValueMessageToJsonObject(message.list_value)
+ if which == 'struct_value':
+ value = message.struct_value
else:
- result = ''
- seconds = message.seconds + int(message.nanos / 1e9)
- nanos = message.nanos % 1e9
- result += '%d' % seconds
- if (nanos % 1e9) == 0:
- # If there are 0 fractional digits, the fractional
- # point '.' should be omitted when serializing.
- return result + 's'
- if (nanos % 1e6) == 0:
- # Serialize 3 fractional digits.
- return result + '.%03ds' % (nanos / 1e6)
- if (nanos % 1e3) == 0:
- # Serialize 6 fractional digits.
- return result + '.%06ds' % (nanos / 1e3)
- # Serialize 9 fractional digits.
- return result + '.%09ds' % nanos
-
-
-def _IsFieldMaskMessage(message_descriptor):
- return (message_descriptor.name == 'FieldMask' and
- message_descriptor.file.name == 'google/protobuf/field_mask.proto')
-
-
-def _FieldMaskMessageToJsonObject(message):
- """Converts FieldMask message according to Proto3 JSON Specification."""
- result = ''
- first = True
- for path in message.paths:
- if not first:
- result += ','
- result += path
- first = False
- return result
+ value = getattr(message, which)
+ oneof_descriptor = message.DESCRIPTOR.fields_by_name[which]
+ return _FieldToJsonObject(oneof_descriptor, value)
+
+
+def _ListValueMessageToJsonObject(message, unused_including_default=False):
+ """Converts ListValue message according to Proto3 JSON Specification."""
+ return [_ValueMessageToJsonObject(value)
+ for value in message.values]
+
+
+def _StructMessageToJsonObject(message, unused_including_default=False):
+ """Converts Struct message according to Proto3 JSON Specification."""
+ fields = message.fields
+ js = {}
+ for key in fields.keys():
+ js[key] = _ValueMessageToJsonObject(fields[key])
+ return js
def _IsWrapperMessage(message_descriptor):
@@ -277,7 +282,7 @@ def _IsWrapperMessage(message_descriptor):
def _WrapperMessageToJsonObject(message):
- return _ConvertFieldToJsonObject(
+ return _FieldToJsonObject(
message.DESCRIPTOR.fields_by_name['value'], message.value)
@@ -285,7 +290,7 @@ def _DuplicateChecker(js):
result = {}
for name, value in js:
if name in result:
- raise ParseError('Failed to load JSON: duplicate key ' + name)
+ raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name))
result[name] = value
return result
@@ -303,7 +308,7 @@ def Parse(text, message):
Raises::
ParseError: On JSON parsing problems.
"""
- if not isinstance(text, _UNICODETYPE): text = text.decode('utf-8')
+ if not isinstance(text, six.text_type): text = text.decode('utf-8')
try:
if sys.version_info < (2, 7):
# object_pair_hook is not supported before python2.7
@@ -311,8 +316,8 @@ def Parse(text, message):
else:
js = json.loads(text, object_pairs_hook=_DuplicateChecker)
except ValueError as e:
- raise ParseError('Failed to load JSON: ' + str(e))
- _ConvertFieldValuePair(js, message)
+ raise ParseError('Failed to load JSON: {0}.'.format(str(e)))
+ _ConvertMessage(js, message)
return message
@@ -362,14 +367,23 @@ def _ConvertFieldValuePair(js, message):
message.ClearField(field.name)
if not isinstance(value, list):
raise ParseError('repeated field {0} must be in [] which is '
- '{1}'.format(name, value))
- for item in value:
- if item is None:
- continue
- if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
+ '{1}.'.format(name, value))
+ if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
+ # Repeated message field.
+ for item in value:
sub_message = getattr(message, field.name).add()
+ # None is a null_value in Value.
+ if (item is None and
+ sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'):
+ raise ParseError('null is not allowed to be used as an element'
+ ' in a repeated field.')
_ConvertMessage(item, sub_message)
- else:
+ else:
+ # Repeated scalar field.
+ for item in value:
+ if item is None:
+ raise ParseError('null is not allowed to be used as an element'
+ ' in a repeated field.')
getattr(message, field.name).append(
_ConvertScalarFieldValue(item, field))
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
@@ -383,9 +397,9 @@ def _ConvertFieldValuePair(js, message):
else:
raise ParseError(str(e))
except ValueError as e:
- raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
+ raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
except TypeError as e:
- raise ParseError('Failed to parse {0} field: {1}'.format(name, e))
+ raise ParseError('Failed to parse {0} field: {1}.'.format(name, e))
def _ConvertMessage(value, message):
@@ -399,86 +413,85 @@ def _ConvertMessage(value, message):
ParseError: In case of convert problems.
"""
message_descriptor = message.DESCRIPTOR
- if _IsTimestampMessage(message_descriptor):
- _ConvertTimestampMessage(value, message)
- elif _IsDurationMessage(message_descriptor):
- _ConvertDurationMessage(value, message)
- elif _IsFieldMaskMessage(message_descriptor):
- _ConvertFieldMaskMessage(value, message)
- elif _IsWrapperMessage(message_descriptor):
+ full_name = message_descriptor.full_name
+ if _IsWrapperMessage(message_descriptor):
_ConvertWrapperMessage(value, message)
+ elif full_name in _WKTJSONMETHODS:
+ _WKTJSONMETHODS[full_name][1](value, message)
else:
_ConvertFieldValuePair(value, message)
-def _ConvertTimestampMessage(value, message):
- """Convert a JSON representation into Timestamp message."""
- timezone_offset = value.find('Z')
- if timezone_offset == -1:
- timezone_offset = value.find('+')
- if timezone_offset == -1:
- timezone_offset = value.rfind('-')
- if timezone_offset == -1:
- raise ParseError(
- 'Failed to parse timestamp: missing valid timezone offset.')
- time_value = value[0:timezone_offset]
- # Parse datetime and nanos
- point_position = time_value.find('.')
- if point_position == -1:
- second_value = time_value
- nano_value = ''
- else:
- second_value = time_value[:point_position]
- nano_value = time_value[point_position + 1:]
- date_object = datetime.strptime(second_value, _TIMESTAMPFOMAT)
- td = date_object - datetime(1970, 1, 1)
- seconds = td.seconds + td.days * 24 * 3600
- if len(nano_value) > 9:
- raise ParseError(
- 'Failed to parse Timestamp: nanos {0} more than '
- '9 fractional digits.'.format(nano_value))
- if nano_value:
- nanos = round(float('0.' + nano_value) * 1e9)
+def _ConvertAnyMessage(value, message):
+ """Convert a JSON representation into Any message."""
+ if isinstance(value, dict) and not value:
+ return
+ try:
+ type_url = value['@type']
+ except KeyError:
+ raise ParseError('@type is missing when parsing any message.')
+
+ sub_message = _CreateMessageFromTypeUrl(type_url)
+ message_descriptor = sub_message.DESCRIPTOR
+ full_name = message_descriptor.full_name
+ if _IsWrapperMessage(message_descriptor):
+ _ConvertWrapperMessage(value['value'], sub_message)
+ elif full_name in _WKTJSONMETHODS:
+ _WKTJSONMETHODS[full_name][1](value['value'], sub_message)
else:
- nanos = 0
- # Parse timezone offsets
- if value[timezone_offset] == 'Z':
- if len(value) != timezone_offset + 1:
- raise ParseError(
- 'Failed to parse timestamp: invalid trailing data {0}.'.format(value))
+ del value['@type']
+ _ConvertFieldValuePair(value, sub_message)
+ # Sets Any message
+ message.value = sub_message.SerializeToString()
+ message.type_url = type_url
+
+
+def _ConvertGenericMessage(value, message):
+ """Convert a JSON representation into message with FromJsonString."""
+ # Durantion, Timestamp, FieldMask have FromJsonString method to do the
+ # convert. Users can also call the method directly.
+ message.FromJsonString(value)
+
+
+_INT_OR_FLOAT = six.integer_types + (float,)
+
+
+def _ConvertValueMessage(value, message):
+ """Convert a JSON representation into Value message."""
+ if isinstance(value, dict):
+ _ConvertStructMessage(value, message.struct_value)
+ elif isinstance(value, list):
+ _ConvertListValueMessage(value, message.list_value)
+ elif value is None:
+ message.null_value = 0
+ elif isinstance(value, bool):
+ message.bool_value = value
+ elif isinstance(value, six.string_types):
+ message.string_value = value
+ elif isinstance(value, _INT_OR_FLOAT):
+ message.number_value = value
else:
- timezone = value[timezone_offset:]
- pos = timezone.find(':')
- if pos == -1:
- raise ParseError(
- 'Invalid timezone offset value: ' + timezone)
- if timezone[0] == '+':
- seconds += (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60
- else:
- seconds -= (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60
- # Set seconds and nanos
- message.seconds = int(seconds)
- message.nanos = int(nanos)
+ raise ParseError('Unexpected type for Value message.')
-def _ConvertDurationMessage(value, message):
- """Convert a JSON representation into Duration message."""
- if value[-1] != 's':
- raise ParseError(
- 'Duration must end with letter "s": ' + value)
- try:
- duration = float(value[:-1])
- except ValueError:
+def _ConvertListValueMessage(value, message):
+ """Convert a JSON representation into ListValue message."""
+ if not isinstance(value, list):
raise ParseError(
- 'Couldn\'t parse duration: ' + value)
- message.seconds = int(duration)
- message.nanos = int(round((duration - message.seconds) * 1e9))
+ 'ListValue must be in [] which is {0}.'.format(value))
+ message.ClearField('values')
+ for item in value:
+ _ConvertValueMessage(item, message.values.add())
-def _ConvertFieldMaskMessage(value, message):
- """Convert a JSON representation into FieldMask message."""
- for path in value.split(','):
- message.paths.append(path)
+def _ConvertStructMessage(value, message):
+ """Convert a JSON representation into Struct message."""
+ if not isinstance(value, dict):
+ raise ParseError(
+ 'Struct must be in a dict which is {0}.'.format(value))
+ for key in value:
+ _ConvertValueMessage(value[key], message.fields[key])
+ return
def _ConvertWrapperMessage(value, message):
@@ -500,7 +513,8 @@ def _ConvertMapFieldValue(value, message, field):
"""
if not isinstance(value, dict):
raise ParseError(
- 'Map fieled {0} must be in {} which is {1}.'.format(field.name, value))
+ 'Map field {0} must be in a dict which is {1}.'.format(
+ field.name, value))
key_field = field.message_type.fields_by_name['key']
value_field = field.message_type.fields_by_name['value']
for key in value:
@@ -512,13 +526,13 @@ def _ConvertMapFieldValue(value, message, field):
value[key], value_field)
-def _ConvertScalarFieldValue(value, field, require_quote=False):
+def _ConvertScalarFieldValue(value, field, require_str=False):
"""Convert a single scalar field value.
Args:
value: A scalar value to convert the scalar field value.
field: The descriptor of the field to convert.
- require_quote: If True, '"' is required for the field value.
+ require_str: If True, the field value must be a str.
Returns:
The converted scalar field value
@@ -531,7 +545,7 @@ def _ConvertScalarFieldValue(value, field, require_quote=False):
elif field.cpp_type in _FLOAT_TYPES:
return _ConvertFloat(value)
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
- return _ConvertBool(value, require_quote)
+ return _ConvertBool(value, require_str)
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
return base64.b64decode(value)
@@ -561,10 +575,10 @@ def _ConvertInteger(value):
ParseError: If an integer couldn't be consumed.
"""
if isinstance(value, float):
- raise ParseError('Couldn\'t parse integer: {0}'.format(value))
+ raise ParseError('Couldn\'t parse integer: {0}.'.format(value))
- if isinstance(value, _UNICODETYPE) and not _INTEGER.match(value):
- raise ParseError('Couldn\'t parse integer: "{0}"'.format(value))
+ if isinstance(value, six.text_type) and value.find(' ') != -1:
+ raise ParseError('Couldn\'t parse integer: "{0}".'.format(value))
return int(value)
@@ -572,28 +586,28 @@ def _ConvertInteger(value):
def _ConvertFloat(value):
"""Convert an floating point number."""
if value == 'nan':
- raise ParseError('Couldn\'t parse float "nan", use "NaN" instead')
+ raise ParseError('Couldn\'t parse float "nan", use "NaN" instead.')
try:
# Assume Python compatible syntax.
return float(value)
except ValueError:
# Check alternative spellings.
- if value == '-Infinity':
+ if value == _NEG_INFINITY:
return float('-inf')
- elif value == 'Infinity':
+ elif value == _INFINITY:
return float('inf')
- elif value == 'NaN':
+ elif value == _NAN:
return float('nan')
else:
- raise ParseError('Couldn\'t parse float: {0}'.format(value))
+ raise ParseError('Couldn\'t parse float: {0}.'.format(value))
-def _ConvertBool(value, require_quote):
+def _ConvertBool(value, require_str):
"""Convert a boolean value.
Args:
value: A scalar value to convert.
- require_quote: If True, '"' is required for the boolean value.
+ require_str: If True, value must be a str.
Returns:
The bool parsed.
@@ -601,14 +615,31 @@ def _ConvertBool(value, require_quote):
Raises:
ParseError: If a boolean value couldn't be consumed.
"""
- if require_quote:
+ if require_str:
if value == 'true':
return True
elif value == 'false':
return False
else:
- raise ParseError('Expect "true" or "false", not {0}.'.format(value))
+ raise ParseError('Expected "true" or "false", not {0}.'.format(value))
if not isinstance(value, bool):
raise ParseError('Expected true or false without quotes.')
return value
+
+_WKTJSONMETHODS = {
+ 'google.protobuf.Any': [_AnyMessageToJsonObject,
+ _ConvertAnyMessage],
+ 'google.protobuf.Duration': [_GenericMessageToJsonObject,
+ _ConvertGenericMessage],
+ 'google.protobuf.FieldMask': [_GenericMessageToJsonObject,
+ _ConvertGenericMessage],
+ 'google.protobuf.ListValue': [_ListValueMessageToJsonObject,
+ _ConvertListValueMessage],
+ 'google.protobuf.Struct': [_StructMessageToJsonObject,
+ _ConvertStructMessage],
+ 'google.protobuf.Timestamp': [_GenericMessageToJsonObject,
+ _ConvertGenericMessage],
+ 'google.protobuf.Value': [_ValueMessageToJsonObject,
+ _ConvertValueMessage]
+}
diff --git a/python/google/protobuf/message_factory.py b/python/google/protobuf/message_factory.py
index 9cd9c2a8..1b059d13 100644
--- a/python/google/protobuf/message_factory.py
+++ b/python/google/protobuf/message_factory.py
@@ -39,7 +39,6 @@ my_proto_instance = message_classes['some.proto.package.MessageName']()
__author__ = 'matthewtoia@google.com (Matt Toia)'
-from google.protobuf import descriptor_database
from google.protobuf import descriptor_pool
from google.protobuf import message
from google.protobuf import reflection
@@ -50,8 +49,7 @@ class MessageFactory(object):
def __init__(self, pool=None):
"""Initializes a new factory."""
- self.pool = (pool or descriptor_pool.DescriptorPool(
- descriptor_database.DescriptorDatabase()))
+ self.pool = pool or descriptor_pool.DescriptorPool()
# local cache of all classes built from protobuf descriptors
self._classes = {}
diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc
index 61a3d237..a875a7be 100644
--- a/python/google/protobuf/pyext/descriptor.cc
+++ b/python/google/protobuf/pyext/descriptor.cc
@@ -203,6 +203,14 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
PyObject* message_class(cdescriptor_pool::GetMessageClass(
pool, message_type));
if (message_class == NULL) {
+ // The Options message was not found in the current DescriptorPool.
+ // In this case, there cannot be extensions to these options, and we can
+ // try to use the basic pool instead.
+ PyErr_Clear();
+ message_class = cdescriptor_pool::GetMessageClass(
+ GetDefaultDescriptorPool(), message_type);
+ }
+ if (message_class == NULL) {
PyErr_Format(PyExc_TypeError, "Could not retrieve class for Options: %s",
message_type->full_name().c_str());
return NULL;
@@ -211,6 +219,12 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
if (value == NULL) {
return NULL;
}
+ if (!PyObject_TypeCheck(value.get(), &CMessage_Type)) {
+ PyErr_Format(PyExc_TypeError, "Invalid class for %s: %s",
+ message_type->full_name().c_str(),
+ Py_TYPE(value.get())->tp_name);
+ return NULL;
+ }
CMessage* cmsg = reinterpret_cast<CMessage*>(value.get());
const Reflection* reflection = options.GetReflection();
@@ -327,7 +341,8 @@ PyObject* NewInternedDescriptor(PyTypeObject* type,
PyDescriptorPool* pool = GetDescriptorPool_FromPool(
GetFileDescriptor(descriptor)->pool());
if (pool == NULL) {
- Py_DECREF(py_descriptor);
+ // Don't DECREF, the object is not fully initialized.
+ PyObject_Del(py_descriptor);
return NULL;
}
Py_INCREF(pool);
@@ -1213,6 +1228,13 @@ static void Dealloc(PyFileDescriptor* self) {
descriptor::Dealloc(&self->base);
}
+static PyObject* GetPool(PyFileDescriptor *self, void *closure) {
+ PyObject* pool = reinterpret_cast<PyObject*>(
+ GetDescriptorPool_FromPool(_GetDescriptor(self)->pool()));
+ Py_XINCREF(pool);
+ return pool;
+}
+
static PyObject* GetName(PyFileDescriptor *self, void *closure) {
return PyString_FromCppString(_GetDescriptor(self)->name());
}
@@ -1292,6 +1314,7 @@ static PyObject* CopyToProto(PyFileDescriptor *self, PyObject *target) {
}
static PyGetSetDef Getters[] = {
+ { "pool", (getter)GetPool, NULL, "pool"},
{ "name", (getter)GetName, NULL, "name"},
{ "package", (getter)GetPackage, NULL, "package"},
{ "serialized_pb", (getter)GetSerializedPb},
@@ -1354,8 +1377,8 @@ PyTypeObject PyFileDescriptor_Type = {
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
- PyType_GenericAlloc, // tp_alloc
- PyType_GenericNew, // tp_new
+ 0, // tp_alloc
+ 0, // tp_new
PyObject_Del, // tp_free
};
diff --git a/python/google/protobuf/pyext/descriptor_database.cc b/python/google/protobuf/pyext/descriptor_database.cc
new file mode 100644
index 00000000..514722b4
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_database.cc
@@ -0,0 +1,145 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file defines a C++ DescriptorDatabase, which wraps a Python Database
+// and delegate all its operations to Python methods.
+
+#include <google/protobuf/pyext/descriptor_database.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+PyDescriptorDatabase::PyDescriptorDatabase(PyObject* py_database)
+ : py_database_(py_database) {
+ Py_INCREF(py_database_);
+}
+
+PyDescriptorDatabase::~PyDescriptorDatabase() { Py_DECREF(py_database_); }
+
+// Convert a Python object to a FileDescriptorProto pointer.
+// Handles all kinds of Python errors, which are simply logged.
+static bool GetFileDescriptorProto(PyObject* py_descriptor,
+ FileDescriptorProto* output) {
+ if (py_descriptor == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ // Expected error: item was simply not found.
+ PyErr_Clear();
+ } else {
+ GOOGLE_LOG(ERROR) << "DescriptorDatabase method raised an error";
+ PyErr_Print();
+ }
+ return false;
+ }
+ const Descriptor* filedescriptor_descriptor =
+ FileDescriptorProto::default_instance().GetDescriptor();
+ CMessage* message = reinterpret_cast<CMessage*>(py_descriptor);
+ if (PyObject_TypeCheck(py_descriptor, &CMessage_Type) &&
+ message->message->GetDescriptor() == filedescriptor_descriptor) {
+ // Fast path: Just use the pointer.
+ FileDescriptorProto* file_proto =
+ static_cast<FileDescriptorProto*>(message->message);
+ *output = *file_proto;
+ return true;
+ } else {
+ // Slow path: serialize the message. This allows to use databases which
+ // use a different implementation of FileDescriptorProto.
+ ScopedPyObjectPtr serialized_pb(
+ PyObject_CallMethod(py_descriptor, "SerializeToString", NULL));
+ if (serialized_pb == NULL) {
+ GOOGLE_LOG(ERROR)
+ << "DescriptorDatabase method did not return a FileDescriptorProto";
+ PyErr_Print();
+ return false;
+ }
+ char* str;
+ Py_ssize_t len;
+ if (PyBytes_AsStringAndSize(serialized_pb.get(), &str, &len) < 0) {
+ GOOGLE_LOG(ERROR)
+ << "DescriptorDatabase method did not return a FileDescriptorProto";
+ PyErr_Print();
+ return false;
+ }
+ FileDescriptorProto file_proto;
+ if (!file_proto.ParseFromArray(str, len)) {
+ GOOGLE_LOG(ERROR)
+ << "DescriptorDatabase method did not return a FileDescriptorProto";
+ return false;
+ }
+ *output = file_proto;
+ return true;
+ }
+}
+
+// Find a file by file name.
+bool PyDescriptorDatabase::FindFileByName(const string& filename,
+ FileDescriptorProto* output) {
+ ScopedPyObjectPtr py_descriptor(PyObject_CallMethod(
+ py_database_, "FindFileByName", "s#", filename.c_str(), filename.size()));
+ return GetFileDescriptorProto(py_descriptor.get(), output);
+}
+
+// Find the file that declares the given fully-qualified symbol name.
+bool PyDescriptorDatabase::FindFileContainingSymbol(
+ const string& symbol_name, FileDescriptorProto* output) {
+ ScopedPyObjectPtr py_descriptor(
+ PyObject_CallMethod(py_database_, "FindFileContainingSymbol", "s#",
+ symbol_name.c_str(), symbol_name.size()));
+ return GetFileDescriptorProto(py_descriptor.get(), output);
+}
+
+// Find the file which defines an extension extending the given message type
+// with the given field number.
+// Python DescriptorDatabases are not required to implement this method.
+bool PyDescriptorDatabase::FindFileContainingExtension(
+ const string& containing_type, int field_number,
+ FileDescriptorProto* output) {
+ ScopedPyObjectPtr py_method(
+ PyObject_GetAttrString(py_database_, "FindFileContainingExtension"));
+ if (py_method == NULL) {
+ // This method is not implemented, returns without error.
+ PyErr_Clear();
+ return false;
+ }
+ ScopedPyObjectPtr py_descriptor(
+ PyObject_CallFunction(py_method.get(), "s#i", containing_type.c_str(),
+ containing_type.size(), field_number));
+ return GetFileDescriptorProto(py_descriptor.get(), output);
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/descriptor_database.h b/python/google/protobuf/pyext/descriptor_database.h
new file mode 100644
index 00000000..fc71c4bc
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_database.h
@@ -0,0 +1,75 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
+
+#include <Python.h>
+
+#include <google/protobuf/descriptor_database.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+class PyDescriptorDatabase : public DescriptorDatabase {
+ public:
+ explicit PyDescriptorDatabase(PyObject* py_database);
+ ~PyDescriptorDatabase();
+
+ // Implement the abstract interface. All these functions fill the output
+ // with a copy of FileDescriptorProto.
+
+ // Find a file by file name.
+ bool FindFileByName(const string& filename,
+ FileDescriptorProto* output);
+
+ // Find the file that declares the given fully-qualified symbol name.
+ bool FindFileContainingSymbol(const string& symbol_name,
+ FileDescriptorProto* output);
+
+ // Find the file which defines an extension extending the given message type
+ // with the given field number.
+ // Containing_type must be a fully-qualified type name.
+ // Python objects are not required to implement this method.
+ bool FindFileContainingExtension(const string& containing_type,
+ int field_number,
+ FileDescriptorProto* output);
+
+ private:
+ // The python object that implements the database. The reference is owned.
+ PyObject* py_database_;
+};
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc
index 0f7487fa..0bc76bc9 100644
--- a/python/google/protobuf/pyext/descriptor_pool.cc
+++ b/python/google/protobuf/pyext/descriptor_pool.cc
@@ -34,8 +34,9 @@
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/pyext/descriptor_pool.h>
#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_database.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
#include <google/protobuf/pyext/message.h>
#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
@@ -60,38 +61,93 @@ static hash_map<const DescriptorPool*, PyDescriptorPool*> descriptor_pool_map;
namespace cdescriptor_pool {
-static PyDescriptorPool* NewDescriptorPool() {
- PyDescriptorPool* cdescriptor_pool = PyObject_New(
+// Create a Python DescriptorPool object, but does not fill the "pool"
+// attribute.
+static PyDescriptorPool* _CreateDescriptorPool() {
+ PyDescriptorPool* cpool = PyObject_New(
PyDescriptorPool, &PyDescriptorPool_Type);
- if (cdescriptor_pool == NULL) {
+ if (cpool == NULL) {
return NULL;
}
- // Build a DescriptorPool for messages only declared in Python libraries.
- // generated_pool() contains all messages linked in C++ libraries, and is used
- // as underlay.
- cdescriptor_pool->pool = new DescriptorPool(DescriptorPool::generated_pool());
+ cpool->underlay = NULL;
+ cpool->database = NULL;
DynamicMessageFactory* message_factory = new DynamicMessageFactory();
// This option might be the default some day.
message_factory->SetDelegateToGeneratedFactory(true);
- cdescriptor_pool->message_factory = message_factory;
+ cpool->message_factory = message_factory;
// TODO(amauryfa): Rewrite the SymbolDatabase in C so that it uses the same
// storage.
- cdescriptor_pool->classes_by_descriptor =
+ cpool->classes_by_descriptor =
new PyDescriptorPool::ClassesByMessageMap();
- cdescriptor_pool->descriptor_options =
+ cpool->descriptor_options =
new hash_map<const void*, PyObject *>();
+ return cpool;
+}
+
+// Create a Python DescriptorPool, using the given pool as an underlay:
+// new messages will be added to a custom pool, not to the underlay.
+//
+// Ownership of the underlay is not transferred, its pointer should
+// stay alive.
+static PyDescriptorPool* PyDescriptorPool_NewWithUnderlay(
+ const DescriptorPool* underlay) {
+ PyDescriptorPool* cpool = _CreateDescriptorPool();
+ if (cpool == NULL) {
+ return NULL;
+ }
+ cpool->pool = new DescriptorPool(underlay);
+ cpool->underlay = underlay;
+
if (!descriptor_pool_map.insert(
- std::make_pair(cdescriptor_pool->pool, cdescriptor_pool)).second) {
+ std::make_pair(cpool->pool, cpool)).second) {
// Should never happen -- would indicate an internal error / bug.
PyErr_SetString(PyExc_ValueError, "DescriptorPool already registered");
return NULL;
}
- return cdescriptor_pool;
+ return cpool;
+}
+
+static PyDescriptorPool* PyDescriptorPool_NewWithDatabase(
+ DescriptorDatabase* database) {
+ PyDescriptorPool* cpool = _CreateDescriptorPool();
+ if (cpool == NULL) {
+ return NULL;
+ }
+ if (database != NULL) {
+ cpool->pool = new DescriptorPool(database);
+ cpool->database = database;
+ } else {
+ cpool->pool = new DescriptorPool();
+ }
+
+ if (!descriptor_pool_map.insert(std::make_pair(cpool->pool, cpool)).second) {
+ // Should never happen -- would indicate an internal error / bug.
+ PyErr_SetString(PyExc_ValueError, "DescriptorPool already registered");
+ return NULL;
+ }
+
+ return cpool;
+}
+
+// The public DescriptorPool constructor.
+static PyObject* New(PyTypeObject* type,
+ PyObject* args, PyObject* kwargs) {
+ static char* kwlist[] = {"descriptor_db", 0};
+ PyObject* py_database = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &py_database)) {
+ return NULL;
+ }
+ DescriptorDatabase* database = NULL;
+ if (py_database && py_database != Py_None) {
+ database = new PyDescriptorDatabase(py_database);
+ }
+ return reinterpret_cast<PyObject*>(
+ PyDescriptorPool_NewWithDatabase(database));
}
static void Dealloc(PyDescriptorPool* self) {
@@ -108,8 +164,9 @@ static void Dealloc(PyDescriptorPool* self) {
Py_DECREF(it->second);
}
delete self->descriptor_options;
- delete self->pool;
delete self->message_factory;
+ delete self->database;
+ delete self->pool;
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
}
@@ -354,6 +411,14 @@ PyObject* AddSerializedFile(PyDescriptorPool* self, PyObject* serialized_pb) {
char* message_type;
Py_ssize_t message_len;
+ if (self->database != NULL) {
+ PyErr_SetString(
+ PyExc_ValueError,
+ "Cannot call Add on a DescriptorPool that uses a DescriptorDatabase. "
+ "Add your file to the underlying database.");
+ return NULL;
+ }
+
if (PyBytes_AsStringAndSize(serialized_pb, &message_type, &message_len) < 0) {
return NULL;
}
@@ -366,8 +431,10 @@ PyObject* AddSerializedFile(PyDescriptorPool* self, PyObject* serialized_pb) {
// If the file was already part of a C++ library, all its descriptors are in
// the underlying pool. No need to do anything else.
- const FileDescriptor* generated_file =
- DescriptorPool::generated_pool()->FindFileByName(file_proto.name());
+ const FileDescriptor* generated_file = NULL;
+ if (self->underlay) {
+ generated_file = self->underlay->FindFileByName(file_proto.name());
+ }
if (generated_file != NULL) {
return PyFileDescriptor_FromDescriptorWithSerializedPb(
generated_file, serialized_pb);
@@ -470,7 +537,7 @@ PyTypeObject PyDescriptorPool_Type = {
0, // tp_dictoffset
0, // tp_init
0, // tp_alloc
- 0, // tp_new
+ cdescriptor_pool::New, // tp_new
PyObject_Del, // tp_free
};
@@ -482,7 +549,11 @@ bool InitDescriptorPool() {
if (PyType_Ready(&PyDescriptorPool_Type) < 0)
return false;
- python_generated_pool = cdescriptor_pool::NewDescriptorPool();
+ // The Pool of messages declared in Python libraries.
+ // generated_pool() contains all messages already linked in C++ libraries, and
+ // is used as underlay.
+ python_generated_pool = cdescriptor_pool::PyDescriptorPool_NewWithUnderlay(
+ DescriptorPool::generated_pool());
if (python_generated_pool == NULL) {
return false;
}
@@ -494,6 +565,10 @@ bool InitDescriptorPool() {
return true;
}
+// The default DescriptorPool used everywhere in this module.
+// Today it's the python_generated_pool.
+// TODO(amauryfa): Remove all usages of this function: the pool should be
+// derived from the context.
PyDescriptorPool* GetDefaultDescriptorPool() {
return python_generated_pool;
}
diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h
index eda73d38..16bc910c 100644
--- a/python/google/protobuf/pyext/descriptor_pool.h
+++ b/python/google/protobuf/pyext/descriptor_pool.h
@@ -55,8 +55,17 @@ namespace python {
typedef struct PyDescriptorPool {
PyObject_HEAD
+ // The C++ pool containing Descriptors.
DescriptorPool* pool;
+ // The C++ pool acting as an underlay. Can be NULL.
+ // This pointer is not owned and must stay alive.
+ const DescriptorPool* underlay;
+
+ // The C++ descriptor database used to fetch unknown protos. Can be NULL.
+ // This pointer is owned.
+ const DescriptorDatabase* database;
+
// DynamicMessageFactory used to create C++ instances of messages.
// This object cache the descriptors that were used, so the DescriptorPool
// needs to get rid of it before it can delete itself.
@@ -138,6 +147,7 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg);
// Retrieve the global descriptor pool owned by the _message module.
// This is the one used by pb2.py generated modules.
// Returns a *borrowed* reference.
+// "Default" pool used to register messages from _pb2.py modules.
PyDescriptorPool* GetDefaultDescriptorPool();
// Retrieve the python descriptor pool owning a C++ descriptor pool.
diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc
index b361b342..555bd293 100644
--- a/python/google/protobuf/pyext/extension_dict.cc
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -94,13 +94,13 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) {
if (descriptor == NULL) {
return NULL;
}
- if (!CheckFieldBelongsToMessage(descriptor, self->parent->message)) {
+ if (!CheckFieldBelongsToMessage(descriptor, self->message)) {
return NULL;
}
if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
- return cmessage::InternalGetScalar(self->parent->message, descriptor);
+ return cmessage::InternalGetScalar(self->message, descriptor);
}
PyObject* value = PyDict_GetItem(self->values, key);
@@ -109,6 +109,14 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) {
return value;
}
+ if (self->parent == NULL) {
+ // We are in "detached" state. Don't allow further modifications.
+ // TODO(amauryfa): Support adding non-scalars to a detached extension dict.
+ // This probably requires to store the type of the main message.
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+
if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
PyObject* sub_message = cmessage::InternalGetSubMessage(
@@ -154,7 +162,7 @@ int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) {
if (descriptor == NULL) {
return -1;
}
- if (!CheckFieldBelongsToMessage(descriptor, self->parent->message)) {
+ if (!CheckFieldBelongsToMessage(descriptor, self->message)) {
return -1;
}
@@ -164,9 +172,11 @@ int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) {
"type");
return -1;
}
- cmessage::AssureWritable(self->parent);
- if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) {
- return -1;
+ if (self->parent) {
+ cmessage::AssureWritable(self->parent);
+ if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) {
+ return -1;
+ }
}
// TODO(tibell): We shouldn't write scalars to the cache.
PyDict_SetItem(self->values, key, value);
@@ -180,15 +190,17 @@ PyObject* ClearExtension(ExtensionDict* self, PyObject* extension) {
return NULL;
}
PyObject* value = PyDict_GetItem(self->values, extension);
- if (value != NULL) {
- if (ReleaseExtension(self, value, descriptor) < 0) {
+ if (self->parent) {
+ if (value != NULL) {
+ if (ReleaseExtension(self, value, descriptor) < 0) {
+ return NULL;
+ }
+ }
+ if (ScopedPyObjectPtr(cmessage::ClearFieldByDescriptor(
+ self->parent, descriptor)) == NULL) {
return NULL;
}
}
- if (ScopedPyObjectPtr(cmessage::ClearFieldByDescriptor(
- self->parent, descriptor)) == NULL) {
- return NULL;
- }
if (PyDict_DelItem(self->values, extension) < 0) {
PyErr_Clear();
}
@@ -201,8 +213,15 @@ PyObject* HasExtension(ExtensionDict* self, PyObject* extension) {
if (descriptor == NULL) {
return NULL;
}
- PyObject* result = cmessage::HasFieldByDescriptor(self->parent, descriptor);
- return result;
+ if (self->parent) {
+ return cmessage::HasFieldByDescriptor(self->parent, descriptor);
+ } else {
+ int exists = PyDict_Contains(self->values, extension);
+ if (exists < 0) {
+ return NULL;
+ }
+ return PyBool_FromLong(exists);
+ }
}
PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) {
diff --git a/python/google/protobuf/pyext/extension_dict.h b/python/google/protobuf/pyext/extension_dict.h
index 7e1049f1..d92cf956 100644
--- a/python/google/protobuf/pyext/extension_dict.h
+++ b/python/google/protobuf/pyext/extension_dict.h
@@ -47,7 +47,11 @@ namespace protobuf {
class Message;
class FieldDescriptor;
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
using internal::shared_ptr;
+#endif
namespace python {
@@ -113,11 +117,6 @@ int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value);
PyObject* ClearExtension(ExtensionDict* self,
PyObject* extension);
-// Checks if the dict has an extension.
-//
-// Returns a new python boolean reference.
-PyObject* HasExtension(ExtensionDict* self, PyObject* extension);
-
// Gets an extension from the dict given the extension name as opposed to
// descriptor.
//
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
new file mode 100644
index 00000000..df9138a4
--- /dev/null
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -0,0 +1,965 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: haberman@google.com (Josh Haberman)
+
+#include <google/protobuf/pyext/map_container.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/scoped_ptr.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyInt_FromLong PyLong_FromLong
+ #define PyInt_FromSize_t PyLong_FromSize_t
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+// Functions that need access to map reflection functionality.
+// They need to be contained in this class because it is friended.
+class MapReflectionFriend {
+ public:
+ // Methods that are in common between the map types.
+ static PyObject* Contains(PyObject* _self, PyObject* key);
+ static Py_ssize_t Length(PyObject* _self);
+ static PyObject* GetIterator(PyObject *_self);
+ static PyObject* IterNext(PyObject* _self);
+
+ // Methods that differ between the map types.
+ static PyObject* ScalarMapGetItem(PyObject* _self, PyObject* key);
+ static PyObject* MessageMapGetItem(PyObject* _self, PyObject* key);
+ static int ScalarMapSetItem(PyObject* _self, PyObject* key, PyObject* v);
+ static int MessageMapSetItem(PyObject* _self, PyObject* key, PyObject* v);
+};
+
+struct MapIterator {
+ PyObject_HEAD;
+
+ scoped_ptr< ::google::protobuf::MapIterator> iter;
+
+ // A pointer back to the container, so we can notice changes to the version.
+ // We own a ref on this.
+ MapContainer* container;
+
+ // We need to keep a ref on the Message* too, because
+ // MapIterator::~MapIterator() accesses it. Normally this would be ok because
+ // the ref on container (above) would guarantee outlive semantics. However in
+ // the case of ClearField(), InitializeAndCopyToParentContainer() resets the
+ // message pointer (and the owner) to a different message, a copy of the
+ // original. But our iterator still points to the original, which could now
+ // get deleted before us.
+ //
+ // To prevent this, we ensure that the Message will always stay alive as long
+ // as this iterator does. This is solely for the benefit of the MapIterator
+ // destructor -- we should never actually access the iterator in this state
+ // except to delete it.
+ shared_ptr<Message> owner;
+
+ // The version of the map when we took the iterator to it.
+ //
+ // We store this so that if the map is modified during iteration we can throw
+ // an error.
+ uint64 version;
+
+ // True if the container is empty. We signal this separately to avoid calling
+ // any of the iteration methods, which are non-const.
+ bool empty;
+};
+
+Message* MapContainer::GetMutableMessage() {
+ cmessage::AssureWritable(parent);
+ return const_cast<Message*>(message);
+}
+
+// Consumes a reference on the Python string object.
+static bool PyStringToSTL(PyObject* py_string, string* stl_string) {
+ char *value;
+ Py_ssize_t value_len;
+
+ if (!py_string) {
+ return false;
+ }
+ if (PyBytes_AsStringAndSize(py_string, &value, &value_len) < 0) {
+ Py_DECREF(py_string);
+ return false;
+ } else {
+ stl_string->assign(value, value_len);
+ Py_DECREF(py_string);
+ return true;
+ }
+}
+
+static bool PythonToMapKey(PyObject* obj,
+ const FieldDescriptor* field_descriptor,
+ MapKey* key) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(obj, value, false);
+ key->SetInt32Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(obj, value, false);
+ key->SetInt64Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(obj, value, false);
+ key->SetUInt32Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(obj, value, false);
+ key->SetUInt64Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(obj, value, false);
+ key->SetBoolValue(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string str;
+ if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) {
+ return false;
+ }
+ key->SetStringValue(str);
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Type %d cannot be a map key",
+ field_descriptor->cpp_type());
+ return false;
+ }
+ return true;
+}
+
+static PyObject* MapKeyToPython(const FieldDescriptor* field_descriptor,
+ const MapKey& key) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return PyInt_FromLong(key.GetInt32Value());
+ case FieldDescriptor::CPPTYPE_INT64:
+ return PyLong_FromLongLong(key.GetInt64Value());
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return PyInt_FromSize_t(key.GetUInt32Value());
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return PyLong_FromUnsignedLongLong(key.GetUInt64Value());
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return PyBool_FromLong(key.GetBoolValue());
+ case FieldDescriptor::CPPTYPE_STRING:
+ return ToStringObject(field_descriptor, key.GetStringValue());
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Couldn't convert type %d to value",
+ field_descriptor->cpp_type());
+ return NULL;
+ }
+}
+
+// This is only used for ScalarMap, so we don't need to handle the
+// CPPTYPE_MESSAGE case.
+PyObject* MapValueRefToPython(const FieldDescriptor* field_descriptor,
+ MapValueRef* value) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return PyInt_FromLong(value->GetInt32Value());
+ case FieldDescriptor::CPPTYPE_INT64:
+ return PyLong_FromLongLong(value->GetInt64Value());
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return PyInt_FromSize_t(value->GetUInt32Value());
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return PyLong_FromUnsignedLongLong(value->GetUInt64Value());
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return PyFloat_FromDouble(value->GetFloatValue());
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return PyFloat_FromDouble(value->GetDoubleValue());
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return PyBool_FromLong(value->GetBoolValue());
+ case FieldDescriptor::CPPTYPE_STRING:
+ return ToStringObject(field_descriptor, value->GetStringValue());
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return PyInt_FromLong(value->GetEnumValue());
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Couldn't convert type %d to value",
+ field_descriptor->cpp_type());
+ return NULL;
+ }
+}
+
+// This is only used for ScalarMap, so we don't need to handle the
+// CPPTYPE_MESSAGE case.
+static bool PythonToMapValueRef(PyObject* obj,
+ const FieldDescriptor* field_descriptor,
+ bool allow_unknown_enum_values,
+ MapValueRef* value_ref) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(obj, value, false);
+ value_ref->SetInt32Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(obj, value, false);
+ value_ref->SetInt64Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(obj, value, false);
+ value_ref->SetUInt32Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(obj, value, false);
+ value_ref->SetUInt64Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ GOOGLE_CHECK_GET_FLOAT(obj, value, false);
+ value_ref->SetFloatValue(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ GOOGLE_CHECK_GET_DOUBLE(obj, value, false);
+ value_ref->SetDoubleValue(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(obj, value, false);
+ value_ref->SetBoolValue(value);
+ return true;;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string str;
+ if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) {
+ return false;
+ }
+ value_ref->SetStringValue(str);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ GOOGLE_CHECK_GET_INT32(obj, value, false);
+ if (allow_unknown_enum_values) {
+ value_ref->SetEnumValue(value);
+ return true;
+ } else {
+ const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
+ const EnumValueDescriptor* enum_value =
+ enum_descriptor->FindValueByNumber(value);
+ if (enum_value != NULL) {
+ value_ref->SetEnumValue(value);
+ return true;
+ } else {
+ PyErr_Format(PyExc_ValueError, "Unknown enum value: %d", value);
+ return false;
+ }
+ }
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Setting value to a field of unknown type %d",
+ field_descriptor->cpp_type());
+ return false;
+ }
+}
+
+// Map methods common to ScalarMap and MessageMap //////////////////////////////
+
+static MapContainer* GetMap(PyObject* obj) {
+ return reinterpret_cast<MapContainer*>(obj);
+}
+
+Py_ssize_t MapReflectionFriend::Length(PyObject* _self) {
+ MapContainer* self = GetMap(_self);
+ const google::protobuf::Message* message = self->message;
+ return message->GetReflection()->MapSize(*message,
+ self->parent_field_descriptor);
+}
+
+PyObject* Clear(PyObject* _self) {
+ MapContainer* self = GetMap(_self);
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+
+ reflection->ClearField(message, self->parent_field_descriptor);
+
+ Py_RETURN_NONE;
+}
+
+PyObject* MapReflectionFriend::Contains(PyObject* _self, PyObject* key) {
+ MapContainer* self = GetMap(_self);
+
+ const Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return NULL;
+ }
+
+ if (reflection->ContainsMapKey(*message, self->parent_field_descriptor,
+ map_key)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+// Initializes the underlying Message object of "to" so it becomes a new parent
+// repeated scalar, and copies all the values from "from" to it. A child scalar
+// container can be released by passing it as both from and to (e.g. making it
+// the recipient of the new parent message and copying the values from itself).
+static int InitializeAndCopyToParentContainer(MapContainer* from,
+ MapContainer* to) {
+ // For now we require from == to, re-evaluate if we want to support deep copy
+ // as in repeated_scalar_container.cc.
+ GOOGLE_DCHECK(from == to);
+ Message* new_message = from->message->New();
+
+ if (MapReflectionFriend::Length(reinterpret_cast<PyObject*>(from)) > 0) {
+ // A somewhat roundabout way of copying just one field from old_message to
+ // new_message. This is the best we can do with what Reflection gives us.
+ Message* mutable_old = from->GetMutableMessage();
+ vector<const FieldDescriptor*> fields;
+ fields.push_back(from->parent_field_descriptor);
+
+ // Move the map field into the new message.
+ mutable_old->GetReflection()->SwapFields(mutable_old, new_message, fields);
+
+ // If/when we support from != to, this will be required also to copy the
+ // map field back into the existing message:
+ // mutable_old->MergeFrom(*new_message);
+ }
+
+ // If from == to this could delete old_message.
+ to->owner.reset(new_message);
+
+ to->parent = NULL;
+ to->parent_field_descriptor = from->parent_field_descriptor;
+ to->message = new_message;
+
+ // Invalidate iterators, since they point to the old copy of the field.
+ to->version++;
+
+ return 0;
+}
+
+int MapContainer::Release() {
+ return InitializeAndCopyToParentContainer(this, this);
+}
+
+
+// ScalarMap ///////////////////////////////////////////////////////////////////
+
+PyObject *NewScalarMapContainer(
+ CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) {
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
+ return NULL;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(
+ reinterpret_cast<PyTypeObject *>(ScalarMapContainer_Type), 0));
+#else
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapContainer_Type, 0));
+#endif
+ if (obj.get() == NULL) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Could not allocate new container.");
+ }
+
+ MapContainer* self = GetMap(obj.get());
+
+ self->message = parent->message;
+ self->parent = parent;
+ self->parent_field_descriptor = parent_field_descriptor;
+ self->owner = parent->owner;
+ self->version = 0;
+
+ self->key_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("key");
+ self->value_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("value");
+
+ if (self->key_field_descriptor == NULL ||
+ self->value_field_descriptor == NULL) {
+ return PyErr_Format(PyExc_KeyError,
+ "Map entry descriptor did not have key/value fields");
+ }
+
+ return obj.release();
+}
+
+PyObject* MapReflectionFriend::ScalarMapGetItem(PyObject* _self,
+ PyObject* key) {
+ MapContainer* self = GetMap(_self);
+
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return NULL;
+ }
+
+ if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
+ map_key, &value)) {
+ self->version++;
+ }
+
+ return MapValueRefToPython(self->value_field_descriptor, &value);
+}
+
+int MapReflectionFriend::ScalarMapSetItem(PyObject* _self, PyObject* key,
+ PyObject* v) {
+ MapContainer* self = GetMap(_self);
+
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return -1;
+ }
+
+ self->version++;
+
+ if (v) {
+ // Set item to v.
+ reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
+ map_key, &value);
+
+ return PythonToMapValueRef(v, self->value_field_descriptor,
+ reflection->SupportsUnknownEnumValues(), &value)
+ ? 0
+ : -1;
+ } else {
+ // Delete key from map.
+ if (reflection->DeleteMapValue(message, self->parent_field_descriptor,
+ map_key)) {
+ return 0;
+ } else {
+ PyErr_Format(PyExc_KeyError, "Key not present in map");
+ return -1;
+ }
+ }
+}
+
+static PyObject* ScalarMapGet(PyObject* self, PyObject* args) {
+ PyObject* key;
+ PyObject* default_value = NULL;
+ if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
+ if (is_present.get() == NULL) {
+ return NULL;
+ }
+
+ if (PyObject_IsTrue(is_present.get())) {
+ return MapReflectionFriend::ScalarMapGetItem(self, key);
+ } else {
+ if (default_value != NULL) {
+ Py_INCREF(default_value);
+ return default_value;
+ } else {
+ Py_RETURN_NONE;
+ }
+ }
+}
+
+static void ScalarMapDealloc(PyObject* _self) {
+ MapContainer* self = GetMap(_self);
+ self->owner.reset();
+ Py_TYPE(_self)->tp_free(_self);
+}
+
+static PyMethodDef ScalarMapMethods[] = {
+ { "__contains__", MapReflectionFriend::Contains, METH_O,
+ "Tests whether a key is a member of the map." },
+ { "clear", (PyCFunction)Clear, METH_NOARGS,
+ "Removes all elements from the map." },
+ { "get", ScalarMapGet, METH_VARARGS,
+ "Gets the value for the given key if present, or otherwise a default" },
+ /*
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
+ "Makes a deep copy of the class." },
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
+ "Outputs picklable representation of the repeated field." },
+ */
+ {NULL, NULL},
+};
+
+#if PY_MAJOR_VERSION >= 3
+ static PyType_Slot ScalarMapContainer_Type_slots[] = {
+ {Py_tp_dealloc, (void *)ScalarMapDealloc},
+ {Py_mp_length, (void *)MapReflectionFriend::Length},
+ {Py_mp_subscript, (void *)MapReflectionFriend::ScalarMapGetItem},
+ {Py_mp_ass_subscript, (void *)MapReflectionFriend::ScalarMapSetItem},
+ {Py_tp_methods, (void *)ScalarMapMethods},
+ {Py_tp_iter, (void *)MapReflectionFriend::GetIterator},
+ {0, 0},
+ };
+
+ PyType_Spec ScalarMapContainer_Type_spec = {
+ FULL_MODULE_NAME ".ScalarMapContainer",
+ sizeof(MapContainer),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ ScalarMapContainer_Type_slots
+ };
+ PyObject *ScalarMapContainer_Type;
+#else
+ static PyMappingMethods ScalarMapMappingMethods = {
+ MapReflectionFriend::Length, // mp_length
+ MapReflectionFriend::ScalarMapGetItem, // mp_subscript
+ MapReflectionFriend::ScalarMapSetItem, // mp_ass_subscript
+ };
+
+ PyTypeObject ScalarMapContainer_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".ScalarMapContainer", // tp_name
+ sizeof(MapContainer), // tp_basicsize
+ 0, // tp_itemsize
+ ScalarMapDealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ &ScalarMapMappingMethods, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A scalar map container", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ MapReflectionFriend::GetIterator, // tp_iter
+ 0, // tp_iternext
+ ScalarMapMethods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ };
+#endif
+
+
+// MessageMap //////////////////////////////////////////////////////////////////
+
+static MessageMapContainer* GetMessageMap(PyObject* obj) {
+ return reinterpret_cast<MessageMapContainer*>(obj);
+}
+
+static PyObject* GetCMessage(MessageMapContainer* self, Message* message) {
+ // Get or create the CMessage object corresponding to this message.
+ ScopedPyObjectPtr key(PyLong_FromVoidPtr(message));
+ PyObject* ret = PyDict_GetItem(self->message_dict, key.get());
+
+ if (ret == NULL) {
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
+ message->GetDescriptor());
+ ret = reinterpret_cast<PyObject*>(cmsg);
+
+ if (cmsg == NULL) {
+ return NULL;
+ }
+ cmsg->owner = self->owner;
+ cmsg->message = message;
+ cmsg->parent = self->parent;
+
+ if (PyDict_SetItem(self->message_dict, key.get(), ret) < 0) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ } else {
+ Py_INCREF(ret);
+ }
+
+ return ret;
+}
+
+PyObject* NewMessageMapContainer(
+ CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor,
+ PyObject* concrete_class) {
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
+ return NULL;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ PyObject* obj = PyType_GenericAlloc(
+ reinterpret_cast<PyTypeObject *>(MessageMapContainer_Type), 0);
+#else
+ PyObject* obj = PyType_GenericAlloc(&MessageMapContainer_Type, 0);
+#endif
+ if (obj == NULL) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Could not allocate new container.");
+ }
+
+ MessageMapContainer* self = GetMessageMap(obj);
+
+ self->message = parent->message;
+ self->parent = parent;
+ self->parent_field_descriptor = parent_field_descriptor;
+ self->owner = parent->owner;
+ self->version = 0;
+
+ self->key_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("key");
+ self->value_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("value");
+
+ self->message_dict = PyDict_New();
+ if (self->message_dict == NULL) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Could not allocate message dict.");
+ }
+
+ Py_INCREF(concrete_class);
+ self->subclass_init = concrete_class;
+
+ if (self->key_field_descriptor == NULL ||
+ self->value_field_descriptor == NULL) {
+ Py_DECREF(obj);
+ return PyErr_Format(PyExc_KeyError,
+ "Map entry descriptor did not have key/value fields");
+ }
+
+ return obj;
+}
+
+int MapReflectionFriend::MessageMapSetItem(PyObject* _self, PyObject* key,
+ PyObject* v) {
+ if (v) {
+ PyErr_Format(PyExc_ValueError,
+ "Direct assignment of submessage not allowed");
+ return -1;
+ }
+
+ // Now we know that this is a delete, not a set.
+
+ MessageMapContainer* self = GetMessageMap(_self);
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ self->version++;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return -1;
+ }
+
+ // Delete key from map.
+ if (reflection->DeleteMapValue(message, self->parent_field_descriptor,
+ map_key)) {
+ return 0;
+ } else {
+ PyErr_Format(PyExc_KeyError, "Key not present in map");
+ return -1;
+ }
+}
+
+PyObject* MapReflectionFriend::MessageMapGetItem(PyObject* _self,
+ PyObject* key) {
+ MessageMapContainer* self = GetMessageMap(_self);
+
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return NULL;
+ }
+
+ if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
+ map_key, &value)) {
+ self->version++;
+ }
+
+ return GetCMessage(self, value.MutableMessageValue());
+}
+
+PyObject* MessageMapGet(PyObject* self, PyObject* args) {
+ PyObject* key;
+ PyObject* default_value = NULL;
+ if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
+ if (is_present.get() == NULL) {
+ return NULL;
+ }
+
+ if (PyObject_IsTrue(is_present.get())) {
+ return MapReflectionFriend::MessageMapGetItem(self, key);
+ } else {
+ if (default_value != NULL) {
+ Py_INCREF(default_value);
+ return default_value;
+ } else {
+ Py_RETURN_NONE;
+ }
+ }
+}
+
+static void MessageMapDealloc(PyObject* _self) {
+ MessageMapContainer* self = GetMessageMap(_self);
+ self->owner.reset();
+ Py_DECREF(self->message_dict);
+ Py_TYPE(_self)->tp_free(_self);
+}
+
+static PyMethodDef MessageMapMethods[] = {
+ { "__contains__", (PyCFunction)MapReflectionFriend::Contains, METH_O,
+ "Tests whether the map contains this element."},
+ { "clear", (PyCFunction)Clear, METH_NOARGS,
+ "Removes all elements from the map."},
+ { "get", MessageMapGet, METH_VARARGS,
+ "Gets the value for the given key if present, or otherwise a default" },
+ { "get_or_create", MapReflectionFriend::MessageMapGetItem, METH_O,
+ "Alias for getitem, useful to make explicit that the map is mutated." },
+ /*
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
+ "Makes a deep copy of the class." },
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
+ "Outputs picklable representation of the repeated field." },
+ */
+ {NULL, NULL},
+};
+
+#if PY_MAJOR_VERSION >= 3
+ static PyType_Slot MessageMapContainer_Type_slots[] = {
+ {Py_tp_dealloc, (void *)MessageMapDealloc},
+ {Py_mp_length, (void *)MapReflectionFriend::Length},
+ {Py_mp_subscript, (void *)MapReflectionFriend::MessageMapGetItem},
+ {Py_mp_ass_subscript, (void *)MapReflectionFriend::MessageMapSetItem},
+ {Py_tp_methods, (void *)MessageMapMethods},
+ {Py_tp_iter, (void *)MapReflectionFriend::GetIterator},
+ {0, 0}
+ };
+
+ PyType_Spec MessageMapContainer_Type_spec = {
+ FULL_MODULE_NAME ".MessageMapContainer",
+ sizeof(MessageMapContainer),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ MessageMapContainer_Type_slots
+ };
+
+ PyObject *MessageMapContainer_Type;
+#else
+ static PyMappingMethods MessageMapMappingMethods = {
+ MapReflectionFriend::Length, // mp_length
+ MapReflectionFriend::MessageMapGetItem, // mp_subscript
+ MapReflectionFriend::MessageMapSetItem, // mp_ass_subscript
+ };
+
+ PyTypeObject MessageMapContainer_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MessageMapContainer", // tp_name
+ sizeof(MessageMapContainer), // tp_basicsize
+ 0, // tp_itemsize
+ MessageMapDealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ &MessageMapMappingMethods, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A map container for message", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ MapReflectionFriend::GetIterator, // tp_iter
+ 0, // tp_iternext
+ MessageMapMethods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ };
+#endif
+
+// MapIterator /////////////////////////////////////////////////////////////////
+
+static MapIterator* GetIter(PyObject* obj) {
+ return reinterpret_cast<MapIterator*>(obj);
+}
+
+PyObject* MapReflectionFriend::GetIterator(PyObject *_self) {
+ MapContainer* self = GetMap(_self);
+
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(&MapIterator_Type, 0));
+ if (obj == NULL) {
+ return PyErr_Format(PyExc_KeyError, "Could not allocate iterator");
+ }
+
+ MapIterator* iter = GetIter(obj.get());
+
+ Py_INCREF(self);
+ iter->container = self;
+ iter->version = self->version;
+ iter->owner = self->owner;
+
+ if (MapReflectionFriend::Length(_self) > 0) {
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+
+ iter->iter.reset(new ::google::protobuf::MapIterator(
+ reflection->MapBegin(message, self->parent_field_descriptor)));
+ }
+
+ return obj.release();
+}
+
+PyObject* MapReflectionFriend::IterNext(PyObject* _self) {
+ MapIterator* self = GetIter(_self);
+
+ // This won't catch mutations to the map performed by MergeFrom(); no easy way
+ // to address that.
+ if (self->version != self->container->version) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Map modified during iteration.");
+ }
+
+ if (self->iter.get() == NULL) {
+ return NULL;
+ }
+
+ Message* message = self->container->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+
+ if (*self->iter ==
+ reflection->MapEnd(message, self->container->parent_field_descriptor)) {
+ return NULL;
+ }
+
+ PyObject* ret = MapKeyToPython(self->container->key_field_descriptor,
+ self->iter->GetKey());
+
+ ++(*self->iter);
+
+ return ret;
+}
+
+static void DeallocMapIterator(PyObject* _self) {
+ MapIterator* self = GetIter(_self);
+ self->iter.reset();
+ self->owner.reset();
+ Py_XDECREF(self->container);
+ Py_TYPE(_self)->tp_free(_self);
+}
+
+PyTypeObject MapIterator_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MapIterator", // tp_name
+ sizeof(MapIterator), // tp_basicsize
+ 0, // tp_itemsize
+ DeallocMapIterator, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A scalar map iterator", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ PyObject_SelfIter, // tp_iter
+ MapReflectionFriend::IterNext, // tp_iternext
+ 0, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+};
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/message_map_container.h b/python/google/protobuf/pyext/map_container.h
index 8286ba8a..ddf94be7 100644
--- a/python/google/protobuf/pyext/message_map_container.h
+++ b/python/google/protobuf/pyext/map_container.h
@@ -28,8 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__
-#define GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
#include <Python.h>
@@ -39,30 +39,40 @@
#endif
#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
namespace google {
namespace protobuf {
class Message;
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
using internal::shared_ptr;
+#endif
namespace python {
struct CMessage;
-struct MessageMapContainer {
+// This struct is used directly for ScalarMap, and is the base class of
+// MessageMapContainer, which is used for MessageMap.
+struct MapContainer {
PyObject_HEAD;
// This is the top-level C++ Message object that owns the whole
- // proto tree. Every Python MessageMapContainer holds a
+ // proto tree. Every Python MapContainer holds a
// reference to it in order to keep it alive as long as there's a
// Python object that references any part of the tree.
shared_ptr<Message> owner;
// Pointer to the C++ Message that contains this container. The
- // MessageMapContainer does not own this pointer.
- Message* message;
+ // MapContainer does not own this pointer.
+ const Message* message;
+
+ // Use to get a mutable message when necessary.
+ Message* GetMutableMessage();
// Weak reference to a parent CMessage object (i.e. may be NULL.)
//
@@ -78,45 +88,54 @@ struct MessageMapContainer {
const FieldDescriptor* key_field_descriptor;
const FieldDescriptor* value_field_descriptor;
+ // We bump this whenever we perform a mutation, to invalidate existing
+ // iterators.
+ uint64 version;
+
+ // Releases the messages in the container to a new message.
+ //
+ // Returns 0 on success, -1 on failure.
+ int Release();
+
+ // Set the owner field of self and any children of self.
+ void SetOwner(const shared_ptr<Message>& new_owner) {
+ owner = new_owner;
+ }
+};
+
+struct MessageMapContainer : public MapContainer {
// A callable that is used to create new child messages.
PyObject* subclass_init;
// A dict mapping Message* -> CMessage.
PyObject* message_dict;
-
- // We bump this whenever we perform a mutation, to invalidate existing
- // iterators.
- uint64 version;
};
#if PY_MAJOR_VERSION >= 3
extern PyObject *MessageMapContainer_Type;
extern PyType_Spec MessageMapContainer_Type_spec;
+ extern PyObject *ScalarMapContainer_Type;
+ extern PyType_Spec ScalarMapContainer_Type_spec;
#else
extern PyTypeObject MessageMapContainer_Type;
+ extern PyTypeObject ScalarMapContainer_Type;
#endif
-extern PyTypeObject MessageMapIterator_Type;
-namespace message_map_container {
+extern PyTypeObject MapIterator_Type; // Both map types use the same iterator.
-// Builds a MessageMapContainer object, from a parent message and a
+// Builds a MapContainer object, from a parent message and a
// field descriptor.
-extern PyObject* NewContainer(CMessage* parent,
- const FieldDescriptor* parent_field_descriptor,
- PyObject* concrete_class);
-
-// Releases the messages in the container to a new message.
-//
-// Returns 0 on success, -1 on failure.
-int Release(MessageMapContainer* self);
+extern PyObject* NewScalarMapContainer(
+ CMessage* parent, const FieldDescriptor* parent_field_descriptor);
-// Set the owner field of self and any children of self.
-void SetOwner(MessageMapContainer* self,
- const shared_ptr<Message>& new_owner);
+// Builds a MessageMap object, from a parent message and a
+// field descriptor.
+extern PyObject* NewMessageMapContainer(
+ CMessage* parent, const FieldDescriptor* parent_field_descriptor,
+ PyObject* concrete_class);
-} // namespace message_map_container
} // namespace python
} // namespace protobuf
} // namespace google
-#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 72f51ec1..863cde01 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -33,6 +33,7 @@
#include <google/protobuf/pyext/message.h>
+#include <map>
#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
@@ -61,8 +62,7 @@
#include <google/protobuf/pyext/extension_dict.h>
#include <google/protobuf/pyext/repeated_composite_container.h>
#include <google/protobuf/pyext/repeated_scalar_container.h>
-#include <google/protobuf/pyext/message_map_container.h>
-#include <google/protobuf/pyext/scalar_map_container.h>
+#include <google/protobuf/pyext/map_container.h>
#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
#include <google/protobuf/stubs/strutil.h>
@@ -96,6 +96,7 @@ static PyObject* k_extensions_by_number;
PyObject* EnumTypeWrapper_class;
static PyObject* PythonMessage_class;
static PyObject* kEmptyWeakref;
+static PyObject* WKT_classes = NULL;
// Defines the Metaclass of all Message classes.
// It allows us to cache some C++ pointers in the class object itself, they are
@@ -274,8 +275,32 @@ static PyObject* New(PyTypeObject* type,
// Build the arguments to the base metaclass.
// We change the __bases__ classes.
- ScopedPyObjectPtr new_args(Py_BuildValue(
- "s(OO)O", name, &CMessage_Type, PythonMessage_class, dict));
+ ScopedPyObjectPtr new_args;
+ const Descriptor* message_descriptor =
+ PyMessageDescriptor_AsDescriptor(py_descriptor);
+ if (message_descriptor == NULL) {
+ return NULL;
+ }
+
+ if (WKT_classes == NULL) {
+ ScopedPyObjectPtr well_known_types(PyImport_ImportModule(
+ "google.protobuf.internal.well_known_types"));
+ GOOGLE_DCHECK(well_known_types != NULL);
+
+ WKT_classes = PyObject_GetAttrString(well_known_types.get(), "WKTBASES");
+ GOOGLE_DCHECK(WKT_classes != NULL);
+ }
+
+ PyObject* well_known_class = PyDict_GetItemString(
+ WKT_classes, message_descriptor->full_name().c_str());
+ if (well_known_class == NULL) {
+ new_args.reset(Py_BuildValue("s(OO)O", name, &CMessage_Type,
+ PythonMessage_class, dict));
+ } else {
+ new_args.reset(Py_BuildValue("s(OOO)O", name, &CMessage_Type,
+ PythonMessage_class, well_known_class, dict));
+ }
+
if (new_args == NULL) {
return NULL;
}
@@ -448,21 +473,9 @@ static int VisitCompositeField(const FieldDescriptor* descriptor,
if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
if (descriptor->is_map()) {
- const Descriptor* entry_type = descriptor->message_type();
- const FieldDescriptor* value_type =
- entry_type->FindFieldByName("value");
- if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- MessageMapContainer* container =
- reinterpret_cast<MessageMapContainer*>(child);
- if (visitor.VisitMessageMapContainer(container) == -1) {
- return -1;
- }
- } else {
- ScalarMapContainer* container =
- reinterpret_cast<ScalarMapContainer*>(child);
- if (visitor.VisitScalarMapContainer(container) == -1) {
- return -1;
- }
+ MapContainer* container = reinterpret_cast<MapContainer*>(child);
+ if (visitor.VisitMapContainer(container) == -1) {
+ return -1;
}
} else {
RepeatedCompositeContainer* container =
@@ -579,12 +592,14 @@ bool CheckAndGetInteger(
if (PyObject_RichCompareBool(min, arg, Py_LE) != 1 ||
PyObject_RichCompareBool(max, arg, Py_GE) != 1) {
#endif
- PyObject *s = PyObject_Str(arg);
- if (s) {
- PyErr_Format(PyExc_ValueError,
- "Value out of range: %s",
- PyString_AsString(s));
- Py_DECREF(s);
+ if (!PyErr_Occurred()) {
+ PyObject *s = PyObject_Str(arg);
+ if (s) {
+ PyErr_Format(PyExc_ValueError,
+ "Value out of range: %s",
+ PyString_AsString(s));
+ Py_DECREF(s);
+ }
}
return false;
}
@@ -642,38 +657,51 @@ bool CheckAndGetBool(PyObject* arg, bool* value) {
return true;
}
-bool CheckAndSetString(
- PyObject* arg, Message* message,
- const FieldDescriptor* descriptor,
- const Reflection* reflection,
- bool append,
- int index) {
+// Checks whether the given object (which must be "bytes" or "unicode") contains
+// valid UTF-8.
+bool IsValidUTF8(PyObject* obj) {
+ if (PyBytes_Check(obj)) {
+ PyObject* unicode = PyUnicode_FromEncodedObject(obj, "utf-8", NULL);
+
+ // Clear the error indicator; we report our own error when desired.
+ PyErr_Clear();
+
+ if (unicode) {
+ Py_DECREF(unicode);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // Unicode object, known to be valid UTF-8.
+ return true;
+ }
+}
+
+bool AllowInvalidUTF8(const FieldDescriptor* field) { return false; }
+
+PyObject* CheckString(PyObject* arg, const FieldDescriptor* descriptor) {
GOOGLE_DCHECK(descriptor->type() == FieldDescriptor::TYPE_STRING ||
descriptor->type() == FieldDescriptor::TYPE_BYTES);
if (descriptor->type() == FieldDescriptor::TYPE_STRING) {
if (!PyBytes_Check(arg) && !PyUnicode_Check(arg)) {
FormatTypeError(arg, "bytes, unicode");
- return false;
+ return NULL;
}
- if (PyBytes_Check(arg)) {
- PyObject* unicode = PyUnicode_FromEncodedObject(arg, "utf-8", NULL);
- if (unicode == NULL) {
- PyObject* repr = PyObject_Repr(arg);
- PyErr_Format(PyExc_ValueError,
- "%s has type str, but isn't valid UTF-8 "
- "encoding. Non-UTF-8 strings must be converted to "
- "unicode objects before being added.",
- PyString_AsString(repr));
- Py_DECREF(repr);
- return false;
- } else {
- Py_DECREF(unicode);
- }
+ if (!IsValidUTF8(arg) && !AllowInvalidUTF8(descriptor)) {
+ PyObject* repr = PyObject_Repr(arg);
+ PyErr_Format(PyExc_ValueError,
+ "%s has type str, but isn't valid UTF-8 "
+ "encoding. Non-UTF-8 strings must be converted to "
+ "unicode objects before being added.",
+ PyString_AsString(repr));
+ Py_DECREF(repr);
+ return NULL;
}
} else if (!PyBytes_Check(arg)) {
FormatTypeError(arg, "bytes");
- return false;
+ return NULL;
}
PyObject* encoded_string = NULL;
@@ -691,14 +719,24 @@ bool CheckAndSetString(
Py_INCREF(encoded_string);
}
- if (encoded_string == NULL) {
+ return encoded_string;
+}
+
+bool CheckAndSetString(
+ PyObject* arg, Message* message,
+ const FieldDescriptor* descriptor,
+ const Reflection* reflection,
+ bool append,
+ int index) {
+ ScopedPyObjectPtr encoded_string(CheckString(arg, descriptor));
+
+ if (encoded_string.get() == NULL) {
return false;
}
char* value;
Py_ssize_t value_len;
- if (PyBytes_AsStringAndSize(encoded_string, &value, &value_len) < 0) {
- Py_DECREF(encoded_string);
+ if (PyBytes_AsStringAndSize(encoded_string.get(), &value, &value_len) < 0) {
return false;
}
@@ -710,7 +748,6 @@ bool CheckAndSetString(
} else {
reflection->SetRepeatedString(message, descriptor, index, value_string);
}
- Py_DECREF(encoded_string);
return true;
}
@@ -823,12 +860,7 @@ struct FixupMessageReference : public ChildVisitor {
return 0;
}
- int VisitScalarMapContainer(ScalarMapContainer* container) {
- container->message = message_;
- return 0;
- }
-
- int VisitMessageMapContainer(MessageMapContainer* container) {
+ int VisitMapContainer(MapContainer* container) {
container->message = message_;
return 0;
}
@@ -870,9 +902,8 @@ int AssureWritable(CMessage* self) {
// When a CMessage is made writable its Message pointer is updated
// to point to a new mutable Message. When that happens we need to
// update any references to the old, read-only CMessage. There are
- // five places such references occur: RepeatedScalarContainer,
- // RepeatedCompositeContainer, ScalarMapContainer, MessageMapContainer,
- // and ExtensionDict.
+ // four places such references occur: RepeatedScalarContainer,
+ // RepeatedCompositeContainer, MapContainer, and ExtensionDict.
if (self->extensions != NULL)
self->extensions->message = self->message;
if (ForEachCompositeField(self, FixupMessageReference(self->message)) == -1)
@@ -1054,7 +1085,8 @@ int InitAttributes(CMessage* self, PyObject* kwargs) {
}
const FieldDescriptor* descriptor = GetFieldDescriptor(self, name);
if (descriptor == NULL) {
- PyErr_Format(PyExc_ValueError, "Protocol message has no \"%s\" field.",
+ PyErr_Format(PyExc_ValueError, "Protocol message %s has no \"%s\" field.",
+ self->message->GetDescriptor()->name().c_str(),
PyString_AsString(name));
return -1;
}
@@ -1203,18 +1235,6 @@ CMessage* NewEmptyMessage(PyObject* type, const Descriptor *descriptor) {
self->composite_fields = NULL;
- // If there are extension_ranges, the message is "extendable". Allocate a
- // dictionary to store the extension fields.
- if (descriptor->extension_range_count() > 0) {
- // TODO(amauryfa): Delay the construction of this dict until extensions are
- // really used on the object.
- ExtensionDict* extension_dict = extension_dict::NewExtensionDict(self);
- if (extension_dict == NULL) {
- return NULL;
- }
- self->extensions = extension_dict;
- }
-
return self;
}
@@ -1285,12 +1305,7 @@ struct ClearWeakReferences : public ChildVisitor {
return 0;
}
- int VisitScalarMapContainer(ScalarMapContainer* container) {
- container->parent = NULL;
- return 0;
- }
-
- int VisitMessageMapContainer(MessageMapContainer* container) {
+ int VisitMapContainer(MapContainer* container) {
container->parent = NULL;
return 0;
}
@@ -1305,6 +1320,9 @@ struct ClearWeakReferences : public ChildVisitor {
static void Dealloc(CMessage* self) {
// Null out all weak references from children to this message.
GOOGLE_CHECK_EQ(0, ForEachCompositeField(self, ClearWeakReferences()));
+ if (self->extensions) {
+ self->extensions->parent = NULL;
+ }
Py_CLEAR(self->extensions);
Py_CLEAR(self->composite_fields);
@@ -1466,20 +1484,27 @@ PyObject* HasField(CMessage* self, PyObject* arg) {
Py_RETURN_FALSE;
}
-PyObject* ClearExtension(CMessage* self, PyObject* arg) {
+PyObject* ClearExtension(CMessage* self, PyObject* extension) {
if (self->extensions != NULL) {
- return extension_dict::ClearExtension(self->extensions, arg);
+ return extension_dict::ClearExtension(self->extensions, extension);
+ } else {
+ const FieldDescriptor* descriptor = GetExtensionDescriptor(extension);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ if (ScopedPyObjectPtr(ClearFieldByDescriptor(self, descriptor)) == NULL) {
+ return NULL;
+ }
}
- PyErr_SetString(PyExc_TypeError, "Message is not extendable");
- return NULL;
+ Py_RETURN_NONE;
}
-PyObject* HasExtension(CMessage* self, PyObject* arg) {
- if (self->extensions != NULL) {
- return extension_dict::HasExtension(self->extensions, arg);
+PyObject* HasExtension(CMessage* self, PyObject* extension) {
+ const FieldDescriptor* descriptor = GetExtensionDescriptor(extension);
+ if (descriptor == NULL) {
+ return NULL;
}
- PyErr_SetString(PyExc_TypeError, "Message is not extendable");
- return NULL;
+ return HasFieldByDescriptor(self, descriptor);
}
// ---------------------------------------------------------------------
@@ -1529,13 +1554,8 @@ struct SetOwnerVisitor : public ChildVisitor {
return 0;
}
- int VisitScalarMapContainer(ScalarMapContainer* container) {
- scalar_map_container::SetOwner(container, new_owner_);
- return 0;
- }
-
- int VisitMessageMapContainer(MessageMapContainer* container) {
- message_map_container::SetOwner(container, new_owner_);
+ int VisitMapContainer(MapContainer* container) {
+ container->SetOwner(new_owner_);
return 0;
}
@@ -1608,14 +1628,8 @@ struct ReleaseChild : public ChildVisitor {
reinterpret_cast<RepeatedScalarContainer*>(container));
}
- int VisitScalarMapContainer(ScalarMapContainer* container) {
- return scalar_map_container::Release(
- reinterpret_cast<ScalarMapContainer*>(container));
- }
-
- int VisitMessageMapContainer(MessageMapContainer* container) {
- return message_map_container::Release(
- reinterpret_cast<MessageMapContainer*>(container));
+ int VisitMapContainer(MapContainer* container) {
+ return reinterpret_cast<MapContainer*>(container)->Release();
}
int VisitCMessage(CMessage* cmessage,
@@ -1707,17 +1721,7 @@ PyObject* Clear(CMessage* self) {
AssureWritable(self);
if (ForEachCompositeField(self, ReleaseChild(self)) == -1)
return NULL;
-
- // The old ExtensionDict still aliases this CMessage, but all its
- // fields have been released.
- if (self->extensions != NULL) {
- Py_CLEAR(self->extensions);
- ExtensionDict* extension_dict = extension_dict::NewExtensionDict(self);
- if (extension_dict == NULL) {
- return NULL;
- }
- self->extensions = extension_dict;
- }
+ Py_CLEAR(self->extensions);
if (self->composite_fields) {
PyDict_Clear(self->composite_fields);
}
@@ -1997,7 +2001,6 @@ static PyObject* RegisterExtension(PyObject* cls,
if (descriptor->is_extension() &&
descriptor->containing_type()->options().message_set_wire_format() &&
descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
- descriptor->message_type() == descriptor->extension_scope() &&
descriptor->label() == FieldDescriptor::LABEL_OPTIONAL) {
ScopedPyObjectPtr message_name(PyString_FromStringAndSize(
descriptor->message_type()->full_name().c_str(),
@@ -2042,6 +2045,8 @@ static PyObject* WhichOneof(CMessage* self, PyObject* arg) {
}
}
+static PyObject* GetExtensionDict(CMessage* self, void *closure);
+
static PyObject* ListFields(CMessage* self) {
vector<const FieldDescriptor*> fields;
self->message->GetReflection()->ListFields(*self->message, &fields);
@@ -2079,12 +2084,13 @@ static PyObject* ListFields(CMessage* self) {
PyErr_Clear();
continue;
}
- PyObject* extensions = reinterpret_cast<PyObject*>(self->extensions);
+ ScopedPyObjectPtr extensions(GetExtensionDict(self, NULL));
if (extensions == NULL) {
return NULL;
}
// 'extension' reference later stolen by PyTuple_SET_ITEM.
- PyObject* extension = PyObject_GetItem(extensions, extension_field.get());
+ PyObject* extension = PyObject_GetItem(
+ extensions.get(), extension_field.get());
if (extension == NULL) {
return NULL;
}
@@ -2493,9 +2499,31 @@ PyObject* _CheckCalledFromGeneratedFile(PyObject* unused,
Py_RETURN_NONE;
}
-static PyMemberDef Members[] = {
- {"Extensions", T_OBJECT_EX, offsetof(CMessage, extensions), 0,
- "Extension dict"},
+static PyObject* GetExtensionDict(CMessage* self, void *closure) {
+ if (self->extensions) {
+ Py_INCREF(self->extensions);
+ return reinterpret_cast<PyObject*>(self->extensions);
+ }
+
+ // If there are extension_ranges, the message is "extendable". Allocate a
+ // dictionary to store the extension fields.
+ const Descriptor* descriptor = GetMessageDescriptor(Py_TYPE(self));
+ if (descriptor->extension_range_count() > 0) {
+ ExtensionDict* extension_dict = extension_dict::NewExtensionDict(self);
+ if (extension_dict == NULL) {
+ return NULL;
+ }
+ self->extensions = extension_dict;
+ Py_INCREF(self->extensions);
+ return reinterpret_cast<PyObject*>(self->extensions);
+ }
+
+ PyErr_SetNone(PyExc_AttributeError);
+ return NULL;
+}
+
+static PyGetSetDef Getters[] = {
+ {"Extensions", (getter)GetExtensionDict, NULL, "Extension dict"},
{NULL}
};
@@ -2592,10 +2620,10 @@ PyObject* GetAttr(CMessage* self, PyObject* name) {
if (value_class == NULL) {
return NULL;
}
- py_container = message_map_container::NewContainer(self, field_descriptor,
- value_class);
+ py_container =
+ NewMessageMapContainer(self, field_descriptor, value_class);
} else {
- py_container = scalar_map_container::NewContainer(self, field_descriptor);
+ py_container = NewScalarMapContainer(self, field_descriptor);
}
if (py_container == NULL) {
return NULL;
@@ -2672,7 +2700,10 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) {
}
}
- PyErr_Format(PyExc_AttributeError, "Assignment not allowed");
+ PyErr_Format(PyExc_AttributeError,
+ "Assignment not allowed "
+ "(no field \"%s\"in protocol message object).",
+ PyString_AsString(name));
return -1;
}
@@ -2707,8 +2738,8 @@ PyTypeObject CMessage_Type = {
0, // tp_iter
0, // tp_iternext
cmessage::Methods, // tp_methods
- cmessage::Members, // tp_members
- 0, // tp_getset
+ 0, // tp_members
+ cmessage::Getters, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
@@ -2910,12 +2941,12 @@ bool InitProto2MessageModule(PyObject *m) {
reinterpret_cast<PyObject*>(&ScalarMapContainer_Type));
#endif
- if (PyType_Ready(&ScalarMapIterator_Type) < 0) {
+ if (PyType_Ready(&MapIterator_Type) < 0) {
return false;
}
- PyModule_AddObject(m, "ScalarMapIterator",
- reinterpret_cast<PyObject*>(&ScalarMapIterator_Type));
+ PyModule_AddObject(m, "MapIterator",
+ reinterpret_cast<PyObject*>(&MapIterator_Type));
#if PY_MAJOR_VERSION >= 3
@@ -2934,13 +2965,6 @@ bool InitProto2MessageModule(PyObject *m) {
PyModule_AddObject(m, "MessageMapContainer",
reinterpret_cast<PyObject*>(&MessageMapContainer_Type));
#endif
-
- if (PyType_Ready(&MessageMapIterator_Type) < 0) {
- return false;
- }
-
- PyModule_AddObject(m, "MessageMapIterator",
- reinterpret_cast<PyObject*>(&MessageMapIterator_Type));
}
if (PyType_Ready(&ExtensionDict_Type) < 0) {
@@ -2957,6 +2981,9 @@ bool InitProto2MessageModule(PyObject *m) {
PyModule_AddObject(m, "default_pool",
reinterpret_cast<PyObject*>(GetDefaultDescriptorPool()));
+ PyModule_AddObject(m, "DescriptorPool", reinterpret_cast<PyObject*>(
+ &PyDescriptorPool_Type));
+
// This implementation provides full Descriptor types, we advertise it so that
// descriptor.py can use them in replacement of the Python classes.
PyModule_AddIntConstant(m, "_USE_C_DESCRIPTORS", 1);
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
index 1ff82e2f..cc0012e9 100644
--- a/python/google/protobuf/pyext/message.h
+++ b/python/google/protobuf/pyext/message.h
@@ -52,7 +52,12 @@ class Descriptor;
class DescriptorPool;
class MessageFactory;
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+using std::string;
+#else
using internal::shared_ptr;
+#endif
namespace python {
@@ -302,6 +307,7 @@ bool CheckAndGetInteger(
bool CheckAndGetDouble(PyObject* arg, double* value);
bool CheckAndGetFloat(PyObject* arg, float* value);
bool CheckAndGetBool(PyObject* arg, bool* value);
+PyObject* CheckString(PyObject* arg, const FieldDescriptor* descriptor);
bool CheckAndSetString(
PyObject* arg, Message* message,
const FieldDescriptor* descriptor,
diff --git a/python/google/protobuf/pyext/message_map_container.cc b/python/google/protobuf/pyext/message_map_container.cc
deleted file mode 100644
index 8902fa00..00000000
--- a/python/google/protobuf/pyext/message_map_container.cc
+++ /dev/null
@@ -1,569 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Author: haberman@google.com (Josh Haberman)
-
-#include <google/protobuf/pyext/message_map_container.h>
-
-#include <google/protobuf/stubs/logging.h>
-#include <google/protobuf/stubs/common.h>
-#include <google/protobuf/message.h>
-#include <google/protobuf/pyext/message.h>
-#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
-
-namespace google {
-namespace protobuf {
-namespace python {
-
-struct MessageMapIterator {
- PyObject_HEAD;
-
- // This dict contains the full contents of what we want to iterate over.
- // There's no way to avoid building this, because the list representation
- // (which is canonical) can contain duplicate keys. So at the very least we
- // need a set that lets us skip duplicate keys. And at the point that we're
- // doing that, we might as well just build the actual dict we're iterating
- // over and use dict's built-in iterator.
- PyObject* dict;
-
- // An iterator on dict.
- PyObject* iter;
-
- // A pointer back to the container, so we can notice changes to the version.
- MessageMapContainer* container;
-
- // The version of the map when we took the iterator to it.
- //
- // We store this so that if the map is modified during iteration we can throw
- // an error.
- uint64 version;
-};
-
-static MessageMapIterator* GetIter(PyObject* obj) {
- return reinterpret_cast<MessageMapIterator*>(obj);
-}
-
-namespace message_map_container {
-
-static MessageMapContainer* GetMap(PyObject* obj) {
- return reinterpret_cast<MessageMapContainer*>(obj);
-}
-
-// The private constructor of MessageMapContainer objects.
-PyObject* NewContainer(CMessage* parent,
- const google::protobuf::FieldDescriptor* parent_field_descriptor,
- PyObject* concrete_class) {
- if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
- return NULL;
- }
-
-#if PY_MAJOR_VERSION >= 3
- PyObject* obj = PyType_GenericAlloc(
- reinterpret_cast<PyTypeObject *>(MessageMapContainer_Type), 0);
-#else
- PyObject* obj = PyType_GenericAlloc(&MessageMapContainer_Type, 0);
-#endif
- if (obj == NULL) {
- return PyErr_Format(PyExc_RuntimeError,
- "Could not allocate new container.");
- }
-
- MessageMapContainer* self = GetMap(obj);
-
- self->message = parent->message;
- self->parent = parent;
- self->parent_field_descriptor = parent_field_descriptor;
- self->owner = parent->owner;
- self->version = 0;
-
- self->key_field_descriptor =
- parent_field_descriptor->message_type()->FindFieldByName("key");
- self->value_field_descriptor =
- parent_field_descriptor->message_type()->FindFieldByName("value");
-
- self->message_dict = PyDict_New();
- if (self->message_dict == NULL) {
- return PyErr_Format(PyExc_RuntimeError,
- "Could not allocate message dict.");
- }
-
- Py_INCREF(concrete_class);
- self->subclass_init = concrete_class;
-
- if (self->key_field_descriptor == NULL ||
- self->value_field_descriptor == NULL) {
- Py_DECREF(obj);
- return PyErr_Format(PyExc_KeyError,
- "Map entry descriptor did not have key/value fields");
- }
-
- return obj;
-}
-
-// Initializes the underlying Message object of "to" so it becomes a new parent
-// repeated scalar, and copies all the values from "from" to it. A child scalar
-// container can be released by passing it as both from and to (e.g. making it
-// the recipient of the new parent message and copying the values from itself).
-static int InitializeAndCopyToParentContainer(
- MessageMapContainer* from,
- MessageMapContainer* to) {
- // For now we require from == to, re-evaluate if we want to support deep copy
- // as in repeated_composite_container.cc.
- GOOGLE_DCHECK(from == to);
- Message* old_message = from->message;
- Message* new_message = old_message->New();
- to->parent = NULL;
- to->parent_field_descriptor = from->parent_field_descriptor;
- to->message = new_message;
- to->owner.reset(new_message);
-
- vector<const FieldDescriptor*> fields;
- fields.push_back(from->parent_field_descriptor);
- old_message->GetReflection()->SwapFields(old_message, new_message, fields);
- return 0;
-}
-
-static PyObject* GetCMessage(MessageMapContainer* self, Message* entry) {
- // Get or create the CMessage object corresponding to this message.
- Message* message = entry->GetReflection()->MutableMessage(
- entry, self->value_field_descriptor);
- ScopedPyObjectPtr key(PyLong_FromVoidPtr(message));
- PyObject* ret = PyDict_GetItem(self->message_dict, key.get());
-
- if (ret == NULL) {
- CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
- message->GetDescriptor());
- ret = reinterpret_cast<PyObject*>(cmsg);
-
- if (cmsg == NULL) {
- return NULL;
- }
- cmsg->owner = self->owner;
- cmsg->message = message;
- cmsg->parent = self->parent;
-
- if (PyDict_SetItem(self->message_dict, key.get(), ret) < 0) {
- Py_DECREF(ret);
- return NULL;
- }
- } else {
- Py_INCREF(ret);
- }
-
- return ret;
-}
-
-int Release(MessageMapContainer* self) {
- InitializeAndCopyToParentContainer(self, self);
- return 0;
-}
-
-void SetOwner(MessageMapContainer* self,
- const shared_ptr<Message>& new_owner) {
- self->owner = new_owner;
-}
-
-Py_ssize_t Length(PyObject* _self) {
- MessageMapContainer* self = GetMap(_self);
- google::protobuf::Message* message = self->message;
- return message->GetReflection()->FieldSize(*message,
- self->parent_field_descriptor);
-}
-
-int MapKeyMatches(MessageMapContainer* self, const Message* entry,
- PyObject* key) {
- // TODO(haberman): do we need more strict type checking?
- ScopedPyObjectPtr entry_key(
- cmessage::InternalGetScalar(entry, self->key_field_descriptor));
- int ret = PyObject_RichCompareBool(key, entry_key.get(), Py_EQ);
- return ret;
-}
-
-int SetItem(PyObject *_self, PyObject *key, PyObject *v) {
- if (v) {
- PyErr_Format(PyExc_ValueError,
- "Direct assignment of submessage not allowed");
- return -1;
- }
-
- // Now we know that this is a delete, not a set.
-
- MessageMapContainer* self = GetMap(_self);
- cmessage::AssureWritable(self->parent);
-
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
- size_t size =
- reflection->FieldSize(*message, self->parent_field_descriptor);
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search. We need to search from the end because the underlying
- // representation can have duplicates if a user calls MergeFrom(); the last
- // one needs to win.
- //
- // TODO(haberman): add lookup API to Reflection API.
- bool found = false;
- for (int i = size - 1; i >= 0; i--) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, entry, key);
- if (matches < 0) return -1;
- if (matches) {
- found = true;
- if (i != (int)size - 1) {
- reflection->SwapElements(message, self->parent_field_descriptor, i,
- size - 1);
- }
- reflection->RemoveLast(message, self->parent_field_descriptor);
-
- // Can't exit now, the repeated field representation of maps allows
- // duplicate keys, and we have to be sure to remove all of them.
- }
- }
-
- if (!found) {
- PyErr_Format(PyExc_KeyError, "Key not present in map");
- return -1;
- }
-
- self->version++;
-
- return 0;
-}
-
-PyObject* GetIterator(PyObject *_self) {
- MessageMapContainer* self = GetMap(_self);
-
- ScopedPyObjectPtr obj(PyType_GenericAlloc(&MessageMapIterator_Type, 0));
- if (obj == NULL) {
- return PyErr_Format(PyExc_KeyError, "Could not allocate iterator");
- }
-
- MessageMapIterator* iter = GetIter(obj.get());
-
- Py_INCREF(self);
- iter->container = self;
- iter->version = self->version;
- iter->dict = PyDict_New();
- if (iter->dict == NULL) {
- return PyErr_Format(PyExc_RuntimeError,
- "Could not allocate dict for iterator.");
- }
-
- // Build the entire map into a dict right now. Start from the beginning so
- // that later entries win in the case of duplicates.
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search. We need to search from the end because the underlying
- // representation can have duplicates if a user calls MergeFrom(); the last
- // one needs to win.
- //
- // TODO(haberman): add lookup API to Reflection API.
- size_t size =
- reflection->FieldSize(*message, self->parent_field_descriptor);
- for (int i = size - 1; i >= 0; i--) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- ScopedPyObjectPtr key(
- cmessage::InternalGetScalar(entry, self->key_field_descriptor));
- if (PyDict_SetItem(iter->dict, key.get(), GetCMessage(self, entry)) < 0) {
- return PyErr_Format(PyExc_RuntimeError,
- "SetItem failed in iterator construction.");
- }
- }
-
- iter->iter = PyObject_GetIter(iter->dict);
-
- return obj.release();
-}
-
-PyObject* GetItem(PyObject* _self, PyObject* key) {
- MessageMapContainer* self = GetMap(_self);
- cmessage::AssureWritable(self->parent);
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search. We need to search from the end because the underlying
- // representation can have duplicates if a user calls MergeFrom(); the last
- // one needs to win.
- //
- // TODO(haberman): add lookup API to Reflection API.
- size_t size =
- reflection->FieldSize(*message, self->parent_field_descriptor);
- for (int i = size - 1; i >= 0; i--) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, entry, key);
- if (matches < 0) return NULL;
- if (matches) {
- return GetCMessage(self, entry);
- }
- }
-
- // Key is not already present; insert a new entry.
- Message* entry =
- reflection->AddMessage(message, self->parent_field_descriptor);
-
- self->version++;
-
- if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor,
- key) < 0) {
- reflection->RemoveLast(message, self->parent_field_descriptor);
- return NULL;
- }
-
- return GetCMessage(self, entry);
-}
-
-PyObject* Contains(PyObject* _self, PyObject* key) {
- MessageMapContainer* self = GetMap(_self);
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search.
- //
- // TODO(haberman): add lookup API to Reflection API.
- int size =
- reflection->FieldSize(*message, self->parent_field_descriptor);
- for (int i = 0; i < size; i++) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, entry, key);
- if (matches < 0) return NULL;
- if (matches) {
- Py_RETURN_TRUE;
- }
- }
-
- Py_RETURN_FALSE;
-}
-
-PyObject* Clear(PyObject* _self) {
- MessageMapContainer* self = GetMap(_self);
- cmessage::AssureWritable(self->parent);
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- self->version++;
- reflection->ClearField(message, self->parent_field_descriptor);
-
- Py_RETURN_NONE;
-}
-
-PyObject* Get(PyObject* self, PyObject* args) {
- PyObject* key;
- PyObject* default_value = NULL;
- if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
- return NULL;
- }
-
- ScopedPyObjectPtr is_present(Contains(self, key));
- if (is_present.get() == NULL) {
- return NULL;
- }
-
- if (PyObject_IsTrue(is_present.get())) {
- return GetItem(self, key);
- } else {
- if (default_value != NULL) {
- Py_INCREF(default_value);
- return default_value;
- } else {
- Py_RETURN_NONE;
- }
- }
-}
-
-static void Dealloc(PyObject* _self) {
- MessageMapContainer* self = GetMap(_self);
- self->owner.reset();
- Py_DECREF(self->message_dict);
- Py_TYPE(_self)->tp_free(_self);
-}
-
-static PyMethodDef Methods[] = {
- { "__contains__", (PyCFunction)Contains, METH_O,
- "Tests whether the map contains this element."},
- { "clear", (PyCFunction)Clear, METH_NOARGS,
- "Removes all elements from the map."},
- { "get", Get, METH_VARARGS,
- "Gets the value for the given key if present, or otherwise a default" },
- { "get_or_create", GetItem, METH_O,
- "Alias for getitem, useful to make explicit that the map is mutated." },
- /*
- { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
- "Makes a deep copy of the class." },
- { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
- "Outputs picklable representation of the repeated field." },
- */
- {NULL, NULL},
-};
-
-} // namespace message_map_container
-
-namespace message_map_iterator {
-
-static void Dealloc(PyObject* _self) {
- MessageMapIterator* self = GetIter(_self);
- Py_DECREF(self->dict);
- Py_DECREF(self->iter);
- Py_DECREF(self->container);
- Py_TYPE(_self)->tp_free(_self);
-}
-
-PyObject* IterNext(PyObject* _self) {
- MessageMapIterator* self = GetIter(_self);
-
- // This won't catch mutations to the map performed by MergeFrom(); no easy way
- // to address that.
- if (self->version != self->container->version) {
- return PyErr_Format(PyExc_RuntimeError,
- "Map modified during iteration.");
- }
-
- return PyIter_Next(self->iter);
-}
-
-} // namespace message_map_iterator
-
-#if PY_MAJOR_VERSION >= 3
- static PyType_Slot MessageMapContainer_Type_slots[] = {
- {Py_tp_dealloc, (void *)message_map_container::Dealloc},
- {Py_mp_length, (void *)message_map_container::Length},
- {Py_mp_subscript, (void *)message_map_container::GetItem},
- {Py_mp_ass_subscript, (void *)message_map_container::SetItem},
- {Py_tp_methods, (void *)message_map_container::Methods},
- {Py_tp_iter, (void *)message_map_container::GetIterator},
- {0, 0}
- };
-
- PyType_Spec MessageMapContainer_Type_spec = {
- FULL_MODULE_NAME ".MessageMapContainer",
- sizeof(MessageMapContainer),
- 0,
- Py_TPFLAGS_DEFAULT,
- MessageMapContainer_Type_slots
- };
-
- PyObject *MessageMapContainer_Type;
-
-#else
- static PyMappingMethods MpMethods = {
- message_map_container::Length, // mp_length
- message_map_container::GetItem, // mp_subscript
- message_map_container::SetItem, // mp_ass_subscript
- };
-
- PyTypeObject MessageMapContainer_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- FULL_MODULE_NAME ".MessageMapContainer", // tp_name
- sizeof(MessageMapContainer), // tp_basicsize
- 0, // tp_itemsize
- message_map_container::Dealloc, // tp_dealloc
- 0, // tp_print
- 0, // tp_getattr
- 0, // tp_setattr
- 0, // tp_compare
- 0, // tp_repr
- 0, // tp_as_number
- 0, // tp_as_sequence
- &MpMethods, // tp_as_mapping
- 0, // tp_hash
- 0, // tp_call
- 0, // tp_str
- 0, // tp_getattro
- 0, // tp_setattro
- 0, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "A map container for message", // tp_doc
- 0, // tp_traverse
- 0, // tp_clear
- 0, // tp_richcompare
- 0, // tp_weaklistoffset
- message_map_container::GetIterator, // tp_iter
- 0, // tp_iternext
- message_map_container::Methods, // tp_methods
- 0, // tp_members
- 0, // tp_getset
- 0, // tp_base
- 0, // tp_dict
- 0, // tp_descr_get
- 0, // tp_descr_set
- 0, // tp_dictoffset
- 0, // tp_init
- };
-#endif
-
-PyTypeObject MessageMapIterator_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- FULL_MODULE_NAME ".MessageMapIterator", // tp_name
- sizeof(MessageMapIterator), // tp_basicsize
- 0, // tp_itemsize
- message_map_iterator::Dealloc, // tp_dealloc
- 0, // tp_print
- 0, // tp_getattr
- 0, // tp_setattr
- 0, // tp_compare
- 0, // tp_repr
- 0, // tp_as_number
- 0, // tp_as_sequence
- 0, // tp_as_mapping
- 0, // tp_hash
- 0, // tp_call
- 0, // tp_str
- 0, // tp_getattro
- 0, // tp_setattro
- 0, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "A scalar map iterator", // tp_doc
- 0, // tp_traverse
- 0, // tp_clear
- 0, // tp_richcompare
- 0, // tp_weaklistoffset
- PyObject_SelfIter, // tp_iter
- message_map_iterator::IterNext, // tp_iternext
- 0, // tp_methods
- 0, // tp_members
- 0, // tp_getset
- 0, // tp_base
- 0, // tp_dict
- 0, // tp_descr_get
- 0, // tp_descr_set
- 0, // tp_dictoffset
- 0, // tp_init
-};
-
-} // namespace python
-} // namespace protobuf
-} // namespace google
diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h
index 3013aba9..58d37b02 100644
--- a/python/google/protobuf/pyext/repeated_composite_container.h
+++ b/python/google/protobuf/pyext/repeated_composite_container.h
@@ -49,7 +49,11 @@ namespace protobuf {
class FieldDescriptor;
class Message;
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
using internal::shared_ptr;
+#endif
namespace python {
diff --git a/python/google/protobuf/pyext/repeated_scalar_container.h b/python/google/protobuf/pyext/repeated_scalar_container.h
index 5dfa21e0..555e621c 100644
--- a/python/google/protobuf/pyext/repeated_scalar_container.h
+++ b/python/google/protobuf/pyext/repeated_scalar_container.h
@@ -48,7 +48,11 @@ namespace protobuf {
class Message;
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
using internal::shared_ptr;
+#endif
namespace python {
diff --git a/python/google/protobuf/pyext/scalar_map_container.cc b/python/google/protobuf/pyext/scalar_map_container.cc
deleted file mode 100644
index 0b0d5a3d..00000000
--- a/python/google/protobuf/pyext/scalar_map_container.cc
+++ /dev/null
@@ -1,542 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Author: haberman@google.com (Josh Haberman)
-
-#include <google/protobuf/pyext/scalar_map_container.h>
-
-#include <google/protobuf/stubs/logging.h>
-#include <google/protobuf/stubs/common.h>
-#include <google/protobuf/message.h>
-#include <google/protobuf/pyext/message.h>
-#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
-
-namespace google {
-namespace protobuf {
-namespace python {
-
-struct ScalarMapIterator {
- PyObject_HEAD;
-
- // This dict contains the full contents of what we want to iterate over.
- // There's no way to avoid building this, because the list representation
- // (which is canonical) can contain duplicate keys. So at the very least we
- // need a set that lets us skip duplicate keys. And at the point that we're
- // doing that, we might as well just build the actual dict we're iterating
- // over and use dict's built-in iterator.
- PyObject* dict;
-
- // An iterator on dict.
- PyObject* iter;
-
- // A pointer back to the container, so we can notice changes to the version.
- ScalarMapContainer* container;
-
- // The version of the map when we took the iterator to it.
- //
- // We store this so that if the map is modified during iteration we can throw
- // an error.
- uint64 version;
-};
-
-static ScalarMapIterator* GetIter(PyObject* obj) {
- return reinterpret_cast<ScalarMapIterator*>(obj);
-}
-
-namespace scalar_map_container {
-
-static ScalarMapContainer* GetMap(PyObject* obj) {
- return reinterpret_cast<ScalarMapContainer*>(obj);
-}
-
-// The private constructor of ScalarMapContainer objects.
-PyObject *NewContainer(
- CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) {
- if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
- return NULL;
- }
-
-#if PY_MAJOR_VERSION >= 3
- ScopedPyObjectPtr obj(PyType_GenericAlloc(
- reinterpret_cast<PyTypeObject *>(ScalarMapContainer_Type), 0));
-#else
- ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapContainer_Type, 0));
-#endif
- if (obj.get() == NULL) {
- return PyErr_Format(PyExc_RuntimeError,
- "Could not allocate new container.");
- }
-
- ScalarMapContainer* self = GetMap(obj.get());
-
- self->message = parent->message;
- self->parent = parent;
- self->parent_field_descriptor = parent_field_descriptor;
- self->owner = parent->owner;
- self->version = 0;
-
- self->key_field_descriptor =
- parent_field_descriptor->message_type()->FindFieldByName("key");
- self->value_field_descriptor =
- parent_field_descriptor->message_type()->FindFieldByName("value");
-
- if (self->key_field_descriptor == NULL ||
- self->value_field_descriptor == NULL) {
- return PyErr_Format(PyExc_KeyError,
- "Map entry descriptor did not have key/value fields");
- }
-
- return obj.release();
-}
-
-// Initializes the underlying Message object of "to" so it becomes a new parent
-// repeated scalar, and copies all the values from "from" to it. A child scalar
-// container can be released by passing it as both from and to (e.g. making it
-// the recipient of the new parent message and copying the values from itself).
-static int InitializeAndCopyToParentContainer(
- ScalarMapContainer* from,
- ScalarMapContainer* to) {
- // For now we require from == to, re-evaluate if we want to support deep copy
- // as in repeated_scalar_container.cc.
- GOOGLE_DCHECK(from == to);
- Message* old_message = from->message;
- Message* new_message = old_message->New();
- to->parent = NULL;
- to->parent_field_descriptor = from->parent_field_descriptor;
- to->message = new_message;
- to->owner.reset(new_message);
-
- vector<const FieldDescriptor*> fields;
- fields.push_back(from->parent_field_descriptor);
- old_message->GetReflection()->SwapFields(old_message, new_message, fields);
- return 0;
-}
-
-int Release(ScalarMapContainer* self) {
- return InitializeAndCopyToParentContainer(self, self);
-}
-
-void SetOwner(ScalarMapContainer* self,
- const shared_ptr<Message>& new_owner) {
- self->owner = new_owner;
-}
-
-Py_ssize_t Length(PyObject* _self) {
- ScalarMapContainer* self = GetMap(_self);
- google::protobuf::Message* message = self->message;
- return message->GetReflection()->FieldSize(*message,
- self->parent_field_descriptor);
-}
-
-int MapKeyMatches(ScalarMapContainer* self, const Message* entry,
- PyObject* key) {
- // TODO(haberman): do we need more strict type checking?
- ScopedPyObjectPtr entry_key(
- cmessage::InternalGetScalar(entry, self->key_field_descriptor));
- int ret = PyObject_RichCompareBool(key, entry_key.get(), Py_EQ);
- return ret;
-}
-
-PyObject* GetItem(PyObject* _self, PyObject* key) {
- ScalarMapContainer* self = GetMap(_self);
-
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search.
- //
- // TODO(haberman): add lookup API to Reflection API.
- size_t size = reflection->FieldSize(*message, self->parent_field_descriptor);
- for (int i = size - 1; i >= 0; i--) {
- const Message& entry = reflection->GetRepeatedMessage(
- *message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, &entry, key);
- if (matches < 0) return NULL;
- if (matches) {
- return cmessage::InternalGetScalar(&entry, self->value_field_descriptor);
- }
- }
-
- // Need to add a new entry.
- Message* entry =
- reflection->AddMessage(message, self->parent_field_descriptor);
- PyObject* ret = NULL;
-
- if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor,
- key) >= 0) {
- ret = cmessage::InternalGetScalar(entry, self->value_field_descriptor);
- }
-
- self->version++;
-
- // If there was a type error above, it set the Python exception.
- return ret;
-}
-
-int SetItem(PyObject *_self, PyObject *key, PyObject *v) {
- ScalarMapContainer* self = GetMap(_self);
- cmessage::AssureWritable(self->parent);
-
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
- size_t size =
- reflection->FieldSize(*message, self->parent_field_descriptor);
- self->version++;
-
- if (v) {
- // Set item.
- //
- // Right now the Reflection API doesn't support map lookup, so we implement
- // it via linear search.
- //
- // TODO(haberman): add lookup API to Reflection API.
- for (int i = size - 1; i >= 0; i--) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, entry, key);
- if (matches < 0) return -1;
- if (matches) {
- return cmessage::InternalSetNonOneofScalar(
- entry, self->value_field_descriptor, v);
- }
- }
-
- // Key is not already present; insert a new entry.
- Message* entry =
- reflection->AddMessage(message, self->parent_field_descriptor);
-
- if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor,
- key) < 0 ||
- cmessage::InternalSetNonOneofScalar(entry, self->value_field_descriptor,
- v) < 0) {
- reflection->RemoveLast(message, self->parent_field_descriptor);
- return -1;
- }
-
- return 0;
- } else {
- bool found = false;
- for (int i = size - 1; i >= 0; i--) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, entry, key);
- if (matches < 0) return -1;
- if (matches) {
- found = true;
- if (i != (int)size - 1) {
- reflection->SwapElements(message, self->parent_field_descriptor, i,
- size - 1);
- }
- reflection->RemoveLast(message, self->parent_field_descriptor);
-
- // Can't exit now, the repeated field representation of maps allows
- // duplicate keys, and we have to be sure to remove all of them.
- }
- }
-
- if (found) {
- return 0;
- } else {
- PyErr_Format(PyExc_KeyError, "Key not present in map");
- return -1;
- }
- }
-}
-
-PyObject* GetIterator(PyObject *_self) {
- ScalarMapContainer* self = GetMap(_self);
-
- ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapIterator_Type, 0));
- if (obj == NULL) {
- return PyErr_Format(PyExc_KeyError, "Could not allocate iterator");
- }
-
- ScalarMapIterator* iter = GetIter(obj.get());
-
- Py_INCREF(self);
- iter->container = self;
- iter->version = self->version;
- iter->dict = PyDict_New();
- if (iter->dict == NULL) {
- return PyErr_Format(PyExc_RuntimeError,
- "Could not allocate dict for iterator.");
- }
-
- // Build the entire map into a dict right now. Start from the beginning so
- // that later entries win in the case of duplicates.
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search. We need to search from the end because the underlying
- // representation can have duplicates if a user calls MergeFrom(); the last
- // one needs to win.
- //
- // TODO(haberman): add lookup API to Reflection API.
- size_t size =
- reflection->FieldSize(*message, self->parent_field_descriptor);
- for (size_t i = 0; i < size; i++) {
- Message* entry = reflection->MutableRepeatedMessage(
- message, self->parent_field_descriptor, i);
- ScopedPyObjectPtr key(
- cmessage::InternalGetScalar(entry, self->key_field_descriptor));
- ScopedPyObjectPtr val(
- cmessage::InternalGetScalar(entry, self->value_field_descriptor));
- if (PyDict_SetItem(iter->dict, key.get(), val.get()) < 0) {
- return PyErr_Format(PyExc_RuntimeError,
- "SetItem failed in iterator construction.");
- }
- }
-
-
- iter->iter = PyObject_GetIter(iter->dict);
-
-
- return obj.release();
-}
-
-PyObject* Clear(PyObject* _self) {
- ScalarMapContainer* self = GetMap(_self);
- cmessage::AssureWritable(self->parent);
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- reflection->ClearField(message, self->parent_field_descriptor);
-
- Py_RETURN_NONE;
-}
-
-PyObject* Contains(PyObject* _self, PyObject* key) {
- ScalarMapContainer* self = GetMap(_self);
-
- Message* message = self->message;
- const Reflection* reflection = message->GetReflection();
-
- // Right now the Reflection API doesn't support map lookup, so we implement it
- // via linear search.
- //
- // TODO(haberman): add lookup API to Reflection API.
- size_t size = reflection->FieldSize(*message, self->parent_field_descriptor);
- for (int i = size - 1; i >= 0; i--) {
- const Message& entry = reflection->GetRepeatedMessage(
- *message, self->parent_field_descriptor, i);
- int matches = MapKeyMatches(self, &entry, key);
- if (matches < 0) return NULL;
- if (matches) {
- Py_RETURN_TRUE;
- }
- }
-
- Py_RETURN_FALSE;
-}
-
-PyObject* Get(PyObject* self, PyObject* args) {
- PyObject* key;
- PyObject* default_value = NULL;
- if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
- return NULL;
- }
-
- ScopedPyObjectPtr is_present(Contains(self, key));
- if (is_present.get() == NULL) {
- return NULL;
- }
-
- if (PyObject_IsTrue(is_present.get())) {
- return GetItem(self, key);
- } else {
- if (default_value != NULL) {
- Py_INCREF(default_value);
- return default_value;
- } else {
- Py_RETURN_NONE;
- }
- }
-}
-
-static void Dealloc(PyObject* _self) {
- ScalarMapContainer* self = GetMap(_self);
- self->owner.reset();
- Py_TYPE(_self)->tp_free(_self);
-}
-
-static PyMethodDef Methods[] = {
- { "__contains__", Contains, METH_O,
- "Tests whether a key is a member of the map." },
- { "clear", (PyCFunction)Clear, METH_NOARGS,
- "Removes all elements from the map." },
- { "get", Get, METH_VARARGS,
- "Gets the value for the given key if present, or otherwise a default" },
- /*
- { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
- "Makes a deep copy of the class." },
- { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
- "Outputs picklable representation of the repeated field." },
- */
- {NULL, NULL},
-};
-
-} // namespace scalar_map_container
-
-namespace scalar_map_iterator {
-
-static void Dealloc(PyObject* _self) {
- ScalarMapIterator* self = GetIter(_self);
- Py_DECREF(self->dict);
- Py_DECREF(self->iter);
- Py_DECREF(self->container);
- Py_TYPE(_self)->tp_free(_self);
-}
-
-PyObject* IterNext(PyObject* _self) {
- ScalarMapIterator* self = GetIter(_self);
-
- // This won't catch mutations to the map performed by MergeFrom(); no easy way
- // to address that.
- if (self->version != self->container->version) {
- return PyErr_Format(PyExc_RuntimeError,
- "Map modified during iteration.");
- }
-
- return PyIter_Next(self->iter);
-}
-
-} // namespace scalar_map_iterator
-
-
-#if PY_MAJOR_VERSION >= 3
- static PyType_Slot ScalarMapContainer_Type_slots[] = {
- {Py_tp_dealloc, (void *)scalar_map_container::Dealloc},
- {Py_mp_length, (void *)scalar_map_container::Length},
- {Py_mp_subscript, (void *)scalar_map_container::GetItem},
- {Py_mp_ass_subscript, (void *)scalar_map_container::SetItem},
- {Py_tp_methods, (void *)scalar_map_container::Methods},
- {Py_tp_iter, (void *)scalar_map_container::GetIterator},
- {0, 0},
- };
-
- PyType_Spec ScalarMapContainer_Type_spec = {
- FULL_MODULE_NAME ".ScalarMapContainer",
- sizeof(ScalarMapContainer),
- 0,
- Py_TPFLAGS_DEFAULT,
- ScalarMapContainer_Type_slots
- };
- PyObject *ScalarMapContainer_Type;
-#else
- static PyMappingMethods MpMethods = {
- scalar_map_container::Length, // mp_length
- scalar_map_container::GetItem, // mp_subscript
- scalar_map_container::SetItem, // mp_ass_subscript
- };
-
- PyTypeObject ScalarMapContainer_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- FULL_MODULE_NAME ".ScalarMapContainer", // tp_name
- sizeof(ScalarMapContainer), // tp_basicsize
- 0, // tp_itemsize
- scalar_map_container::Dealloc, // tp_dealloc
- 0, // tp_print
- 0, // tp_getattr
- 0, // tp_setattr
- 0, // tp_compare
- 0, // tp_repr
- 0, // tp_as_number
- 0, // tp_as_sequence
- &MpMethods, // tp_as_mapping
- 0, // tp_hash
- 0, // tp_call
- 0, // tp_str
- 0, // tp_getattro
- 0, // tp_setattro
- 0, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "A scalar map container", // tp_doc
- 0, // tp_traverse
- 0, // tp_clear
- 0, // tp_richcompare
- 0, // tp_weaklistoffset
- scalar_map_container::GetIterator, // tp_iter
- 0, // tp_iternext
- scalar_map_container::Methods, // tp_methods
- 0, // tp_members
- 0, // tp_getset
- 0, // tp_base
- 0, // tp_dict
- 0, // tp_descr_get
- 0, // tp_descr_set
- 0, // tp_dictoffset
- 0, // tp_init
- };
-#endif
-
-PyTypeObject ScalarMapIterator_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- FULL_MODULE_NAME ".ScalarMapIterator", // tp_name
- sizeof(ScalarMapIterator), // tp_basicsize
- 0, // tp_itemsize
- scalar_map_iterator::Dealloc, // tp_dealloc
- 0, // tp_print
- 0, // tp_getattr
- 0, // tp_setattr
- 0, // tp_compare
- 0, // tp_repr
- 0, // tp_as_number
- 0, // tp_as_sequence
- 0, // tp_as_mapping
- 0, // tp_hash
- 0, // tp_call
- 0, // tp_str
- 0, // tp_getattro
- 0, // tp_setattro
- 0, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "A scalar map iterator", // tp_doc
- 0, // tp_traverse
- 0, // tp_clear
- 0, // tp_richcompare
- 0, // tp_weaklistoffset
- PyObject_SelfIter, // tp_iter
- scalar_map_iterator::IterNext, // tp_iternext
- 0, // tp_methods
- 0, // tp_members
- 0, // tp_getset
- 0, // tp_base
- 0, // tp_dict
- 0, // tp_descr_get
- 0, // tp_descr_set
- 0, // tp_dictoffset
- 0, // tp_init
-};
-
-} // namespace python
-} // namespace protobuf
-} // namespace google
diff --git a/python/google/protobuf/pyext/scalar_map_container.h b/python/google/protobuf/pyext/scalar_map_container.h
deleted file mode 100644
index aded8d49..00000000
--- a/python/google/protobuf/pyext/scalar_map_container.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__
-#define GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__
-
-#include <Python.h>
-
-#include <memory>
-#ifndef _SHARED_PTR_H
-#include <google/protobuf/stubs/shared_ptr.h>
-#endif
-
-#include <google/protobuf/descriptor.h>
-
-namespace google {
-namespace protobuf {
-
-class Message;
-
-using internal::shared_ptr;
-
-namespace python {
-
-struct CMessage;
-
-struct ScalarMapContainer {
- PyObject_HEAD;
-
- // This is the top-level C++ Message object that owns the whole
- // proto tree. Every Python ScalarMapContainer holds a
- // reference to it in order to keep it alive as long as there's a
- // Python object that references any part of the tree.
- shared_ptr<Message> owner;
-
- // Pointer to the C++ Message that contains this container. The
- // ScalarMapContainer does not own this pointer.
- Message* message;
-
- // Weak reference to a parent CMessage object (i.e. may be NULL.)
- //
- // Used to make sure all ancestors are also mutable when first
- // modifying the container.
- CMessage* parent;
-
- // Pointer to the parent's descriptor that describes this
- // field. Used together with the parent's message when making a
- // default message instance mutable.
- // The pointer is owned by the global DescriptorPool.
- const FieldDescriptor* parent_field_descriptor;
- const FieldDescriptor* key_field_descriptor;
- const FieldDescriptor* value_field_descriptor;
-
- // We bump this whenever we perform a mutation, to invalidate existing
- // iterators.
- uint64 version;
-};
-
-#if PY_MAJOR_VERSION >= 3
- extern PyObject *ScalarMapContainer_Type;
- extern PyType_Spec ScalarMapContainer_Type_spec;
-#else
- extern PyTypeObject ScalarMapContainer_Type;
-#endif
-extern PyTypeObject ScalarMapIterator_Type;
-
-namespace scalar_map_container {
-
-// Builds a ScalarMapContainer object, from a parent message and a
-// field descriptor.
-extern PyObject *NewContainer(
- CMessage* parent, const FieldDescriptor* parent_field_descriptor);
-
-// Releases the messages in the container to a new message.
-//
-// Returns 0 on success, -1 on failure.
-int Release(ScalarMapContainer* self);
-
-// Set the owner field of self and any children of self.
-void SetOwner(ScalarMapContainer* self,
- const shared_ptr<Message>& new_owner);
-
-} // namespace scalar_map_container
-} // namespace python
-} // namespace protobuf
-
-} // namespace google
-#endif // GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__
diff --git a/python/google/protobuf/symbol_database.py b/python/google/protobuf/symbol_database.py
index b81ef4d7..87760f26 100644
--- a/python/google/protobuf/symbol_database.py
+++ b/python/google/protobuf/symbol_database.py
@@ -60,7 +60,6 @@ Example usage:
"""
-from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool
@@ -73,37 +72,12 @@ class SymbolDatabase(object):
buffer types used within a program.
"""
- # pylint: disable=protected-access
- if _descriptor._USE_C_DESCRIPTORS:
-
- def __new__(cls):
- raise TypeError("Instances of SymbolDatabase cannot be created")
-
- @classmethod
- def _CreateDefaultDatabase(cls):
- self = object.__new__(cls) # Bypass the __new__ above.
- # Don't call __init__() and initialize here.
- self._symbols = {}
- self._symbols_by_file = {}
- # As of today all descriptors are registered and retrieved from
- # _message.default_pool (see FileDescriptor.__new__), so it's not
- # necessary to use another pool.
- self.pool = _descriptor._message.default_pool
- return self
- # pylint: enable=protected-access
-
- else:
-
- @classmethod
- def _CreateDefaultDatabase(cls):
- return cls()
-
- def __init__(self):
+ def __init__(self, pool=None):
"""Constructor."""
self._symbols = {}
self._symbols_by_file = {}
- self.pool = descriptor_pool.DescriptorPool()
+ self.pool = pool or descriptor_pool.Default()
def RegisterMessage(self, message):
"""Registers the given message type in the local database.
@@ -203,7 +177,7 @@ class SymbolDatabase(object):
result.update(self._symbols_by_file[f])
return result
-_DEFAULT = SymbolDatabase._CreateDefaultDatabase()
+_DEFAULT = SymbolDatabase(pool=descriptor_pool.Default())
def Default():
diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py
index e4fadf09..8d256076 100755
--- a/python/google/protobuf/text_format.py
+++ b/python/google/protobuf/text_format.py
@@ -66,6 +66,7 @@ _FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE)
_FLOAT_NAN = re.compile('nanf?', re.IGNORECASE)
_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
+_QUOTES = frozenset(("'", '"'))
class Error(Exception):
@@ -73,7 +74,8 @@ class Error(Exception):
class ParseError(Error):
- """Thrown in case of ASCII parsing error."""
+ """Thrown in case of text parsing error."""
+
class TextWriter(object):
def __init__(self, as_utf8):
@@ -102,7 +104,8 @@ def MessageToString(message, as_utf8=False, as_one_line=False,
Floating point values can be formatted compactly with 15 digits of
precision (which is the most that IEEE 754 "double" can guarantee)
- using float_format='.15g'.
+ using float_format='.15g'. To ensure that converting to text and back to a
+ proto will result in an identical value, float_format='.17g' should be used.
Args:
message: The protocol buffers message.
@@ -130,11 +133,13 @@ def MessageToString(message, as_utf8=False, as_one_line=False,
return result.rstrip()
return result
+
def _IsMapEntry(field):
return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
field.message_type.has_options and
field.message_type.GetOptions().map_entry)
+
def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False,
pointy_brackets=False, use_index_order=False,
float_format=None):
@@ -166,17 +171,18 @@ def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False,
use_index_order=use_index_order,
float_format=float_format)
+
def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False,
pointy_brackets=False, use_index_order=False, float_format=None):
"""Print a single field name/value pair. For repeated fields, the value
- should be a single element."""
+ 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:
@@ -262,95 +268,113 @@ def PrintFieldValue(field, value, out, indent=0, as_utf8=False,
out.write(str(value))
-def Parse(text, message):
- """Parses an ASCII representation of a protocol message into a message.
+def Parse(text, message, allow_unknown_extension=False):
+ """Parses an text representation of a protocol message into a message.
Args:
- text: Message ASCII representation.
+ text: Message text representation.
message: A protocol buffer message to merge into.
+ allow_unknown_extension: if True, skip over missing extensions and keep
+ parsing
Returns:
The same message passed as argument.
Raises:
- ParseError: On ASCII parsing problems.
+ ParseError: On text parsing problems.
"""
- if not isinstance(text, str): text = text.decode('utf-8')
- return ParseLines(text.split('\n'), message)
+ if not isinstance(text, str):
+ text = text.decode('utf-8')
+ return ParseLines(text.split('\n'), message, allow_unknown_extension)
-def Merge(text, message):
- """Parses an ASCII representation of a protocol message into a message.
+def Merge(text, message, allow_unknown_extension=False):
+ """Parses an text representation of a protocol message into a message.
Like Parse(), but allows repeated values for a non-repeated field, and uses
the last one.
Args:
- text: Message ASCII representation.
+ text: Message text representation.
message: A protocol buffer message to merge into.
+ allow_unknown_extension: if True, skip over missing extensions and keep
+ parsing
Returns:
The same message passed as argument.
Raises:
- ParseError: On ASCII parsing problems.
+ ParseError: On text parsing problems.
"""
- return MergeLines(text.split('\n'), message)
+ return MergeLines(text.split('\n'), message, allow_unknown_extension)
-def ParseLines(lines, message):
- """Parses an ASCII representation of a protocol message into a message.
+def ParseLines(lines, message, allow_unknown_extension=False):
+ """Parses an text representation of a protocol message into a message.
Args:
- lines: An iterable of lines of a message's ASCII representation.
+ lines: An iterable of lines of a message's text representation.
message: A protocol buffer message to merge into.
+ allow_unknown_extension: if True, skip over missing extensions and keep
+ parsing
Returns:
The same message passed as argument.
Raises:
- ParseError: On ASCII parsing problems.
+ ParseError: On text parsing problems.
"""
- _ParseOrMerge(lines, message, False)
+ _ParseOrMerge(lines, message, False, allow_unknown_extension)
return message
-def MergeLines(lines, message):
- """Parses an ASCII representation of a protocol message into a message.
+def MergeLines(lines, message, allow_unknown_extension=False):
+ """Parses an text representation of a protocol message into a message.
Args:
- lines: An iterable of lines of a message's ASCII representation.
+ lines: An iterable of lines of a message's text representation.
message: A protocol buffer message to merge into.
+ allow_unknown_extension: if True, skip over missing extensions and keep
+ parsing
Returns:
The same message passed as argument.
Raises:
- ParseError: On ASCII parsing problems.
+ ParseError: On text parsing problems.
"""
- _ParseOrMerge(lines, message, True)
+ _ParseOrMerge(lines, message, True, allow_unknown_extension)
return message
-def _ParseOrMerge(lines, message, allow_multiple_scalars):
- """Converts an ASCII representation of a protocol message into a message.
+def _ParseOrMerge(lines,
+ message,
+ allow_multiple_scalars,
+ allow_unknown_extension=False):
+ """Converts an text representation of a protocol message into a message.
Args:
- lines: Lines of a message's ASCII representation.
+ lines: Lines of a message's text representation.
message: A protocol buffer message to merge into.
allow_multiple_scalars: Determines if repeated values for a non-repeated
field are permitted, e.g., the string "foo: 1 foo: 2" for a
required/optional field named "foo".
+ allow_unknown_extension: if True, skip over missing extensions and keep
+ parsing
Raises:
- ParseError: On ASCII parsing problems.
+ ParseError: On text parsing problems.
"""
tokenizer = _Tokenizer(lines)
while not tokenizer.AtEnd():
- _MergeField(tokenizer, message, allow_multiple_scalars)
+ _MergeField(tokenizer, message, allow_multiple_scalars,
+ allow_unknown_extension)
-def _MergeField(tokenizer, message, allow_multiple_scalars):
+def _MergeField(tokenizer,
+ message,
+ allow_multiple_scalars,
+ allow_unknown_extension=False):
"""Merges a single protocol message field into a message.
Args:
@@ -359,9 +383,11 @@ def _MergeField(tokenizer, message, allow_multiple_scalars):
allow_multiple_scalars: Determines if repeated values for a non-repeated
field are permitted, e.g., the string "foo: 1 foo: 2" for a
required/optional field named "foo".
+ allow_unknown_extension: if True, skip over missing extensions and keep
+ parsing
Raises:
- ParseError: In case of ASCII parsing problems.
+ ParseError: In case of text parsing problems.
"""
message_descriptor = message.DESCRIPTOR
if (hasattr(message_descriptor, 'syntax') and
@@ -383,13 +409,18 @@ def _MergeField(tokenizer, message, allow_multiple_scalars):
field = message.Extensions._FindExtensionByName(name)
# pylint: enable=protected-access
if not field:
- raise tokenizer.ParseErrorPreviousToken(
- 'Extension "%s" not registered.' % name)
+ if allow_unknown_extension:
+ field = None
+ else:
+ raise tokenizer.ParseErrorPreviousToken(
+ 'Extension "%s" not registered.' % name)
elif message_descriptor != field.containing_type:
raise tokenizer.ParseErrorPreviousToken(
'Extension "%s" does not extend message type "%s".' % (
name, message_descriptor.full_name))
+
tokenizer.Consume(']')
+
else:
name = tokenizer.ConsumeIdentifier()
field = message_descriptor.fields_by_name.get(name, None)
@@ -411,7 +442,7 @@ def _MergeField(tokenizer, message, allow_multiple_scalars):
'Message type "%s" has no field named "%s".' % (
message_descriptor.full_name, name))
- if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
+ if field and field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
is_map_entry = _IsMapEntry(field)
tokenizer.TryConsume(':')
@@ -438,7 +469,8 @@ def _MergeField(tokenizer, message, allow_multiple_scalars):
while not tokenizer.TryConsume(end_token):
if tokenizer.AtEnd():
raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token))
- _MergeField(tokenizer, sub_message, allow_multiple_scalars)
+ _MergeField(tokenizer, sub_message, allow_multiple_scalars,
+ allow_unknown_extension)
if is_map_entry:
value_cpptype = field.message_type.fields_by_name['value'].cpp_type
@@ -447,8 +479,63 @@ def _MergeField(tokenizer, message, allow_multiple_scalars):
value.MergeFrom(sub_message.value)
else:
getattr(message, field.name)[sub_message.key] = sub_message.value
+ elif field:
+ tokenizer.Consume(':')
+ if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and
+ tokenizer.TryConsume('[')):
+ # Short repeated format, e.g. "foo: [1, 2, 3]"
+ while True:
+ _MergeScalarField(tokenizer, message, field, allow_multiple_scalars)
+ if tokenizer.TryConsume(']'):
+ break
+ tokenizer.Consume(',')
+ else:
+ _MergeScalarField(tokenizer, message, field, allow_multiple_scalars)
+ else: # Proto field is unknown.
+ assert allow_unknown_extension
+ _SkipFieldContents(tokenizer)
+
+ # For historical reasons, fields may optionally be separated by commas or
+ # semicolons.
+ if not tokenizer.TryConsume(','):
+ tokenizer.TryConsume(';')
+
+
+def _SkipFieldContents(tokenizer):
+ """Skips over contents (value or message) of a field.
+
+ Args:
+ tokenizer: A tokenizer to parse the field name and values.
+ """
+ # Try to guess the type of this field.
+ # If this field is not a message, there should be a ":" between the
+ # field name and the field value and also the field value should not
+ # start with "{" or "<" which indicates the beginning of a message body.
+ # If there is no ":" or there is a "{" or "<" after ":", this field has
+ # to be a message or the input is ill-formed.
+ if tokenizer.TryConsume(':') and not tokenizer.LookingAt(
+ '{') and not tokenizer.LookingAt('<'):
+ _SkipFieldValue(tokenizer)
+ else:
+ _SkipFieldMessage(tokenizer)
+
+
+def _SkipField(tokenizer):
+ """Skips over a complete field (name and value/message).
+
+ Args:
+ tokenizer: A tokenizer to parse the field name and values.
+ """
+ if tokenizer.TryConsume('['):
+ # Consume extension name.
+ tokenizer.ConsumeIdentifier()
+ while tokenizer.TryConsume('.'):
+ tokenizer.ConsumeIdentifier()
+ tokenizer.Consume(']')
else:
- _MergeScalarField(tokenizer, message, field, allow_multiple_scalars)
+ tokenizer.ConsumeIdentifier()
+
+ _SkipFieldContents(tokenizer)
# For historical reasons, fields may optionally be separated by commas or
# semicolons.
@@ -456,6 +543,48 @@ def _MergeField(tokenizer, message, allow_multiple_scalars):
tokenizer.TryConsume(';')
+def _SkipFieldMessage(tokenizer):
+ """Skips over a field message.
+
+ Args:
+ tokenizer: A tokenizer to parse the field name and values.
+ """
+
+ if tokenizer.TryConsume('<'):
+ delimiter = '>'
+ else:
+ tokenizer.Consume('{')
+ delimiter = '}'
+
+ while not tokenizer.LookingAt('>') and not tokenizer.LookingAt('}'):
+ _SkipField(tokenizer)
+
+ tokenizer.Consume(delimiter)
+
+
+def _SkipFieldValue(tokenizer):
+ """Skips over a field value.
+
+ Args:
+ tokenizer: A tokenizer to parse the field name and values.
+
+ Raises:
+ ParseError: In case an invalid field value is found.
+ """
+ # String tokens can come in multiple adjacent string literals.
+ # If we can consume one, consume as many as we can.
+ if tokenizer.TryConsumeString():
+ while tokenizer.TryConsumeString():
+ pass
+ return
+
+ if (not tokenizer.TryConsumeIdentifier() and
+ not tokenizer.TryConsumeInt64() and
+ not tokenizer.TryConsumeUint64() and
+ not tokenizer.TryConsumeFloat()):
+ raise ParseError('Invalid field value: ' + tokenizer.token)
+
+
def _MergeScalarField(tokenizer, message, field, allow_multiple_scalars):
"""Merges a single protocol message scalar field into a message.
@@ -468,10 +597,9 @@ def _MergeScalarField(tokenizer, message, field, allow_multiple_scalars):
required/optional field named "foo".
Raises:
- ParseError: In case of ASCII parsing problems.
+ ParseError: In case of text parsing problems.
RuntimeError: On runtime errors.
"""
- tokenizer.Consume(':')
value = None
if field.type in (descriptor.FieldDescriptor.TYPE_INT32,
@@ -525,7 +653,7 @@ def _MergeScalarField(tokenizer, message, field, allow_multiple_scalars):
class _Tokenizer(object):
- """Protocol buffer ASCII representation tokenizer.
+ """Protocol buffer text representation tokenizer.
This class handles the lower level string parsing by splitting it into
meaningful tokens.
@@ -534,11 +662,13 @@ class _Tokenizer(object):
"""
_WHITESPACE = re.compile('(\\s|(#.*$))+', re.MULTILINE)
- _TOKEN = re.compile(
- '[a-zA-Z_][0-9a-zA-Z_+-]*|' # an identifier
- '[0-9+-][0-9a-zA-Z_.+-]*|' # a number
- '\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|' # a double-quoted string
- '\'([^\'\n\\\\]|\\\\.)*(\'|\\\\?$)') # a single-quoted string
+ _TOKEN = re.compile('|'.join([
+ r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier
+ r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number
+ ] + [ # quoted str for each quote mark
+ r'{qt}([^{qt}\n\\]|\\.)*({qt}|\\?$)'.format(qt=mark) for mark in _QUOTES
+ ]))
+
_IDENTIFIER = re.compile(r'\w+')
def __init__(self, lines):
@@ -555,6 +685,9 @@ class _Tokenizer(object):
self._SkipWhitespace()
self.NextToken()
+ def LookingAt(self, token):
+ return self.token == token
+
def AtEnd(self):
"""Checks the end of the text was reached.
@@ -610,6 +743,13 @@ class _Tokenizer(object):
if not self.TryConsume(token):
raise self._ParseError('Expected "%s".' % token)
+ def TryConsumeIdentifier(self):
+ try:
+ self.ConsumeIdentifier()
+ return True
+ except ParseError:
+ return False
+
def ConsumeIdentifier(self):
"""Consumes protocol message field identifier.
@@ -657,6 +797,13 @@ class _Tokenizer(object):
self.NextToken()
return result
+ def TryConsumeInt64(self):
+ try:
+ self.ConsumeInt64()
+ return True
+ except ParseError:
+ return False
+
def ConsumeInt64(self):
"""Consumes a signed 64bit integer number.
@@ -673,6 +820,13 @@ class _Tokenizer(object):
self.NextToken()
return result
+ def TryConsumeUint64(self):
+ try:
+ self.ConsumeUint64()
+ return True
+ except ParseError:
+ return False
+
def ConsumeUint64(self):
"""Consumes an unsigned 64bit integer number.
@@ -689,6 +843,13 @@ class _Tokenizer(object):
self.NextToken()
return result
+ def TryConsumeFloat(self):
+ try:
+ self.ConsumeFloat()
+ return True
+ except ParseError:
+ return False
+
def ConsumeFloat(self):
"""Consumes an floating point number.
@@ -721,6 +882,13 @@ class _Tokenizer(object):
self.NextToken()
return result
+ def TryConsumeString(self):
+ try:
+ self.ConsumeString()
+ return True
+ except ParseError:
+ return False
+
def ConsumeString(self):
"""Consumes a string value.
@@ -746,7 +914,7 @@ class _Tokenizer(object):
ParseError: If a byte array value couldn't be consumed.
"""
the_list = [self._ConsumeSingleByteString()]
- while self.token and self.token[0] in ('\'', '"'):
+ while self.token and self.token[0] in _QUOTES:
the_list.append(self._ConsumeSingleByteString())
return b''.join(the_list)
@@ -757,11 +925,13 @@ class _Tokenizer(object):
tokens which are automatically concatenated, like in C or Python. This
method only consumes one token.
+ Returns:
+ The token parsed.
Raises:
ParseError: When the wrong format data is found.
"""
text = self.token
- if len(text) < 1 or text[0] not in ('\'', '"'):
+ if len(text) < 1 or text[0] not in _QUOTES:
raise self._ParseError('Expected string but found: %r' % (text,))
if len(text) < 2 or text[-1] != text[0]:
diff --git a/python/setup.py b/python/setup.py
index 9a7eaddf..6ea3bad7 100755
--- a/python/setup.py
+++ b/python/setup.py
@@ -89,6 +89,7 @@ def GenerateUnittestProtos():
generate_proto("../src/google/protobuf/unittest_no_generic_services.proto", False)
generate_proto("../src/google/protobuf/unittest_proto3_arena.proto", False)
generate_proto("../src/google/protobuf/util/json_format_proto3.proto", False)
+ generate_proto("google/protobuf/internal/any_test.proto", False)
generate_proto("google/protobuf/internal/descriptor_pool_test1.proto", False)
generate_proto("google/protobuf/internal/descriptor_pool_test2.proto", False)
generate_proto("google/protobuf/internal/factory_test1.proto", False)
@@ -113,7 +114,8 @@ class clean(_clean):
filepath = os.path.join(dirpath, filename)
if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \
filepath.endswith(".so") or filepath.endswith(".o") or \
- filepath.endswith('google/protobuf/compiler/__init__.py'):
+ filepath.endswith('google/protobuf/compiler/__init__.py') or \
+ filepath.endswith('google/protobuf/util/__init__.py'):
os.remove(filepath)
# _clean is an old-style class, so super() doesn't work.
_clean.run(self)
@@ -136,7 +138,7 @@ class build_py(_build_py):
GenerateUnittestProtos()
# Make sure google.protobuf/** are valid packages.
- for path in ['', 'internal/', 'compiler/', 'pyext/']:
+ for path in ['', 'internal/', 'compiler/', 'pyext/', 'util/']:
try:
open('google/protobuf/%s__init__.py' % path, 'a').close()
except EnvironmentError:
@@ -144,6 +146,16 @@ class build_py(_build_py):
# _build_py is an old-style class, so super() doesn't work.
_build_py.run(self)
+class test_conformance(_build_py):
+ target = 'test_python'
+ def run(self):
+ if sys.version_info >= (2, 7):
+ # Python 2.6 dodges these extra failures.
+ os.environ["CONFORMANCE_PYTHON_EXTRA_FAILURES"] = (
+ "--failure_list failure_list_python-post26.txt")
+ cmd = 'cd ../conformance && make %s' % (test_conformance.target)
+ status = subprocess.check_call(cmd, shell=True)
+
if __name__ == '__main__':
ext_module_list = []
@@ -152,8 +164,9 @@ if __name__ == '__main__':
if cpp_impl in sys.argv:
sys.argv.remove(cpp_impl)
extra_compile_args = ['-Wno-write-strings', '-Wno-invalid-offsetof']
+ test_conformance.target = 'test_python_cpp'
- if "clang" in os.popen('$CC --version').read():
+ if "clang" in os.popen('$CC --version 2> /dev/null').read():
extra_compile_args.append('-Wno-shorten-64-to-32')
if warnings_as_errors in sys.argv:
@@ -174,7 +187,7 @@ if __name__ == '__main__':
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp'
# Keep this list of dependencies in sync with tox.ini.
- install_requires = ['six', 'setuptools']
+ install_requires = ['six>=1.9', 'setuptools']
if sys.version_info <= (2,7):
install_requires.append('ordereddict')
install_requires.append('unittest2')
@@ -207,6 +220,7 @@ if __name__ == '__main__':
cmdclass={
'clean': clean,
'build_py': build_py,
+ 'test_conformance': test_conformance,
},
install_requires=install_requires,
ext_modules=ext_module_list,
diff --git a/python/tox.ini b/python/tox.ini
index a05460b5..cf8d5401 100644
--- a/python/tox.ini
+++ b/python/tox.ini
@@ -15,8 +15,10 @@ commands =
cpp: python setup.py -q build --cpp_implementation --warnings_as_errors
python: python setup.py -q test -q
cpp: python setup.py -q test -q --cpp_implementation
+ python: python setup.py -q test_conformance
+ cpp: python setup.py -q test_conformance --cpp_implementation
deps =
# Keep this list of dependencies in sync with setup.py.
- six
+ six>=1.9
py26: ordereddict
py26: unittest2
diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock
index 8599da75..27e57506 100644
--- a/ruby/Gemfile.lock
+++ b/ruby/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- google-protobuf (3.0.0.alpha.4.0)
+ google-protobuf (3.0.0.alpha.5.0)
GEM
remote: https://rubygems.org/
@@ -10,6 +10,7 @@ GEM
rake (10.4.2)
rake-compiler (0.9.5)
rake
+ rake-compiler-dock (0.5.1)
rubygems-tasks (0.2.4)
test-unit (3.0.9)
power_assert
@@ -21,8 +22,9 @@ PLATFORMS
DEPENDENCIES
google-protobuf!
rake-compiler
+ rake-compiler-dock
rubygems-tasks
test-unit
BUNDLED WITH
- 1.10.6
+ 1.11.2
diff --git a/ruby/Rakefile b/ruby/Rakefile
index c25103d8..81c3119e 100644
--- a/ruby/Rakefile
+++ b/ruby/Rakefile
@@ -20,6 +20,17 @@ else
Rake::ExtensionTask.new("protobuf_c", spec) do |ext|
ext.ext_dir = "ext/google/protobuf_c"
ext.lib_dir = "lib/google"
+ ext.cross_compile = true
+ ext.cross_platform = [
+ 'x86-mingw32', 'x64-mingw32',
+ 'x86_64-linux', 'x86-linux',
+ 'universal-darwin'
+ ]
+ end
+
+ task 'gem:windows' do
+ require 'rake_compiler_dock'
+ RakeCompilerDock.sh "bundle && rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.6"
end
end
diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec
index 3d53209d..7b64ee77 100644
--- a/ruby/google-protobuf.gemspec
+++ b/ruby/google-protobuf.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "google-protobuf"
- s.version = "3.0.0.alpha.4.0"
+ s.version = "3.0.0.alpha.5.0"
s.licenses = ["BSD"]
s.summary = "Protocol Buffers"
s.description = "Protocol Buffers are Google's data interchange format."
@@ -8,12 +8,13 @@ Gem::Specification.new do |s|
s.authors = ["Protobuf Authors"]
s.email = "protobuf@googlegroups.com"
s.require_paths = ["lib"]
- s.files = `git ls-files -z`.split("\x0").find_all{|f| f =~ /lib\/.+\.rb/}
- unless RUBY_PLATFORM == "java"
- s.files += `git ls-files "*.c" "*.h" extconf.rb Makefile`.split
- s.extensions= ["ext/google/protobuf_c/extconf.rb"]
- else
+ s.files = Dir.glob('lib/**/*.rb')
+ if RUBY_PLATFORM == "java"
s.files += ["lib/google/protobuf_java.jar"]
+ else
+ s.files += Dir.glob('ext/**/*')
+ s.extensions= ["ext/google/protobuf_c/extconf.rb"]
+ s.add_development_dependency "rake-compiler-dock"
end
s.test_files = ["tests/basic.rb",
"tests/stress.rb",
diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb
index f0eb6268..62bdd1bf 100644
--- a/ruby/lib/google/protobuf.rb
+++ b/ruby/lib/google/protobuf.rb
@@ -44,7 +44,11 @@ if RUBY_PLATFORM == "java"
require 'json'
require 'google/protobuf_java'
else
- require 'google/protobuf_c'
+ begin
+ require "google/#{RUBY_VERSION.sub(/\.\d$/, '')}/protobuf_c"
+ rescue LoadError
+ require 'google/protobuf_c'
+ end
end
require 'google/protobuf/repeated_field'
diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh
index 9ec7eb22..75db7d93 100755
--- a/ruby/travis-test.sh
+++ b/ruby/travis-test.sh
@@ -17,9 +17,8 @@ test_version() {
"rvm install $version && rvm use $version && \
which ruby && \
gem install bundler && bundle && \
- rake test && \
- cd ../conformance && \
- make test_ruby"
+ rake test &&
+ cd ../conformance && make test_ruby"
fi
}
diff --git a/six.BUILD b/six.BUILD
index 55c53a42..fb0b3604 100644
--- a/six.BUILD
+++ b/six.BUILD
@@ -8,5 +8,6 @@ genrule(
py_library(
name = "six",
srcs = ["six.py"],
+ srcs_version = "PY2AND3",
visibility = ["//visibility:public"],
)
diff --git a/src/Makefile.am b/src/Makefile.am
index b7d64093..073673a5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -148,15 +148,16 @@ nobase_include_HEADERS = \
google/protobuf/compiler/plugin.h \
google/protobuf/compiler/plugin.pb.h \
google/protobuf/compiler/cpp/cpp_generator.h \
+ google/protobuf/compiler/csharp/csharp_generator.h \
+ google/protobuf/compiler/csharp/csharp_names.h \
google/protobuf/compiler/java/java_generator.h \
google/protobuf/compiler/java/java_names.h \
google/protobuf/compiler/javanano/javanano_generator.h \
+ google/protobuf/compiler/js/js_generator.h \
google/protobuf/compiler/objectivec/objectivec_generator.h \
google/protobuf/compiler/objectivec/objectivec_helpers.h \
google/protobuf/compiler/python/python_generator.h \
google/protobuf/compiler/ruby/ruby_generator.h \
- google/protobuf/compiler/csharp/csharp_generator.h \
- google/protobuf/compiler/csharp/csharp_names.h \
google/protobuf/util/type_resolver.h \
google/protobuf/util/field_comparator.h \
google/protobuf/util/field_mask_util.h \
@@ -276,9 +277,9 @@ libprotobuf_la_SOURCES = \
google/protobuf/util/internal/protostream_objectsource.h \
google/protobuf/util/internal/protostream_objectwriter.cc \
google/protobuf/util/internal/protostream_objectwriter.h \
- google/protobuf/util/internal/snake2camel_objectwriter.h \
+ google/protobuf/util/internal/proto_writer.cc \
+ google/protobuf/util/internal/proto_writer.h \
google/protobuf/util/internal/structured_objectwriter.h \
- google/protobuf/util/internal/testdata \
google/protobuf/util/internal/type_info.cc \
google/protobuf/util/internal/type_info.h \
google/protobuf/util/internal/type_info_test_helper.cc \
@@ -341,6 +342,8 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/java/java_enum_lite.h \
google/protobuf/compiler/java/java_extension.cc \
google/protobuf/compiler/java/java_extension.h \
+ google/protobuf/compiler/java/java_extension_lite.cc \
+ google/protobuf/compiler/java/java_extension_lite.h \
google/protobuf/compiler/java/java_field.cc \
google/protobuf/compiler/java/java_field.h \
google/protobuf/compiler/java/java_file.cc \
@@ -386,6 +389,7 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/java/java_string_field_lite.h \
google/protobuf/compiler/java/java_doc_comment.cc \
google/protobuf/compiler/java/java_doc_comment.h \
+ google/protobuf/compiler/js/js_generator.cc \
google/protobuf/compiler/javanano/javanano_enum.cc \
google/protobuf/compiler/javanano/javanano_enum.h \
google/protobuf/compiler/javanano/javanano_enum_field.cc \
@@ -513,6 +517,7 @@ protoc_inputs = \
google/protobuf/util/internal/testdata/struct.proto \
google/protobuf/util/internal/testdata/timestamp_duration.proto \
google/protobuf/util/json_format_proto3.proto \
+ google/protobuf/util/message_differencer_unittest.proto \
google/protobuf/compiler/cpp/cpp_test_large_enum_value.proto
EXTRA_DIST = \
@@ -624,7 +629,9 @@ protoc_outputs = \
google/protobuf/util/internal/testdata/timestamp_duration.pb.cc \
google/protobuf/util/internal/testdata/timestamp_duration.pb.h \
google/protobuf/util/json_format_proto3.pb.cc \
- google/protobuf/util/json_format_proto3.pb.h
+ google/protobuf/util/json_format_proto3.pb.h \
+ google/protobuf/util/message_differencer_unittest.pb.cc \
+ google/protobuf/util/message_differencer_unittest.pb.h
BUILT_SOURCES = $(protoc_outputs)
@@ -722,6 +729,7 @@ protobuf_test_SOURCES = \
google/protobuf/compiler/cpp/cpp_unittest.h \
google/protobuf/compiler/cpp/cpp_unittest.cc \
google/protobuf/compiler/cpp/cpp_plugin_unittest.cc \
+ google/protobuf/compiler/cpp/metadata_test.cc \
google/protobuf/compiler/java/java_plugin_unittest.cc \
google/protobuf/compiler/java/java_doc_comment_unittest.cc \
google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc \
@@ -737,6 +745,7 @@ protobuf_test_SOURCES = \
google/protobuf/util/internal/protostream_objectwriter_test.cc \
google/protobuf/util/internal/type_info_test_helper.cc \
google/protobuf/util/json_util_test.cc \
+ google/protobuf/util/message_differencer_unittest.cc \
google/protobuf/util/time_util_test.cc \
google/protobuf/util/type_resolver_util_test.cc \
$(COMMON_TEST_SOURCES)
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 00000000..4e312c0c
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,209 @@
+Protocol Buffers - Google's data interchange format
+===================================================
+
+[![Build Status](https://travis-ci.org/google/protobuf.svg?branch=master)](https://travis-ci.org/google/protobuf) [![Build status](https://ci.appveyor.com/api/projects/status/73ctee6ua4w2ruin?svg=true)](https://ci.appveyor.com/project/protobuf/protobuf)
+
+Copyright 2008 Google Inc.
+
+https://developers.google.com/protocol-buffers/
+
+C++ Installation - Unix
+-----------------------
+
+To build protobuf from source, the following tools are needed:
+
+ * autoconf
+ * automake
+ * libtool
+ * curl (used to download gmock)
+
+On Ubuntu, you can install them with:
+
+ $ sudo apt-get install autoconf automake libtool curl
+
+On other platforms, please use the corresponding package managing tool to
+install them before proceeding.
+
+If you get the source from github, you need to generate the configure script
+first:
+
+ $ ./autogen.sh
+
+This will download gmock source (which is used for C++ Protocol Buffer
+unit-tests) to the current directory and run automake, autoconf, etc.
+to generate the configure script and various template makefiles.
+
+You can skip this step if you are using a release package (which already
+contains gmock and the configure script).
+
+To build and install the C++ Protocol Buffer runtime and the Protocol
+Buffer compiler (protoc) execute the following:
+
+ $ ./configure
+ $ make
+ $ make check
+ $ sudo make install
+ $ sudo ldconfig # refresh shared library cache.
+
+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.
+
+For advanced usage information on configure and make, please refer to the
+autoconf documentation:
+
+ http://www.gnu.org/software/autoconf/manual/autoconf.html#Running-configure-Scripts
+
+**Hint on install location**
+
+ By default, the package will be installed to /usr/local. However,
+ on many platforms, /usr/local/lib is not part of LD_LIBRARY_PATH.
+ You can add it, but it may be easier to just install to /usr
+ instead. To do this, invoke configure as follows:
+
+ ./configure --prefix=/usr
+
+ If you already built the package with a different prefix, make sure
+ to run "make clean" before building again.
+
+**Compiling dependent packages**
+
+ To compile a package that uses Protocol Buffers, you need to pass
+ various flags to your compiler and linker. As of version 2.2.0,
+ Protocol Buffers integrates with pkg-config to manage this. If you
+ have pkg-config installed, then you can invoke it to get a list of
+ flags like so:
+
+ pkg-config --cflags protobuf # print compiler flags
+ pkg-config --libs protobuf # print linker flags
+ pkg-config --cflags --libs protobuf # print both
+
+ For example:
+
+ c++ my_program.cc my_proto.pb.cc `pkg-config --cflags --libs protobuf`
+
+ Note that packages written prior to the 2.2.0 release of Protocol
+ Buffers may not yet integrate with pkg-config to get flags, and may
+ not pass the correct set of flags to correctly link against
+ libprotobuf. If the package in question uses autoconf, you can
+ often fix the problem by invoking its configure script like:
+
+ configure CXXFLAGS="$(pkg-config --cflags protobuf)" \
+ LIBS="$(pkg-config --libs protobuf)"
+
+ This will force it to use the correct flags.
+
+ If you are writing an autoconf-based package that uses Protocol
+ Buffers, you should probably use the PKG_CHECK_MODULES macro in your
+ configure script like:
+
+ PKG_CHECK_MODULES([protobuf], [protobuf])
+
+ See the pkg-config man page for more info.
+
+ If you only want protobuf-lite, substitute "protobuf-lite" in place
+ of "protobuf" in these examples.
+
+**Note for Mac users**
+
+ For a Mac system, Unix tools are not available by default. You will first need
+ to install Xcode from the Mac AppStore and then run the following command from
+ a terminal:
+
+ $ sudo xcode-select --install
+
+ To install Unix tools, you can install "port" following the instructions at
+ https://www.macports.org . This will reside in /opt/local/bin/port for most
+ Mac installations.
+
+ $ sudo /opt/local/bin/port install autoconf automake libtool
+
+ Then follow the Unix instructions above.
+
+**Note for cross-compiling**
+
+ The makefiles normally invoke the protoc executable that they just
+ built in order to build tests. When cross-compiling, the protoc
+ executable may not be executable on the host machine. In this case,
+ you must build a copy of protoc for the host machine first, then use
+ the --with-protoc option to tell configure to use it instead. For
+ example:
+
+ ./configure --with-protoc=protoc
+
+ This will use the installed protoc (found in your $PATH) instead of
+ trying to execute the one built during the build process. You can
+ also use an executable that hasn't been installed. For example, if
+ you built the protobuf package for your host machine in ../host,
+ you might do:
+
+ ./configure --with-protoc=../host/src/protoc
+
+ Either way, you must make sure that the protoc executable you use
+ has the same version as the protobuf source code you are trying to
+ use it with.
+
+**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.
+
+**Note for HP C++ Tru64 users**
+
+ To compile invoke configure as follows:
+
+ ./configure CXXFLAGS="-O -std ansi -ieee -D__USE_STD_IOSTREAM"
+
+ Also, you will need to use gmake instead of make.
+
+**Note for AIX users**
+
+ Compile using the IBM xlC C++ compiler as follows:
+
+ ./configure CXX=xlC
+
+ Also, you will need to use GNU `make` (`gmake`) instead of AIX `make`.
+
+C++ Installation - Windows
+--------------------------
+
+If you only need the protoc binary, you can download it from the release
+page:
+
+ https://github.com/google/protobuf/releases
+
+In the downloads section, download the zip file protoc-$VERSION-win32.zip.
+It contains the protoc binary as well as public proto files of protobuf
+library.
+
+To build from source using Microsoft Visual C++, see cmake/README.md.
+
+To build from source using Cygwin or MinGW, follow the Unix installation
+instructions, above.
+
+Binary Compatibility Warning
+----------------------------
+
+Due to the nature of C++, it is unlikely that any two versions of the
+Protocol Buffers C++ runtime libraries will have compatible ABIs.
+That is, if you linked an executable against an older version of
+libprotobuf, it is unlikely to work with a newer version without
+re-compiling. This problem, when it occurs, will normally be detected
+immediately on startup of your app. Still, you may want to consider
+using static linkage. You can configure this package to install
+static libraries only using:
+
+ ./configure --disable-shared
+
+Usage
+-----
+
+The complete documentation for Protocol Buffers is available via the
+web at:
+
+ https://developers.google.com/protocol-buffers/
diff --git a/src/google/protobuf/any.cc b/src/google/protobuf/any.cc
index 7351d377..f3ca06bf 100644
--- a/src/google/protobuf/any.cc
+++ b/src/google/protobuf/any.cc
@@ -65,9 +65,16 @@ bool AnyMetadata::UnpackTo(Message* message) const {
}
bool AnyMetadata::InternalIs(const Descriptor* descriptor) const {
- return type_url_->GetNoArena(
- &::google::protobuf::internal::GetEmptyString()) ==
- GetTypeUrl(descriptor);
+ const string type_url = type_url_->GetNoArena(
+ &::google::protobuf::internal::GetEmptyString());
+ const string full_name = descriptor->full_name();
+ if (type_url.length() < full_name.length()) {
+ return false;
+ }
+ return (0 == type_url.compare(
+ type_url.length() - full_name.length(),
+ full_name.length(),
+ full_name));
}
bool ParseAnyTypeUrl(const string& type_url, string* full_type_name) {
diff --git a/src/google/protobuf/any.h b/src/google/protobuf/any.h
index f760ad5d..c8dbef13 100644
--- a/src/google/protobuf/any.h
+++ b/src/google/protobuf/any.h
@@ -75,7 +75,7 @@ extern const char kTypeGoogleProdComPrefix[]; // "type.googleprod.com/".
// Get the proto type name from Any::type_url value. For example, passing
// "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in
// *full_type_name. Returns false if type_url does not start with
-// "type.googleapis.com".
+// "type.googleapis.com" or "type.googleprod.com".
bool ParseAnyTypeUrl(const string& type_url, string* full_type_name);
// See if message is of type google.protobuf.Any, if so, return the descriptors
diff --git a/src/google/protobuf/any.pb.cc b/src/google/protobuf/any.pb.cc
index cbb5d233..0bf523b3 100644
--- a/src/google/protobuf/any.pb.cc
+++ b/src/google/protobuf/any.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/any.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/any.pb.h"
+#include <google/protobuf/any.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -119,10 +120,10 @@ bool Any::UnpackTo(::google::protobuf::Message* message) const {
return _any_metadata_.UnpackTo(message);
}
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Any::kTypeUrlFieldNumber;
const int Any::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Any::Any()
: ::google::protobuf::Message(), _internal_metadata_(NULL), _any_metadata_(&type_url_, &value_) {
diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h
index c324c4af..97982ecf 100644
--- a/src/google/protobuf/any.pb.h
+++ b/src/google/protobuf/any.pb.h
@@ -27,7 +27,7 @@
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/unknown_field_set.h>
-#include "google/protobuf/any.h"
+#include <google/protobuf/any.h>
// @@protoc_insertion_point(includes)
namespace google {
diff --git a/src/google/protobuf/any.proto b/src/google/protobuf/any.proto
index 423699be..e8a18bc3 100644
--- a/src/google/protobuf/any.proto
+++ b/src/google/protobuf/any.proto
@@ -27,21 +27,22 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_generate_equals_and_hash = true;
-option java_multiple_files = true;
-option java_outer_classname = "AnyProto";
-option java_package = "com.google.protobuf";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "AnyProto";
+option java_multiple_files = true;
+option java_generate_equals_and_hash = true;
option objc_class_prefix = "GPB";
-
// `Any` contains an arbitrary serialized message along with a URL
// that describes the type of the serialized message.
//
+//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
@@ -62,8 +63,8 @@ option objc_class_prefix = "GPB";
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
-// `value` which holds the custom JSON in addition to the the `@type`
-// field. Example (for message [google.protobuf.Duration][google.protobuf.Duration]):
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
@@ -80,7 +81,7 @@ message Any {
// * If no schema is provided, `https` is assumed.
// * The last segment of the URL's path must represent the fully
// qualified name of the type (as in `path/google.protobuf.Duration`).
- // * An HTTP GET on the URL must yield a [google.protobuf.Type][google.protobuf.Type]
+ // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc
index 0a2c4ec0..e589a89d 100644
--- a/src/google/protobuf/api.pb.cc
+++ b/src/google/protobuf/api.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/api.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/api.pb.h"
+#include <google/protobuf/api.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -186,7 +187,7 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Api::kNameFieldNumber;
const int Api::kMethodsFieldNumber;
const int Api::kOptionsFieldNumber;
@@ -194,7 +195,7 @@ const int Api::kVersionFieldNumber;
const int Api::kSourceContextFieldNumber;
const int Api::kMixinsFieldNumber;
const int Api::kSyntaxFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Api::Api()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -904,7 +905,7 @@ void Api::clear_syntax() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Method::kNameFieldNumber;
const int Method::kRequestTypeUrlFieldNumber;
const int Method::kRequestStreamingFieldNumber;
@@ -912,7 +913,7 @@ const int Method::kResponseTypeUrlFieldNumber;
const int Method::kResponseStreamingFieldNumber;
const int Method::kOptionsFieldNumber;
const int Method::kSyntaxFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Method::Method()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1608,10 +1609,10 @@ void Method::clear_syntax() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Mixin::kNameFieldNumber;
const int Mixin::kRootFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Mixin::Mixin()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h
index 3c5a6f31..e1dca4e4 100644
--- a/src/google/protobuf/api.pb.h
+++ b/src/google/protobuf/api.pb.h
@@ -27,8 +27,8 @@
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/unknown_field_set.h>
-#include "google/protobuf/source_context.pb.h"
-#include "google/protobuf/type.pb.h"
+#include <google/protobuf/source_context.pb.h>
+#include <google/protobuf/type.pb.h>
// @@protoc_insertion_point(includes)
namespace google {
diff --git a/src/google/protobuf/api.proto b/src/google/protobuf/api.proto
index 597a6497..dbe87b8f 100644
--- a/src/google/protobuf/api.proto
+++ b/src/google/protobuf/api.proto
@@ -27,6 +27,7 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
@@ -34,11 +35,11 @@ package google.protobuf;
import "google/protobuf/source_context.proto";
import "google/protobuf/type.proto";
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option java_package = "com.google.protobuf";
option java_outer_classname = "ApiProto";
option java_multiple_files = true;
option java_generate_equals_and_hash = true;
-option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option objc_class_prefix = "GPB";
// Api is a light-weight descriptor for a protocol buffer service.
@@ -75,6 +76,7 @@ message Api {
// be omitted. Zero major versions must only be used for
// experimental, none-GA apis.
//
+ //
string version = 4;
// Source context for the protocol buffer service represented by this
@@ -141,7 +143,6 @@ message Method {
//
// package google.storage.v2;
// service Storage {
-// // (-- see AccessControl.GetAcl --)
// rpc GetAcl(GetAclRequest) returns (Acl);
//
// // Get a data record.
diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h
index 16e0d50e..5ad94fa9 100644
--- a/src/google/protobuf/arena.h
+++ b/src/google/protobuf/arena.h
@@ -76,7 +76,7 @@ template<typename T> void arena_destruct_object(void* object) {
template<typename T> void arena_delete_object(void* object) {
delete reinterpret_cast<T*>(object);
}
-inline void arena_free(void* object, size_t size) {
+inline void arena_free(void* object, size_t /* size */) {
free(object);
}
@@ -490,27 +490,28 @@ class LIBPROTOBUF_EXPORT Arena {
return GetArenaInternal(value, static_cast<T*>(0));
}
- // Helper typetrait that indicates support for arenas in a type T at compile
- // time. This is public only to allow construction of higher-level templated
- // utilities. is_arena_constructable<T>::value is an instance of
- // google::protobuf::internal::true_type if the message type T has arena support enabled, and
- // google::protobuf::internal::false_type otherwise.
- //
- // This is inside Arena because only Arena has the friend relationships
- // necessary to see the underlying generated code traits.
- template<typename T>
- struct is_arena_constructable {
+ private:
+ struct InternalIsArenaConstructableHelper {
template<typename U>
static char ArenaConstructable(
const typename U::InternalArenaConstructable_*);
template<typename U>
static double ArenaConstructable(...);
+ };
- // This will resolve to either google::protobuf::internal::true_type or google::protobuf::internal::false_type.
- typedef google::protobuf::internal::integral_constant<bool,
- sizeof(ArenaConstructable<const T>(static_cast<const T*>(0))) ==
- sizeof(char)> type;
- static const type value;
+ public:
+ // Helper typetrait that indicates support for arenas in a type T at compile
+ // time. This is public only to allow construction of higher-level templated
+ // utilities. is_arena_constructable<T>::value is true if the message type T
+ // has arena support enabled, and false otherwise.
+ //
+ // This is inside Arena because only Arena has the friend relationships
+ // necessary to see the underlying generated code traits.
+ template<typename T>
+ struct is_arena_constructable :
+ public google::protobuf::internal::integral_constant<bool,
+ sizeof(InternalIsArenaConstructableHelper::ArenaConstructable<
+ const T>(static_cast<const T*>(0))) == sizeof(char)> {
};
private:
@@ -572,34 +573,30 @@ class LIBPROTOBUF_EXPORT Arena {
return google::protobuf::internal::has_trivial_destructor<T>::value;
}
- // Helper typetrait that indicates whether the desctructor of type T should be
- // called when arena is destroyed at compile time. This is only to allow
- // construction of higher-level templated utilities.
- // is_destructor_skippable<T>::value is an instance of google::protobuf::internal::true_type if the
- // destructor of the message type T should not be called when arena is
- // destroyed or google::protobuf::internal::has_trivial_destructor<T>::value == true, and
- // google::protobuf::internal::false_type otherwise.
- //
- // This is inside Arena because only Arena has the friend relationships
- // necessary to see the underlying generated code traits.
- template<typename T>
- struct is_destructor_skippable {
+ private:
+ struct InternalIsDestructorSkippableHelper {
template<typename U>
static char DestructorSkippable(
const typename U::DestructorSkippable_*);
template<typename U>
static double DestructorSkippable(...);
-
- // The raw_skippable_value const bool variable is separated from the typedef
- // line below as a work-around of an NVCC 7.0 (and earlier) compiler bug.
- static const bool raw_skippable_value =
- sizeof(DestructorSkippable<const T>(static_cast<const T*>(0))) ==
- sizeof(char) || google::protobuf::internal::has_trivial_destructor<T>::value == true;
- // This will resolve to either google::protobuf::internal::true_type or google::protobuf::internal::false_type.
- typedef google::protobuf::internal::integral_constant<bool, raw_skippable_value> type;
- static const type value;
};
+ public:
+ // Helper typetrait that indicates whether the desctructor of type T should be
+ // called when arena is destroyed at compile time. This is only to allow
+ // construction of higher-level templated utilities.
+ // is_destructor_skippable<T>::value is true if the destructor of the message
+ // type T should not be called when arena is destroyed or false otherwise.
+ // This is inside Arena because only Arena has the friend relationships
+ // necessary to see the underlying generated code traits.
+ template<typename T>
+ struct is_destructor_skippable
+ : public google::protobuf::internal::integral_constant<
+ bool,
+ sizeof(InternalIsDestructorSkippableHelper::DestructorSkippable<
+ const T>(static_cast<const T*>(0))) == sizeof(char) ||
+ google::protobuf::internal::has_trivial_destructor<T>::value> {};
// CreateMessage<T> requires that T supports arenas, but this private method
// works whether or not T supports arenas. These are not exposed to user code
@@ -780,8 +777,10 @@ class LIBPROTOBUF_EXPORT Arena {
// which needs to declare google::protobuf::Map as friend of generated message.
template <typename T>
static void CreateInArenaStorage(T* ptr, Arena* arena) {
- CreateInArenaStorageInternal(ptr, arena, is_arena_constructable<T>::value);
- RegisterDestructorInternal(ptr, arena, is_destructor_skippable<T>::value);
+ CreateInArenaStorageInternal(ptr, arena,
+ typename is_arena_constructable<T>::type());
+ RegisterDestructorInternal(ptr, arena,
+ typename is_destructor_skippable<T>::type());
}
template <typename T>
@@ -910,16 +909,6 @@ class LIBPROTOBUF_EXPORT Arena {
// Defined above for supporting environments without RTTI.
#undef RTTI_TYPE_ID
-template<typename T>
-const typename Arena::is_arena_constructable<T>::type
- Arena::is_arena_constructable<T>::value =
- typename Arena::is_arena_constructable<T>::type();
-
-template<typename T>
-const typename Arena::is_destructor_skippable<T>::type
- Arena::is_destructor_skippable<T>::value =
- typename Arena::is_destructor_skippable<T>::type();
-
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/arenastring.h b/src/google/protobuf/arenastring.h
index 1dacdc68..e2e2f254 100755
--- a/src/google/protobuf/arenastring.h
+++ b/src/google/protobuf/arenastring.h
@@ -36,7 +36,6 @@
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/fastmem.h>
-
#include <google/protobuf/arena.h>
#include <google/protobuf/generated_message_util.h>
@@ -65,7 +64,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
}
// Basic accessors.
- inline const ::std::string& Get(const ::std::string* default_value) const {
+ inline const ::std::string& Get(const ::std::string* /* default_value */) const {
return *ptr_;
}
@@ -103,7 +102,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
// state. Used to implement unsafe_arena_release_<field>() methods on
// generated classes.
inline ::std::string* UnsafeArenaRelease(const ::std::string* default_value,
- ::google::protobuf::Arena* arena) {
+ ::google::protobuf::Arena* /* arena */) {
if (ptr_ == default_value) {
return NULL;
}
@@ -135,7 +134,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
// UnsafeArenaRelease() on another field of a message in the same arena. Used
// to implement unsafe_arena_set_allocated_<field> in generated classes.
inline void UnsafeArenaSetAllocated(const ::std::string* default_value,
- ::std::string* value, ::google::protobuf::Arena* arena) {
+ ::std::string* value, ::google::protobuf::Arena* /* arena */) {
if (value != NULL) {
ptr_ = value;
} else {
@@ -164,7 +163,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
// the user) will always be the empty string. Assumes that |default_value|
// is an empty string.
inline void ClearToEmpty(const ::std::string* default_value,
- ::google::protobuf::Arena* arena) {
+ ::google::protobuf::Arena* /* arena */) {
if (ptr_ == default_value) {
// Already set to default (which is empty) -- do nothing.
} else {
@@ -176,7 +175,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
// overhead of heap operations. After this returns, the content (as seen by
// the user) will always be equal to |default_value|.
inline void ClearToDefault(const ::std::string* default_value,
- ::google::protobuf::Arena* arena) {
+ ::google::protobuf::Arena* /* arena */) {
if (ptr_ == default_value) {
// Already set to default -- do nothing.
} else {
@@ -216,7 +215,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
void AssignWithDefault(const ::std::string* default_value, ArenaStringPtr value);
- inline const ::std::string& GetNoArena(const ::std::string* default_value) const {
+ inline const ::std::string& GetNoArena(const ::std::string* /* default_value */) const {
return *ptr_;
}
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index 26a4f0b0..3a816b05 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -33,6 +33,7 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/command_line_interface.h>
+#include <google/protobuf/stubs/platform_macros.h>
#include <stdio.h>
#include <sys/types.h>
@@ -48,6 +49,10 @@
#include <iostream>
#include <ctype.h>
+#ifdef GOOGLE_PROTOBUF_ARCH_SPARC
+#include <limits.h> //For PATH_MAX
+#endif
+
#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
@@ -271,15 +276,35 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
// implements MultiFileErrorCollector ------------------------------
void AddError(const string& filename, int line, int column,
const string& message) {
+ AddErrorOrWarning(filename, line, column, message, "error", std::cerr);
+ }
+
+ void AddWarning(const string& filename, int line, int column,
+ const string& message) {
+ AddErrorOrWarning(filename, line, column, message, "warning", std::clog);
+ }
+
+ // implements io::ErrorCollector -----------------------------------
+ void AddError(int line, int column, const string& message) {
+ AddError("input", line, column, message);
+ }
+
+ void AddWarning(int line, int column, const string& message) {
+ AddErrorOrWarning("input", line, column, message, "warning", std::clog);
+ }
+ private:
+ void AddErrorOrWarning(
+ const string& filename, int line, int column,
+ const string& message, const string& type, ostream& out) {
// Print full path when running under MSVS
string dfile;
if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
tree_ != NULL &&
tree_->VirtualFileToDiskFile(filename, &dfile)) {
- std::cerr << dfile;
+ out << dfile;
} else {
- std::cerr << filename;
+ out << filename;
}
// Users typically expect 1-based line/column numbers, so we add 1
@@ -288,24 +313,22 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
// Allow for both GCC- and Visual-Studio-compatible output.
switch (format_) {
case CommandLineInterface::ERROR_FORMAT_GCC:
- std::cerr << ":" << (line + 1) << ":" << (column + 1);
+ out << ":" << (line + 1) << ":" << (column + 1);
break;
case CommandLineInterface::ERROR_FORMAT_MSVS:
- std::cerr << "(" << (line + 1)
- << ") : error in column=" << (column + 1);
+ out << "(" << (line + 1) << ") : "
+ << type << " in column=" << (column + 1);
break;
}
}
- std::cerr << ": " << message << std::endl;
- }
-
- // implements io::ErrorCollector -----------------------------------
- void AddError(int line, int column, const string& message) {
- AddError("input", line, column, message);
+ if (type == "warning") {
+ out << ": warning: " << message << std::endl;
+ } else {
+ out << ": " << message << std::endl;
+ }
}
- private:
const ErrorFormat format_;
DiskSourceTree *tree_;
};
@@ -1353,7 +1376,7 @@ void CommandLineInterface::PrintHelpText() {
" defined in the given proto files. Groups share\n"
" the same field number space with the parent \n"
" message. Extension ranges are counted as \n"
-" occupied fields numbers."
+" occupied fields numbers.\n"
<< std::endl;
if (!plugin_prefix_.empty()) {
std::cerr <<
@@ -1443,6 +1466,7 @@ bool CommandLineInterface::GenerateDependencyManifestFile(
for (int i = 0; i < parsed_files.size(); i++) {
GetTransitiveDependencies(parsed_files[i],
false,
+ false,
&already_seen,
file_set.mutable_file());
}
@@ -1522,6 +1546,7 @@ bool CommandLineInterface::GeneratePluginOutput(
for (int i = 0; i < parsed_files.size(); i++) {
request.add_file_to_generate(parsed_files[i]->name());
GetTransitiveDependencies(parsed_files[i],
+ true, // Include json_name for plugins.
true, // Include source code info.
&already_seen, request.mutable_proto_file());
}
@@ -1654,6 +1679,7 @@ bool CommandLineInterface::WriteDescriptorSet(
set<const FileDescriptor*> already_seen;
for (int i = 0; i < parsed_files.size(); i++) {
GetTransitiveDependencies(parsed_files[i],
+ true, // Include json_name
source_info_in_descriptor_set_,
&already_seen, file_set.mutable_file());
}
@@ -1665,6 +1691,7 @@ bool CommandLineInterface::WriteDescriptorSet(
}
FileDescriptorProto* file_proto = file_set.add_file();
parsed_files[i]->CopyTo(file_proto);
+ parsed_files[i]->CopyJsonNameTo(file_proto);
if (source_info_in_descriptor_set_) {
parsed_files[i]->CopySourceCodeInfoTo(file_proto);
}
@@ -1699,7 +1726,9 @@ bool CommandLineInterface::WriteDescriptorSet(
}
void CommandLineInterface::GetTransitiveDependencies(
- const FileDescriptor* file, bool include_source_code_info,
+ const FileDescriptor* file,
+ bool include_json_name,
+ bool include_source_code_info,
set<const FileDescriptor*>* already_seen,
RepeatedPtrField<FileDescriptorProto>* output) {
if (!already_seen->insert(file).second) {
@@ -1709,13 +1738,18 @@ void CommandLineInterface::GetTransitiveDependencies(
// Add all dependencies.
for (int i = 0; i < file->dependency_count(); i++) {
- GetTransitiveDependencies(file->dependency(i), include_source_code_info,
+ GetTransitiveDependencies(file->dependency(i),
+ include_json_name,
+ include_source_code_info,
already_seen, output);
}
// Add this file.
FileDescriptorProto* new_descriptor = output->Add();
file->CopyTo(new_descriptor);
+ if (include_json_name) {
+ file->CopyJsonNameTo(new_descriptor);
+ }
if (include_source_code_info) {
file->CopySourceCodeInfoTo(new_descriptor);
}
diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h
index 7e611c44..f196ffc5 100644
--- a/src/google/protobuf/compiler/command_line_interface.h
+++ b/src/google/protobuf/compiler/command_line_interface.h
@@ -262,8 +262,11 @@ class LIBPROTOC_EXPORT CommandLineInterface {
// in order. Any files in *already_seen will not be added, and each file
// added will be inserted into *already_seen. If include_source_code_info is
// true then include the source code information in the FileDescriptorProtos.
+ // If include_json_name is true, populate the json_name field of
+ // FieldDescriptorProto for all fields.
static void GetTransitiveDependencies(
const FileDescriptor* file,
+ bool include_json_name,
bool include_source_code_info,
set<const FileDescriptor*>* already_seen,
RepeatedPtrField<FileDescriptorProto>* output);
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index fa792dae..dda007d4 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -63,13 +63,13 @@
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
+namespace google {
+namespace protobuf {
+namespace compiler {
// Disable the whole test when we use tcmalloc for "draconian" heap checks, in
// which case tcmalloc will print warnings that fail the plugin tests.
#if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
-namespace google {
-namespace protobuf {
-namespace compiler {
#if defined(_WIN32)
#ifndef STDIN_FILENO
@@ -886,6 +886,10 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
// Descriptor set should not have source code info.
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
+ // Descriptor set should have json_name.
+ EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
+ EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
+ EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
}
TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
@@ -919,6 +923,10 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
// Descriptor set should not have source code info.
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
+ // Descriptor set should have json_name.
+ EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
+ EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
+ EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
}
TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
@@ -1413,6 +1421,18 @@ TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
"Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
}
+TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
+ CreateTempFile("foo.proto",
+ "syntax = \"proto2\";\n"
+ "message MockCodeGenerator_HasJsonName {\n"
+ " optional int32 value = 1;\n"
+ "}\n");
+
+ Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
+
+ ExpectErrorSubstring("Saw json_name: 1");
+}
+
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
// Test what happens if the plugin isn't found.
@@ -1802,8 +1822,8 @@ TEST_F(EncodeDecodeTest, ProtoParseError) {
} // anonymous namespace
+#endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
+
} // namespace compiler
} // namespace protobuf
-
-#endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
} // namespace google
diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc
index 4906985d..37e4bae4 100644
--- a/src/google/protobuf/compiler/cpp/cpp_file.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_file.cc
@@ -237,6 +237,7 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer) {
}
void FileGenerator::GenerateSource(io::Printer* printer) {
+ bool well_known = IsWellKnownMessage(file_);
string header =
StripProto(file_->name()) + (options_.proto_h ? ".proto.h" : ".pb.h");
printer->Print(
@@ -246,16 +247,19 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
// The generated code calls accessors that might be deprecated. We don't
// want the compiler to warn in generated code.
"#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n"
- "#include \"$header$\"\n"
+ "#include $left$$header$$right$\n"
"\n"
"#include <algorithm>\n" // for swap()
"\n"
"#include <google/protobuf/stubs/common.h>\n"
+ "#include <google/protobuf/stubs/port.h>\n"
"#include <google/protobuf/stubs/once.h>\n"
"#include <google/protobuf/io/coded_stream.h>\n"
"#include <google/protobuf/wire_format_lite_inl.h>\n",
"filename", file_->name(),
- "header", header);
+ "header", header,
+ "left", well_known ? "<" : "\"",
+ "right", well_known ? ">" : "\"");
// Unknown fields implementation in lite mode uses StringOutputStream
if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) {
@@ -857,14 +861,16 @@ void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) {
}
for (int i = 0; i < file_->dependency_count(); i++) {
+ bool well_known = IsWellKnownMessage(file_->dependency(i));
const string& name = file_->dependency(i)->name();
bool public_import = (public_import_names.count(name) != 0);
-
printer->Print(
- "#include \"$dependency$.pb.h\"$iwyu$\n",
+ "#include $left$$dependency$.pb.h$right$$iwyu$\n",
"dependency", StripProto(name),
- "iwyu", (public_import) ? " // IWYU pragma: export" : "");
+ "iwyu", (public_import) ? " // IWYU pragma: export" : "",
+ "left", well_known ? "<" : "\"",
+ "right", well_known ? ">" : "\"");
}
}
diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc
index 09845458..fb46e387 100644
--- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc
@@ -54,6 +54,7 @@ namespace {
static const char kAnyMessageName[] = "Any";
static const char kAnyProtoFile[] = "google/protobuf/any.proto";
+static const char kGoogleProtobufPrefix[] = "google/protobuf/";
string DotsToUnderscores(const string& name) {
return StringReplace(name, ".", "_", true);
@@ -600,6 +601,10 @@ bool IsAnyMessage(const Descriptor* descriptor) {
descriptor->file()->name() == kAnyProtoFile;
}
+bool IsWellKnownMessage(const FileDescriptor* descriptor) {
+ return !descriptor->name().compare(0, 16, kGoogleProtobufPrefix);
+}
+
enum Utf8CheckMode {
STRICT = 0, // Parsing will fail if non UTF-8 data is in string fields.
VERIFY = 1, // Only log an error but parsing will succeed.
diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h
index 985cb04c..a22d414d 100644
--- a/src/google/protobuf/compiler/cpp/cpp_helpers.h
+++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h
@@ -265,6 +265,8 @@ inline bool SupportsArenas(const FieldDescriptor* field) {
bool IsAnyMessage(const FileDescriptor* descriptor);
bool IsAnyMessage(const Descriptor* descriptor);
+bool IsWellKnownMessage(const FileDescriptor* descriptor);
+
void GenerateUtf8CheckCodeForString(
const FieldDescriptor* field,
bool for_parse,
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc
index 8c0bfab7..8304ebbd 100644
--- a/src/google/protobuf/compiler/cpp/cpp_message.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_message.cc
@@ -1772,6 +1772,17 @@ GenerateShutdownCode(io::Printer* printer) {
void MessageGenerator::
GenerateClassMethods(io::Printer* printer) {
+ // mutable_unknown_fields wrapper function for LazyStringOutputStream
+ // callback.
+ if (!UseUnknownFieldSet(descriptor_->file())) {
+ printer->Print(
+ "static ::std::string* MutableUnknownFieldsFor$classname$(\n"
+ " $classname$* ptr) {\n"
+ " return ptr->mutable_unknown_fields();\n"
+ "}\n"
+ "\n",
+ "classname", classname_);
+ }
if (IsAnyMessage(descriptor_)) {
printer->Print(
"void $classname$::PackFrom(const ::google::protobuf::Message& message) {\n"
@@ -2814,7 +2825,9 @@ GenerateMergeFrom(io::Printer* printer) {
"}\n");
} else {
printer->Print(
- "mutable_unknown_fields()->append(from.unknown_fields());\n");
+ "if (!from.unknown_fields().empty()) {\n"
+ " mutable_unknown_fields()->append(from.unknown_fields());\n"
+ "}\n");
}
}
@@ -2889,11 +2902,16 @@ GenerateMergeFromCodedStream(io::Printer* printer) {
"classname", classname_);
if (!UseUnknownFieldSet(descriptor_->file())) {
+ // Use LazyStringOutputString to avoid initializing unknown fields string
+ // unless it is actually needed. For the same reason, disable eager refresh
+ // on the CodedOutputStream.
printer->Print(
- " ::google::protobuf::io::StringOutputStream unknown_fields_string(\n"
- " mutable_unknown_fields());\n"
+ " ::google::protobuf::io::LazyStringOutputStream unknown_fields_string(\n"
+ " ::google::protobuf::internal::NewPermanentCallback(\n"
+ " &MutableUnknownFieldsFor$classname$, this));\n"
" ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n"
- " &unknown_fields_string);\n");
+ " &unknown_fields_string, false);\n",
+ "classname", classname_);
}
printer->Print(
@@ -3364,7 +3382,7 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) {
} else {
printer->Print(
"output->WriteRaw(unknown_fields().data(),\n"
- " unknown_fields().size());\n");
+ " static_cast<int>(unknown_fields().size()));\n");
}
}
}
diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc b/src/google/protobuf/compiler/cpp/metadata_test.cc
index e5db844c..422eb73b 100644
--- a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc
+++ b/src/google/protobuf/compiler/cpp/metadata_test.cc
@@ -28,30 +28,31 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include <google/protobuf/util/internal/snake2camel_objectwriter.h>
-#include <google/protobuf/util/internal/expecting_objectwriter.h>
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/compiler/cpp/cpp_helpers.h>
+#include <google/protobuf/compiler/cpp/cpp_generator.h>
+#include <google/protobuf/compiler/command_line_interface.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/descriptor.pb.h>
+
+#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
+#include <google/protobuf/testing/file.h>
namespace google {
namespace protobuf {
-namespace util {
-namespace converter {
-
-class Snake2CamelObjectWriterTest : public ::testing::Test {
- protected:
- Snake2CamelObjectWriterTest() : mock_(), expects_(&mock_), testing_(&mock_) {}
- virtual ~Snake2CamelObjectWriterTest() {}
-
- MockObjectWriter mock_;
- ExpectingObjectWriter expects_;
- Snake2CamelObjectWriter testing_;
-};
-
-// All tests are deleted as they are no longer needed. This file will be removed
-// after the component dependecies are cleaned up.
-// TODO(skarvaje): Remove this file.
+namespace compiler {
+namespace cpp {
+namespace {
-} // namespace converter
-} // namespace util
+} // namespace
+} // namespace cpp
+} // namespace compiler
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc
index b493495d..15c68b3f 100644
--- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc
@@ -62,7 +62,6 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
descriptor_->message_type()->FindFieldByName("value");
variables_["key_type_name"] = type_name(key_descriptor);
variables_["value_type_name"] = type_name(value_descriptor);
- variables_["true_for_wrappers"] = IsWrapperType(value_descriptor) ? "true" : "";
scoped_ptr<FieldGeneratorBase> key_generator(CreateFieldGenerator(key_descriptor, 1));
scoped_ptr<FieldGeneratorBase> value_generator(CreateFieldGenerator(value_descriptor, 2));
@@ -76,7 +75,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
", $tag$);\n"
- "private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>($true_for_wrappers$);\n");
+ "private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>();\n");
WritePropertyDocComment(printer, descriptor_);
AddDeprecatedFlag(printer);
printer->Print(
diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc
index 9c72043b..e0230a24 100644
--- a/src/google/protobuf/compiler/csharp/csharp_message.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_message.cc
@@ -353,7 +353,7 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
printer->Print(
"public override string ToString() {\n"
- " return pb::JsonFormatter.Default.Format(this);\n"
+ " return pb::JsonFormatter.ToDiagnosticString(this);\n"
"}\n\n");
}
diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
index 76d5b247..60afd892 100644
--- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
@@ -83,7 +83,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
} else {
printer->Print(
variables_,
- " $name$_ = pb::Preconditions.CheckNotNull(value, \"value\");\n");
+ " $name$_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\");\n");
}
printer->Print(
" }\n"
@@ -186,7 +186,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
} else {
printer->Print(
variables_,
- " $oneof_name$_ = pb::Preconditions.CheckNotNull(value, \"value\");\n");
+ " $oneof_name$_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\");\n");
}
printer->Print(
variables_,
diff --git a/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc b/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc
index 7bed02ff..22dae43b 100644
--- a/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc
@@ -166,7 +166,7 @@ void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
// -----------------------------------------------------------------
// Invoke InternalBuildGeneratedFileFrom() to build the file.
printer->Print(
- "descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n");
+ "descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,\n");
printer->Print(" new pbr::FileDescriptor[] { ");
for (int i = 0; i < file_->dependency_count(); i++) {
// descriptor.proto is special: we don't allow access to the generated code, but there's
@@ -182,7 +182,7 @@ void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
}
}
printer->Print("},\n"
- " new pbr::GeneratedCodeInfo(");
+ " new pbr::GeneratedClrTypeInfo(");
// Specify all the generated code information, recursively.
if (file_->enum_type_count() > 0) {
printer->Print("new[] {");
@@ -195,7 +195,7 @@ void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
printer->Print("null, ");
}
if (file_->message_type_count() > 0) {
- printer->Print("new pbr::GeneratedCodeInfo[] {\n");
+ printer->Print("new pbr::GeneratedClrTypeInfo[] {\n");
printer->Indent();
printer->Indent();
printer->Indent();
@@ -232,7 +232,7 @@ void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descript
return;
}
// Generated message type
- printer->Print("new pbr::GeneratedCodeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor));
+ printer->Print("new pbr::GeneratedClrTypeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor));
// Fields
if (descriptor->field_count() > 0) {
@@ -273,7 +273,7 @@ void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descript
// Nested types
if (descriptor->nested_type_count() > 0) {
// Need to specify array type explicitly here, as all elements may be null.
- printer->Print("new pbr::GeneratedCodeInfo[] { ");
+ printer->Print("new pbr::GeneratedClrTypeInfo[] { ");
for (int i = 0; i < descriptor->nested_type_count(); i++) {
WriteGeneratedCodeInfo(descriptor->nested_type(i), printer, i == descriptor->nested_type_count() - 1);
}
diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc
index 8333684e..0d9093c0 100644
--- a/src/google/protobuf/compiler/importer.cc
+++ b/src/google/protobuf/compiler/importer.cc
@@ -185,6 +185,19 @@ void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
owner_->error_collector_->AddError(filename, line, column, message);
}
+void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddWarning(
+ 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_->AddWarning(filename, line, column, message);
+}
+
// ===================================================================
Importer::Importer(SourceTree* source_tree,
diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h
index f010fd08..cc8fcc39 100644
--- a/src/google/protobuf/compiler/importer.h
+++ b/src/google/protobuf/compiler/importer.h
@@ -121,6 +121,12 @@ class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabas
ErrorLocation location,
const string& message);
+ virtual void AddWarning(const string& filename,
+ const string& element_name,
+ const Message* descriptor,
+ ErrorLocation location,
+ const string& message);
+
private:
SourceTreeDescriptorDatabase* owner_;
};
@@ -188,6 +194,9 @@ class LIBPROTOBUF_EXPORT MultiFileErrorCollector {
virtual void AddError(const string& filename, int line, int column,
const string& message) = 0;
+ virtual void AddWarning(const string& filename, int line, int column,
+ const string& message) {}
+
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector);
};
diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc
index 33c9328f..be19aa2e 100644
--- a/src/google/protobuf/compiler/importer_unittest.cc
+++ b/src/google/protobuf/compiler/importer_unittest.cc
@@ -71,6 +71,7 @@ class MockErrorCollector : public MultiFileErrorCollector {
~MockErrorCollector() {}
string text_;
+ string warning_text_;
// implements ErrorCollector ---------------------------------------
void AddError(const string& filename, int line, int column,
@@ -78,6 +79,12 @@ class MockErrorCollector : public MultiFileErrorCollector {
strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n",
filename, line, column, message);
}
+
+ void AddWarning(const string& filename, int line, int column,
+ const string& message) {
+ strings::SubstituteAndAppend(&warning_text_, "$0:$1:$2: $3\n",
+ filename, line, column, message);
+ }
};
// -------------------------------------------------------------------
@@ -123,6 +130,7 @@ class ImporterTest : public testing::Test {
// Return the collected error text
string error() const { return error_collector_.text_; }
+ string warning() const { return error_collector_.warning_text_; }
MockErrorCollector error_collector_;
MockSourceTree source_tree_;
diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc
index 8a09f3a8..5fc9b002 100644
--- a/src/google/protobuf/compiler/java/java_enum.cc
+++ b/src/google/protobuf/compiler/java/java_enum.cc
@@ -85,17 +85,10 @@ EnumGenerator::~EnumGenerator() {}
void EnumGenerator::Generate(io::Printer* printer) {
WriteEnumDocComment(printer, descriptor_);
- if (HasDescriptorMethods(descriptor_)) {
- printer->Print(
- "public enum $classname$\n"
- " implements com.google.protobuf.ProtocolMessageEnum {\n",
- "classname", descriptor_->name());
- } else {
- printer->Print(
- "public enum $classname$\n"
- " implements com.google.protobuf.Internal.EnumLite {\n",
- "classname", descriptor_->name());
- }
+ printer->Print(
+ "public enum $classname$\n"
+ " implements com.google.protobuf.ProtocolMessageEnum {\n",
+ "classname", descriptor_->name());
printer->Indent();
for (int i = 0; i < canonical_values_.size(); i++) {
@@ -311,7 +304,6 @@ void EnumGenerator::Generate(io::Printer* printer) {
"}\n"
"\n");
- // index is only used for reflection; lite implementation does not need it
printer->Print("private final int index;\n");
}
diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc
index e8bf15d0..e3e87c58 100644
--- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc
+++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc
@@ -596,9 +596,7 @@ GenerateInterfaceMembers(io::Printer* printer) const {
void RepeatedImmutableEnumFieldLiteGenerator::
GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
- // TODO(dweis): Switch to IntList?
- "private com.google.protobuf.Internal.ProtobufList<\n"
- " java.lang.Integer> $name$_;\n"
+ "private com.google.protobuf.Internal.IntList $name$_;\n"
"private static final com.google.protobuf.Internal.ListAdapter.Converter<\n"
" java.lang.Integer, $type$> $name$_converter_ =\n"
" new com.google.protobuf.Internal.ListAdapter.Converter<\n"
@@ -623,7 +621,7 @@ GenerateMembers(io::Printer* printer) const {
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
"$deprecation$public $type$ get$capitalized_name$(int index) {\n"
- " return $name$_converter_.convert($name$_.get(index));\n"
+ " return $name$_converter_.convert($name$_.getInt(index));\n"
"}\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
WriteFieldDocComment(printer, descriptor_);
@@ -635,7 +633,7 @@ GenerateMembers(io::Printer* printer) const {
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
"$deprecation$public int get$capitalized_name$Value(int index) {\n"
- " return $name$_.get(index);\n"
+ " return $name$_.getInt(index);\n"
"}\n");
}
@@ -649,7 +647,7 @@ GenerateMembers(io::Printer* printer) const {
printer->Print(variables_,
"private void ensure$capitalized_name$IsMutable() {\n"
" if (!$is_mutable$) {\n"
- " $name$_ = newProtobufList($name$_);\n"
+ " $name$_ = newIntList($name$_);\n"
" }\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
@@ -660,7 +658,7 @@ GenerateMembers(io::Printer* printer) const {
" throw new NullPointerException();\n"
" }\n"
" ensure$capitalized_name$IsMutable();\n"
- " $name$_.set(index, value.getNumber());\n"
+ " $name$_.setInt(index, value.getNumber());\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
@@ -669,7 +667,7 @@ GenerateMembers(io::Printer* printer) const {
" throw new NullPointerException();\n"
" }\n"
" ensure$capitalized_name$IsMutable();\n"
- " $name$_.add(value.getNumber());\n"
+ " $name$_.addInt(value.getNumber());\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
@@ -677,13 +675,13 @@ GenerateMembers(io::Printer* printer) const {
" java.lang.Iterable<? extends $type$> values) {\n"
" ensure$capitalized_name$IsMutable();\n"
" for ($type$ value : values) {\n"
- " $name$_.add(value.getNumber());\n"
+ " $name$_.addInt(value.getNumber());\n"
" }\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
"private void clear$capitalized_name$() {\n"
- " $name$_ = emptyProtobufList();\n"
+ " $name$_ = emptyIntList();\n"
"}\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
@@ -692,13 +690,13 @@ GenerateMembers(io::Printer* printer) const {
"private void set$capitalized_name$Value(\n"
" int index, int value) {\n"
" ensure$capitalized_name$IsMutable();\n"
- " $name$_.set(index, value);\n"
+ " $name$_.setInt(index, value);\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
"private void add$capitalized_name$Value(int value) {\n"
" ensure$capitalized_name$IsMutable();\n"
- " $name$_.add(value);\n"
+ " $name$_.addInt(value);\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
@@ -706,7 +704,7 @@ GenerateMembers(io::Printer* printer) const {
" java.lang.Iterable<java.lang.Integer> values) {\n"
" ensure$capitalized_name$IsMutable();\n"
" for (int value : values) {\n"
- " $name$_.add(value);\n"
+ " $name$_.addInt(value);\n"
" }\n"
"}\n");
}
@@ -805,7 +803,7 @@ GenerateFieldBuilderInitializationCode(io::Printer* printer) const {
void RepeatedImmutableEnumFieldLiteGenerator::
GenerateInitializationCode(io::Printer* printer) const {
- printer->Print(variables_, "$name$_ = emptyProtobufList();\n");
+ printer->Print(variables_, "$name$_ = emptyIntList();\n");
}
void RepeatedImmutableEnumFieldLiteGenerator::
@@ -840,9 +838,9 @@ GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
"int rawValue = input.readEnum();\n"
"if (!$is_mutable$) {\n"
- " $name$_ = newProtobufList();\n"
+ " $name$_ = newIntList();\n"
"}\n"
- "$name$_.add(rawValue);\n");
+ "$name$_.addInt(rawValue);\n");
} else {
printer->Print(variables_,
"int rawValue = input.readEnum();\n"
@@ -855,9 +853,9 @@ GenerateParsingCode(io::Printer* printer) const {
printer->Print(variables_,
"} else {\n"
" if (!$is_mutable$) {\n"
- " $name$_ = newProtobufList();\n"
+ " $name$_ = newIntList();\n"
" }\n"
- " $name$_.add(rawValue);\n"
+ " $name$_.addInt(rawValue);\n"
"}\n");
}
}
@@ -897,12 +895,12 @@ GenerateSerializationCode(io::Printer* printer) const {
" output.writeRawVarint32($name$MemoizedSerializedSize);\n"
"}\n"
"for (int i = 0; i < $name$_.size(); i++) {\n"
- " output.writeEnumNoTag($name$_.get(i));\n"
+ " output.writeEnumNoTag($name$_.getInt(i));\n"
"}\n");
} else {
printer->Print(variables_,
"for (int i = 0; i < $name$_.size(); i++) {\n"
- " output.writeEnum($number$, $name$_.get(i));\n"
+ " output.writeEnum($number$, $name$_.getInt(i));\n"
"}\n");
}
}
@@ -917,7 +915,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
"for (int i = 0; i < $name$_.size(); i++) {\n"
" dataSize += com.google.protobuf.CodedOutputStream\n"
- " .computeEnumSizeNoTag($name$_.get(i));\n"
+ " .computeEnumSizeNoTag($name$_.getInt(i));\n"
"}\n");
printer->Print(
"size += dataSize;\n");
diff --git a/src/google/protobuf/compiler/java/java_enum_lite.cc b/src/google/protobuf/compiler/java/java_enum_lite.cc
index 62186386..ed415eee 100644
--- a/src/google/protobuf/compiler/java/java_enum_lite.cc
+++ b/src/google/protobuf/compiler/java/java_enum_lite.cc
@@ -85,34 +85,26 @@ EnumLiteGenerator::~EnumLiteGenerator() {}
void EnumLiteGenerator::Generate(io::Printer* printer) {
WriteEnumDocComment(printer, descriptor_);
- if (HasDescriptorMethods(descriptor_)) {
- printer->Print(
- "public enum $classname$\n"
- " implements com.google.protobuf.ProtocolMessageEnum {\n",
- "classname", descriptor_->name());
- } else {
- printer->Print(
- "public enum $classname$\n"
- " implements com.google.protobuf.Internal.EnumLite {\n",
- "classname", descriptor_->name());
- }
+ printer->Print(
+ "public enum $classname$\n"
+ " implements com.google.protobuf.Internal.EnumLite {\n",
+ "classname", descriptor_->name());
printer->Indent();
for (int i = 0; i < canonical_values_.size(); i++) {
map<string, string> vars;
vars["name"] = canonical_values_[i]->name();
- vars["index"] = SimpleItoa(canonical_values_[i]->index());
vars["number"] = SimpleItoa(canonical_values_[i]->number());
WriteEnumValueDocComment(printer, canonical_values_[i]);
if (canonical_values_[i]->options().deprecated()) {
printer->Print("@java.lang.Deprecated\n");
}
printer->Print(vars,
- "$name$($index$, $number$),\n");
+ "$name$($number$),\n");
}
if (SupportUnknownEnumValue(descriptor_->file())) {
- printer->Print("UNRECOGNIZED(-1, -1),\n");
+ printer->Print("UNRECOGNIZED(-1),\n");
}
printer->Print(
@@ -145,15 +137,7 @@ void EnumLiteGenerator::Generate(io::Printer* printer) {
printer->Print(
"\n"
- "public final int getNumber() {\n");
- if (SupportUnknownEnumValue(descriptor_->file())) {
- printer->Print(
- " if (index == -1) {\n"
- " throw new java.lang.IllegalArgumentException(\n"
- " \"Can't get the number of an unknown enum value.\");\n"
- " }\n");
- }
- printer->Print(
+ "public final int getNumber() {\n"
" return value;\n"
"}\n"
"\n"
@@ -193,7 +177,7 @@ void EnumLiteGenerator::Generate(io::Printer* printer) {
printer->Print(
"private final int value;\n\n"
- "private $classname$(int index, int value) {\n",
+ "private $classname$(int value) {\n",
"classname", descriptor_->name());
printer->Print(
" this.value = value;\n"
diff --git a/src/google/protobuf/compiler/java/java_extension_lite.cc b/src/google/protobuf/compiler/java/java_extension_lite.cc
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_extension_lite.cc
diff --git a/src/google/protobuf/compiler/java/java_extension_lite.h b/src/google/protobuf/compiler/java/java_extension_lite.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/google/protobuf/compiler/java/java_extension_lite.h
diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc
index 68b47ee1..c8172330 100644
--- a/src/google/protobuf/compiler/java/java_file.cc
+++ b/src/google/protobuf/compiler/java/java_file.cc
@@ -42,6 +42,7 @@
#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_enum.h>
+#include <google/protobuf/compiler/java/java_enum_lite.h>
#include <google/protobuf/compiler/java/java_extension.h>
#include <google/protobuf/compiler/java/java_generator_factory.h>
#include <google/protobuf/compiler/java/java_helpers.h>
@@ -281,8 +282,13 @@ void FileGenerator::Generate(io::Printer* printer) {
if (!MultipleJavaFiles(file_, immutable_api_)) {
for (int i = 0; i < file_->enum_type_count(); i++) {
- EnumGenerator(file_->enum_type(i), immutable_api_, context_.get())
- .Generate(printer);
+ if (HasDescriptorMethods(file_)) {
+ EnumGenerator(file_->enum_type(i), immutable_api_, context_.get())
+ .Generate(printer);
+ } else {
+ EnumLiteGenerator(file_->enum_type(i), immutable_api_, context_.get())
+ .Generate(printer);
+ }
}
for (int i = 0; i < file_->message_type_count(); i++) {
message_generators_[i]->GenerateInterface(printer);
@@ -543,13 +549,23 @@ void FileGenerator::GenerateSiblings(const string& package_dir,
vector<string>* file_list) {
if (MultipleJavaFiles(file_, immutable_api_)) {
for (int i = 0; i < file_->enum_type_count(); i++) {
- EnumGenerator generator(file_->enum_type(i), immutable_api_,
- context_.get());
- GenerateSibling<EnumGenerator>(package_dir, java_package_,
- file_->enum_type(i),
- context, file_list, "",
- &generator,
- &EnumGenerator::Generate);
+ if (HasDescriptorMethods(file_)) {
+ EnumGenerator generator(file_->enum_type(i), immutable_api_,
+ context_.get());
+ GenerateSibling<EnumGenerator>(package_dir, java_package_,
+ file_->enum_type(i),
+ context, file_list, "",
+ &generator,
+ &EnumGenerator::Generate);
+ } else {
+ EnumLiteGenerator generator(file_->enum_type(i), immutable_api_,
+ context_.get());
+ GenerateSibling<EnumLiteGenerator>(package_dir, java_package_,
+ file_->enum_type(i),
+ context, file_list, "",
+ &generator,
+ &EnumLiteGenerator::Generate);
+ }
}
for (int i = 0; i < file_->message_type_count(); i++) {
if (immutable_api_) {
diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc
index 5715a432..22a70c32 100644
--- a/src/google/protobuf/compiler/java/java_message.cc
+++ b/src/google/protobuf/compiler/java/java_message.cc
@@ -664,34 +664,34 @@ GenerateParseFromMethods(io::Printer* printer) {
"}\n"
"public static $classname$ parseFrom(java.io.InputStream input)\n"
" throws java.io.IOException {\n"
- " return PARSER.parseFrom(input);\n"
+ " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);"
"}\n"
"public static $classname$ parseFrom(\n"
" java.io.InputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws java.io.IOException {\n"
- " return PARSER.parseFrom(input, extensionRegistry);\n"
+ " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);"
"}\n"
"public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n"
" throws java.io.IOException {\n"
- " return PARSER.parseDelimitedFrom(input);\n"
+ " return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input);"
"}\n"
"public static $classname$ parseDelimitedFrom(\n"
" java.io.InputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws java.io.IOException {\n"
- " return PARSER.parseDelimitedFrom(input, extensionRegistry);\n"
+ " return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input, extensionRegistry);"
"}\n"
"public static $classname$ parseFrom(\n"
" com.google.protobuf.CodedInputStream input)\n"
" throws java.io.IOException {\n"
- " return PARSER.parseFrom(input);\n"
+ " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);"
"}\n"
"public static $classname$ parseFrom(\n"
" com.google.protobuf.CodedInputStream input,\n"
" com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n"
" throws java.io.IOException {\n"
- " return PARSER.parseFrom(input, extensionRegistry);\n"
+ " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);"
"}\n"
"\n",
"classname", name_resolver_->GetImmutableClassName(descriptor_));
@@ -1217,9 +1217,8 @@ GenerateParsingConstructor(io::Printer* printer) {
"} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n"
" throw new RuntimeException(e.setUnfinishedMessage(this));\n"
"} catch (java.io.IOException e) {\n"
- " throw new RuntimeException(\n"
- " new com.google.protobuf.InvalidProtocolBufferException(\n"
- " e.getMessage()).setUnfinishedMessage(this));\n"
+ " throw new RuntimeException(new com.google.protobuf.InvalidProtocolBufferException(e)\n"
+ " .setUnfinishedMessage(this));\n"
"} finally {\n");
printer->Indent();
diff --git a/src/google/protobuf/compiler/java/java_message_builder.cc b/src/google/protobuf/compiler/java/java_message_builder.cc
index 72694119..5d535034 100644
--- a/src/google/protobuf/compiler/java/java_message_builder.cc
+++ b/src/google/protobuf/compiler/java/java_message_builder.cc
@@ -538,7 +538,7 @@ GenerateBuilderParsingMethods(io::Printer* printer) {
" parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n"
" } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n"
" parsedMessage = ($classname$) e.getUnfinishedMessage();\n"
- " throw e;\n"
+ " throw e.unwrapIOException();\n"
" } finally {\n"
" if (parsedMessage != null) {\n"
" mergeFrom(parsedMessage);\n"
diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc
index 392333b8..5a7bf82d 100644
--- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc
+++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc
@@ -87,11 +87,13 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor,
(*variables)["field_list_type"] =
"com.google.protobuf.Internal." + capitalized_type + "List";
(*variables)["new_list"] = "new" + capitalized_type + "List";
+ (*variables)["new_list_with_capacity"] =
+ "new" + capitalized_type + "ListWithCapacity";
(*variables)["empty_list"] = "empty" + capitalized_type + "List()";
(*variables)["make_name_unmodifiable"] =
(*variables)["name"] + "_.makeImmutable()";
- (*variables)["repeated_index_get"] =
- (*variables)["name"] + "_.get" + capitalized_type + "(index)";
+ (*variables)["repeated_get"] =
+ (*variables)["name"] + "_.get" + capitalized_type;
(*variables)["repeated_add"] =
(*variables)["name"] + "_.add" + capitalized_type;
(*variables)["repeated_set"] =
@@ -102,11 +104,11 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor,
"com.google.protobuf.Internal.ProtobufList<" +
(*variables)["boxed_type"] + ">";
(*variables)["new_list"] = "newProtobufList";
+ (*variables)["new_list_with_capacity"] = "newProtobufListWithCapacity";
(*variables)["empty_list"] = "emptyProtobufList()";
(*variables)["make_name_unmodifiable"] =
(*variables)["name"] + "_.makeImmutable()";
- (*variables)["repeated_index_get"] =
- (*variables)["name"] + "_.get(index)";
+ (*variables)["repeated_get"] = (*variables)["name"] + "_.get";
(*variables)["repeated_add"] = (*variables)["name"] + "_.add";
(*variables)["repeated_set"] = (*variables)["name"] + "_.set";
}
@@ -629,7 +631,7 @@ GenerateMembers(io::Printer* printer) const {
WriteFieldDocComment(printer, descriptor_);
printer->Print(variables_,
"$deprecation$public $type$ get$capitalized_name$(int index) {\n"
- " return $repeated_index_get$;\n"
+ " return $repeated_get$(index);\n"
"}\n");
if (descriptor_->options().packed() &&
@@ -773,6 +775,9 @@ GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const {
void RepeatedImmutablePrimitiveFieldLiteGenerator::
GenerateParsingCode(io::Printer* printer) const {
+ // TODO(dweis): Scan the input buffer to count, then initialize
+ // appropriately.
+ // TODO(dweis): Scan the input buffer to count and ensure capacity.
printer->Print(variables_,
"if (!$is_mutable$) {\n"
" $name$_ = $new_list$();\n"
@@ -785,8 +790,21 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const {
printer->Print(variables_,
"int length = input.readRawVarint32();\n"
"int limit = input.pushLimit(length);\n"
- "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n"
- " $name$_ = $new_list$();\n"
+ "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n");
+
+ int fixed_size = FixedSize(GetType(descriptor_));
+ if (fixed_size == -1) {
+ // TODO(dweis): Scan the input buffer to count, then initialize
+ // appropriately.
+ printer->Print(variables_,
+ " $name$_ = $new_list$();\n");
+ } else {
+ printer->Print(variables_,
+ " $name$_ = $new_list_with_capacity$(length/$fixed_size$);\n");
+ }
+
+ // TODO(dweis): Scan the input buffer to count and ensure capacity.
+ printer->Print(variables_,
"}\n"
"while (input.getBytesUntilLimit() > 0) {\n"
" $repeated_add$(input.read$capitalized_type$());\n"
@@ -814,12 +832,12 @@ GenerateSerializationCode(io::Printer* printer) const {
" output.writeRawVarint32($name$MemoizedSerializedSize);\n"
"}\n"
"for (int i = 0; i < $name$_.size(); i++) {\n"
- " output.write$capitalized_type$NoTag($name$_.get(i));\n"
+ " output.write$capitalized_type$NoTag($repeated_get$(i));\n"
"}\n");
} else {
printer->Print(variables_,
"for (int i = 0; i < $name$_.size(); i++) {\n"
- " output.write$capitalized_type$($number$, $name$_.get(i));\n"
+ " output.write$capitalized_type$($number$, $repeated_get$(i));\n"
"}\n");
}
}
@@ -835,7 +853,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
"for (int i = 0; i < $name$_.size(); i++) {\n"
" dataSize += com.google.protobuf.CodedOutputStream\n"
- " .compute$capitalized_type$SizeNoTag($name$_.get(i));\n"
+ " .compute$capitalized_type$SizeNoTag($repeated_get$(i));\n"
"}\n");
} else {
printer->Print(variables_,
diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc
index 72ebaeca..7f757e47 100644
--- a/src/google/protobuf/compiler/java/java_string_field.cc
+++ b/src/google/protobuf/compiler/java/java_string_field.cc
@@ -405,7 +405,7 @@ void ImmutableStringFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
if (CheckUtf8(descriptor_)) {
printer->Print(variables_,
- "String s = input.readStringRequireUtf8();\n"
+ "java.lang.String s = input.readStringRequireUtf8();\n"
"$set_has_field_bit_message$\n"
"$name$_ = s;\n");
} else if (!HasDescriptorMethods(descriptor_->file())) {
@@ -414,7 +414,7 @@ GenerateParsingCode(io::Printer* printer) const {
// spurious intermediary ByteString allocations, cutting overall allocations
// in half.
printer->Print(variables_,
- "String s = input.readString();\n"
+ "java.lang.String s = input.readString();\n"
"$set_has_field_bit_message$\n"
"$name$_ = s;\n");
} else {
@@ -665,7 +665,7 @@ void ImmutableStringOneofFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
if (CheckUtf8(descriptor_)) {
printer->Print(variables_,
- "String s = input.readStringRequireUtf8();\n"
+ "java.lang.String s = input.readStringRequireUtf8();\n"
"$set_oneof_case_message$;\n"
"$oneof_name$_ = s;\n");
} else if (!HasDescriptorMethods(descriptor_->file())) {
@@ -674,7 +674,7 @@ GenerateParsingCode(io::Printer* printer) const {
// spurious intermediary ByteString allocations, cutting overall allocations
// in half.
printer->Print(variables_,
- "String s = input.readString();\n"
+ "java.lang.String s = input.readString();\n"
"$set_oneof_case_message$;\n"
"$oneof_name$_ = s;\n");
} else {
@@ -773,12 +773,6 @@ GenerateMembers(io::Printer* printer) const {
" get$capitalized_name$Bytes(int index) {\n"
" return $name$_.getByteString(index);\n"
"}\n");
-
- if (descriptor_->options().packed() &&
- HasGeneratedMethods(descriptor_->containing_type())) {
- printer->Print(variables_,
- "private int $name$MemoizedSerializedSize = -1;\n");
- }
}
void RepeatedImmutableStringFieldGenerator::
@@ -939,14 +933,14 @@ void RepeatedImmutableStringFieldGenerator::
GenerateParsingCode(io::Printer* printer) const {
if (CheckUtf8(descriptor_)) {
printer->Print(variables_,
- "String s = input.readStringRequireUtf8();\n");
+ "java.lang.String s = input.readStringRequireUtf8();\n");
} else if (!HasDescriptorMethods(descriptor_->file())) {
// Lite runtime should attempt to reduce allocations by attempting to
// construct the string directly from the input stream buffer. This avoids
// spurious intermediary ByteString allocations, cutting overall allocations
// in half.
printer->Print(variables_,
- "String s = input.readString();\n");
+ "java.lang.String s = input.readString();\n");
} else {
printer->Print(variables_,
"com.google.protobuf.ByteString bs = input.readBytes();\n");
@@ -966,30 +960,6 @@ GenerateParsingCode(io::Printer* printer) const {
}
void RepeatedImmutableStringFieldGenerator::
-GenerateParsingCodeFromPacked(io::Printer* printer) const {
- printer->Print(variables_,
- "int length = input.readRawVarint32();\n"
- "int limit = input.pushLimit(length);\n"
- "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n"
- " $name$_ = new com.google.protobuf.LazyStringArrayList();\n"
- " $set_mutable_bit_parser$;\n"
- "}\n"
- "while (input.getBytesUntilLimit() > 0) {\n");
- if (CheckUtf8(descriptor_)) {
- printer->Print(variables_,
- " String s = input.readStringRequireUtf8();\n");
- } else {
- printer->Print(variables_,
- " String s = input.readString();\n");
- }
- printer->Print(variables_,
- " $name$.add(s);\n");
- printer->Print(variables_,
- "}\n"
- "input.popLimit(limit);\n");
-}
-
-void RepeatedImmutableStringFieldGenerator::
GenerateParsingDoneCode(io::Printer* printer) const {
printer->Print(variables_,
"if ($get_mutable_bit_parser$) {\n"
@@ -999,21 +969,10 @@ GenerateParsingDoneCode(io::Printer* printer) const {
void RepeatedImmutableStringFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
- if (descriptor_->options().packed()) {
- printer->Print(variables_,
- "if (get$capitalized_name$List().size() > 0) {\n"
- " output.writeRawVarint32($tag$);\n"
- " output.writeRawVarint32($name$MemoizedSerializedSize);\n"
- "}\n"
- "for (int i = 0; i < $name$_.size(); i++) {\n"
- " writeStringNoTag(output, $name$_.getRaw(i));\n"
- "}\n");
- } else {
- printer->Print(variables_,
- "for (int i = 0; i < $name$_.size(); i++) {\n"
- " $writeString$(output, $number$, $name$_.getRaw(i));\n"
- "}\n");
- }
+ printer->Print(variables_,
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " $writeString$(output, $number$, $name$_.getRaw(i));\n"
+ "}\n");
}
void RepeatedImmutableStringFieldGenerator::
@@ -1031,23 +990,8 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(
"size += dataSize;\n");
- if (descriptor_->options().packed()) {
- printer->Print(variables_,
- "if (!get$capitalized_name$List().isEmpty()) {\n"
- " size += $tag_size$;\n"
- " size += com.google.protobuf.CodedOutputStream\n"
- " .computeInt32SizeNoTag(dataSize);\n"
- "}\n");
- } else {
- printer->Print(variables_,
- "size += $tag_size$ * get$capitalized_name$List().size();\n");
- }
-
- // cache the data size for packed fields.
- if (descriptor_->options().packed()) {
- printer->Print(variables_,
- "$name$MemoizedSerializedSize = dataSize;\n");
- }
+ printer->Print(variables_,
+ "size += $tag_size$ * get$capitalized_name$List().size();\n");
printer->Outdent();
printer->Print("}\n");
diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h
index 1ea44dec..a3b57351 100644
--- a/src/google/protobuf/compiler/java/java_string_field.h
+++ b/src/google/protobuf/compiler/java/java_string_field.h
@@ -131,7 +131,6 @@ class RepeatedImmutableStringFieldGenerator : public ImmutableFieldGenerator {
void GenerateMergingCode(io::Printer* printer) const;
void GenerateBuildingCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
- void GenerateParsingCodeFromPacked(io::Printer* printer) const;
void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc
index 092e3c29..eb5964bd 100644
--- a/src/google/protobuf/compiler/java/java_string_field_lite.cc
+++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc
@@ -647,12 +647,6 @@ GenerateMembers(io::Printer* printer) const {
" $name$_.get(index));\n"
"}\n");
- if (descriptor_->options().packed() &&
- HasGeneratedMethods(descriptor_->containing_type())) {
- printer->Print(variables_,
- "private int $name$MemoizedSerializedSize = -1;\n");
- }
-
printer->Print(variables_,
"private void ensure$capitalized_name$IsMutable() {\n"
" if (!$is_mutable$) {\n"
@@ -835,29 +829,6 @@ GenerateParsingCode(io::Printer* printer) const {
}
void RepeatedImmutableStringFieldLiteGenerator::
-GenerateParsingCodeFromPacked(io::Printer* printer) const {
- printer->Print(variables_,
- "int length = input.readRawVarint32();\n"
- "int limit = input.pushLimit(length);\n"
- "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n"
- " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList();\n"
- "}\n"
- "while (input.getBytesUntilLimit() > 0) {\n");
- if (CheckUtf8(descriptor_)) {
- printer->Print(variables_,
- " String s = input.readStringRequireUtf8();\n");
- } else {
- printer->Print(variables_,
- " String s = input.readString();\n");
- }
- printer->Print(variables_,
- " $name$.add(s);\n");
- printer->Print(variables_,
- "}\n"
- "input.popLimit(limit);\n");
-}
-
-void RepeatedImmutableStringFieldLiteGenerator::
GenerateParsingDoneCode(io::Printer* printer) const {
printer->Print(variables_,
"if ($is_mutable$) {\n"
@@ -870,21 +841,10 @@ GenerateSerializationCode(io::Printer* printer) const {
// Lite runtime should reduce allocations by serializing the string directly.
// This avoids spurious intermediary ByteString allocations, cutting overall
// allocations in half.
- if (descriptor_->options().packed()) {
- printer->Print(variables_,
- "if (get$capitalized_name$List().size() > 0) {\n"
- " output.writeRawVarint32($tag$);\n"
- " output.writeRawVarint32($name$MemoizedSerializedSize);\n"
- "}\n"
- "for (int i = 0; i < $name$_.size(); i++) {\n"
- " output.writeStringNoTag($name$_.get(i));\n"
- "}\n");
- } else {
- printer->Print(variables_,
- "for (int i = 0; i < $name$_.size(); i++) {\n"
- " output.writeString($number$, $name$_.get(i));\n"
- "}\n");
- }
+ printer->Print(variables_,
+ "for (int i = 0; i < $name$_.size(); i++) {\n"
+ " output.writeString($number$, $name$_.get(i));\n"
+ "}\n");
}
void RepeatedImmutableStringFieldLiteGenerator::
@@ -906,23 +866,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(
"size += dataSize;\n");
- if (descriptor_->options().packed()) {
- printer->Print(variables_,
- "if (!get$capitalized_name$List().isEmpty()) {\n"
- " size += $tag_size$;\n"
- " size += com.google.protobuf.CodedOutputStream\n"
- " .computeInt32SizeNoTag(dataSize);\n"
- "}\n");
- } else {
- printer->Print(variables_,
- "size += $tag_size$ * get$capitalized_name$List().size();\n");
- }
- // cache the data size for packed fields.
- if (descriptor_->options().packed()) {
- printer->Print(variables_,
- "$name$MemoizedSerializedSize = dataSize;\n");
- }
+ printer->Print(variables_,
+ "size += $tag_size$ * get$capitalized_name$List().size();\n");
printer->Outdent();
printer->Print("}\n");
diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.h b/src/google/protobuf/compiler/java/java_string_field_lite.h
index 9d93b307..4d9b4bd7 100644
--- a/src/google/protobuf/compiler/java/java_string_field_lite.h
+++ b/src/google/protobuf/compiler/java/java_string_field_lite.h
@@ -129,7 +129,6 @@ class RepeatedImmutableStringFieldLiteGenerator
void GenerateMergingCode(io::Printer* printer) const;
void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const;
void GenerateParsingCode(io::Printer* printer) const;
- void GenerateParsingCodeFromPacked(io::Printer* printer) const;
void GenerateParsingDoneCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc
new file mode 100755
index 00000000..e6c3b36a
--- /dev/null
+++ b/src/google/protobuf/compiler/js/js_generator.cc
@@ -0,0 +1,2622 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/compiler/js/js_generator.h>
+
+#include <assert.h>
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace js {
+
+// Sorted list of JavaScript keywords. These cannot be used as names. If they
+// appear, we prefix them with "pb_".
+const char* kKeyword[] = {
+ "abstract",
+ "boolean",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "const",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "export",
+ "extends",
+ "false",
+ "final",
+ "finally",
+ "float",
+ "for",
+ "function",
+ "goto",
+ "if",
+ "implements",
+ "import",
+ "in",
+ "instanceof",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "new",
+ "null",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "return",
+ "short",
+ "static",
+ "super",
+ "switch",
+ "synchronized",
+ "this",
+ "throw",
+ "throws",
+ "transient",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "volatile",
+ "while",
+ "with",
+};
+
+static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*);
+
+namespace {
+
+bool IsReserved(const string& ident) {
+ for (int i = 0; i < kNumKeyword; i++) {
+ if (ident == kKeyword[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns a copy of |filename| with any trailing ".protodevel" or ".proto
+// suffix stripped.
+string StripProto(const string& filename) {
+ const char* suffix = HasSuffixString(filename, ".protodevel")
+ ? ".protodevel" : ".proto";
+ return StripSuffixString(filename, suffix);
+}
+
+// Returns the fully normalized JavaScript path for the given
+// file descriptor's package.
+string GetPath(const GeneratorOptions& options,
+ const FileDescriptor* file) {
+ if (!options.namespace_prefix.empty()) {
+ return options.namespace_prefix;
+ } else if (!file->package().empty()) {
+ return "proto." + file->package();
+ } else {
+ return "proto";
+ }
+}
+
+// Forward declare, so that GetPrefix can call this method,
+// which in turn, calls GetPrefix.
+string GetPath(const GeneratorOptions& options,
+ const Descriptor* descriptor);
+
+// Returns the path prefix for a message or enumeration that
+// lives under the given file and containing type.
+string GetPrefix(const GeneratorOptions& options,
+ const FileDescriptor* file_descriptor,
+ const Descriptor* containing_type) {
+ string prefix = "";
+
+ if (containing_type == NULL) {
+ prefix = GetPath(options, file_descriptor);
+ } else {
+ prefix = GetPath(options, containing_type);
+ }
+
+ if (!prefix.empty()) {
+ prefix += ".";
+ }
+
+ return prefix;
+}
+
+
+// Returns the fully normalized JavaScript path for the given
+// message descriptor.
+string GetPath(const GeneratorOptions& options,
+ const Descriptor* descriptor) {
+ return GetPrefix(
+ options, descriptor->file(),
+ descriptor->containing_type()) + descriptor->name();
+}
+
+
+// Returns the fully normalized JavaScript path for the given
+// field's containing message descriptor.
+string GetPath(const GeneratorOptions& options,
+ const FieldDescriptor* descriptor) {
+ return GetPath(options, descriptor->containing_type());
+}
+
+// Returns the fully normalized JavaScript path for the given
+// enumeration descriptor.
+string GetPath(const GeneratorOptions& options,
+ const EnumDescriptor* enum_descriptor) {
+ return GetPrefix(
+ options, enum_descriptor->file(),
+ enum_descriptor->containing_type()) + enum_descriptor->name();
+}
+
+
+// Returns the fully normalized JavaScript path for the given
+// enumeration value descriptor.
+string GetPath(const GeneratorOptions& options,
+ const EnumValueDescriptor* value_descriptor) {
+ return GetPath(
+ options,
+ value_descriptor->type()) + "." + value_descriptor->name();
+}
+
+// - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
+// (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
+// and with reserved words triggering a "pb_" prefix.
+// - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
+// (use the name directly), then append "List" if appropriate, then append "$"
+// if resulting name is equal to a reserved word.
+// - Enums: just uppercase.
+
+// Locale-independent version of ToLower that deals only with ASCII A-Z.
+char ToLowerASCII(char c) {
+ if (c >= 'A' && c <= 'Z') {
+ return (c - 'A') + 'a';
+ } else {
+ return c;
+ }
+}
+
+vector<string> ParseLowerUnderscore(const string& input) {
+ vector<string> words;
+ string running = "";
+ for (int i = 0; i < input.size(); i++) {
+ if (input[i] == '_') {
+ if (!running.empty()) {
+ words.push_back(running);
+ running.clear();
+ }
+ } else {
+ running += ToLowerASCII(input[i]);
+ }
+ }
+ if (!running.empty()) {
+ words.push_back(running);
+ }
+ return words;
+}
+
+vector<string> ParseUpperCamel(const string& input) {
+ vector<string> words;
+ string running = "";
+ for (int i = 0; i < input.size(); i++) {
+ if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) {
+ words.push_back(running);
+ running.clear();
+ }
+ running += ToLowerASCII(input[i]);
+ }
+ if (!running.empty()) {
+ words.push_back(running);
+ }
+ return words;
+}
+
+string ToLowerCamel(const vector<string>& words) {
+ string result;
+ for (int i = 0; i < words.size(); i++) {
+ string word = words[i];
+ if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
+ word[0] = (word[0] - 'A') + 'a';
+ } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
+ word[0] = (word[0] - 'a') + 'A';
+ }
+ result += word;
+ }
+ return result;
+}
+
+string ToUpperCamel(const vector<string>& words) {
+ string result;
+ for (int i = 0; i < words.size(); i++) {
+ string word = words[i];
+ if (word[0] >= 'a' && word[0] <= 'z') {
+ word[0] = (word[0] - 'a') + 'A';
+ }
+ result += word;
+ }
+ return result;
+}
+
+// Based on code from descriptor.cc (Thanks Kenton!)
+// Uppercases the entire string, turning ValueName into
+// VALUENAME.
+string ToEnumCase(const string& input) {
+ string result;
+ result.reserve(input.size());
+
+ for (int i = 0; i < input.size(); i++) {
+ if ('a' <= input[i] && input[i] <= 'z') {
+ result.push_back(input[i] - 'a' + 'A');
+ } else {
+ result.push_back(input[i]);
+ }
+ }
+
+ return result;
+}
+
+string ToFileName(const string& input) {
+ string result;
+ result.reserve(input.size());
+
+ for (int i = 0; i < input.size(); i++) {
+ if ('A' <= input[i] && input[i] <= 'Z') {
+ result.push_back(input[i] - 'A' + 'a');
+ } else {
+ result.push_back(input[i]);
+ }
+ }
+
+ return result;
+}
+
+// Returns the message/response ID, if set.
+string GetMessageId(const Descriptor* desc) {
+ return string();
+}
+
+
+// Used inside Google only -- do not remove.
+bool IsResponse(const Descriptor* desc) { return false; }
+bool IgnoreField(const FieldDescriptor* field) { return false; }
+
+
+// Does JSPB ignore this entire oneof? True only if all fields are ignored.
+bool IgnoreOneof(const OneofDescriptor* oneof) {
+ for (int i = 0; i < oneof->field_count(); i++) {
+ if (!IgnoreField(oneof->field(i))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+string JSIdent(const FieldDescriptor* field,
+ bool is_upper_camel,
+ bool is_map) {
+ string result;
+ if (field->type() == FieldDescriptor::TYPE_GROUP) {
+ result = is_upper_camel ?
+ ToUpperCamel(ParseUpperCamel(field->message_type()->name())) :
+ ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
+ } else {
+ result = is_upper_camel ?
+ ToUpperCamel(ParseLowerUnderscore(field->name())) :
+ ToLowerCamel(ParseLowerUnderscore(field->name()));
+ }
+ if (is_map) {
+ result += "Map";
+ } else if (field->is_repeated()) {
+ result += "List";
+ }
+ return result;
+}
+
+string JSObjectFieldName(const FieldDescriptor* field) {
+ string name = JSIdent(
+ field,
+ /* is_upper_camel = */ false,
+ /* is_map = */ false);
+ if (IsReserved(name)) {
+ name = "pb_" + name;
+ }
+ return name;
+}
+
+// Returns the field name as a capitalized portion of a getter/setter method
+// name, e.g. MyField for .getMyField().
+string JSGetterName(const FieldDescriptor* field) {
+ string name = JSIdent(field,
+ /* is_upper_camel = */ true,
+ /* is_map = */ false);
+ if (name == "Extension" || name == "JsPbMessageId") {
+ // Avoid conflicts with base-class names.
+ name += "$";
+ }
+ return name;
+}
+
+string JSMapGetterName(const FieldDescriptor* field) {
+ return JSIdent(field,
+ /* is_upper_camel = */ true,
+ /* is_map = */ true);
+}
+
+
+
+string JSOneofName(const OneofDescriptor* oneof) {
+ return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
+}
+
+// Returns the index corresponding to this field in the JSPB array (underlying
+// data storage array).
+string JSFieldIndex(const FieldDescriptor* field) {
+ // Determine whether this field is a member of a group. Group fields are a bit
+ // wonky: their "containing type" is a message type created just for the
+ // group, and that type's parent type has a field with the group-message type
+ // as its message type and TYPE_GROUP as its field type. For such fields, the
+ // index we use is relative to the field number of the group submessage field.
+ // For all other fields, we just use the field number.
+ const Descriptor* containing_type = field->containing_type();
+ const Descriptor* parent_type = containing_type->containing_type();
+ if (parent_type != NULL) {
+ for (int i = 0; i < parent_type->field_count(); i++) {
+ if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP &&
+ parent_type->field(i)->message_type() == containing_type) {
+ return SimpleItoa(field->number() - parent_type->field(i)->number());
+ }
+ }
+ }
+ return SimpleItoa(field->number());
+}
+
+string JSOneofIndex(const OneofDescriptor* oneof) {
+ int index = -1;
+ for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) {
+ const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i);
+ // If at least one field in this oneof is not JSPB-ignored, count the oneof.
+ for (int j = 0; j < o->field_count(); j++) {
+ const FieldDescriptor* f = o->field(j);
+ if (!IgnoreField(f)) {
+ index++;
+ break; // inner loop
+ }
+ }
+ if (o == oneof) {
+ break;
+ }
+ }
+ return SimpleItoa(index);
+}
+
+// Decodes a codepoint in \x0000 -- \xFFFF. Since JS strings are UTF-16, we only
+// need to handle the BMP (16-bit range) here.
+uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) {
+ if (*length == 0) {
+ return 0;
+ }
+ size_t expected = 0;
+ if ((*bytes & 0x80) == 0) {
+ expected = 1;
+ } else if ((*bytes & 0xe0) == 0xc0) {
+ expected = 2;
+ } else if ((*bytes & 0xf0) == 0xe0) {
+ expected = 3;
+ } else {
+ // Too long -- don't accept.
+ *length = 0;
+ return 0;
+ }
+
+ if (*length < expected) {
+ // Not enough bytes -- don't accept.
+ *length = 0;
+ return 0;
+ }
+
+ *length = expected;
+ switch (expected) {
+ case 1: return bytes[0];
+ case 2: return ((bytes[0] & 0x1F) << 6) |
+ ((bytes[1] & 0x3F) << 0);
+ case 3: return ((bytes[0] & 0x0F) << 12) |
+ ((bytes[1] & 0x3F) << 6) |
+ ((bytes[2] & 0x3F) << 0);
+ default: return 0;
+ }
+}
+
+// Escapes the contents of a string to be included within double-quotes ("") in
+// JavaScript. |is_utf8| determines whether the input data (in a C++ string of
+// chars) is UTF-8 encoded (in which case codepoints become JavaScript string
+// characters, escaped with 16-bit hex escapes where necessary) or raw binary
+// (in which case bytes become JavaScript string characters 0 -- 255).
+string EscapeJSString(const string& in, bool is_utf8) {
+ string result;
+ size_t decoded = 0;
+ for (size_t i = 0; i < in.size(); i += decoded) {
+ uint16 codepoint = 0;
+ if (is_utf8) {
+ // Decode the next UTF-8 codepoint.
+ size_t have_bytes = in.size() - i;
+ uint8 bytes[3] = {
+ static_cast<uint8>(in[i]),
+ static_cast<uint8>(((i + 1) < in.size()) ? in[i + 1] : 0),
+ static_cast<uint8>(((i + 2) < in.size()) ? in[i + 2] : 0),
+ };
+ codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
+ if (have_bytes == 0) {
+ break;
+ }
+ decoded = have_bytes;
+ } else {
+ codepoint = static_cast<uint16>(static_cast<uint8>(in[i]));
+ decoded = 1;
+ }
+
+ // Next byte -- used for minimal octal escapes below.
+ char next_byte = (i + decoded) < in.size() ?
+ in[i + decoded] : 0;
+ bool pad_octal = (next_byte >= '0' && next_byte <= '7');
+
+ switch (codepoint) {
+ case '\0': result += pad_octal ? "\\000" : "\\0"; break;
+ case '\b': result += "\\\b"; break;
+ case '\t': result += "\\\t"; break;
+ case '\n': result += "\\\n"; break;
+ case '\r': result += "\\\r"; break;
+ case '\f': result += "\\\f"; break;
+ case '\\': result += "\\\\"; break;
+ case '"': result += pad_octal ? "\\042" : "\\42"; break;
+ case '&': result += pad_octal ? "\\046" : "\\46"; break;
+ case '\'': result += pad_octal ? "\\047" : "\\47"; break;
+ case '<': result += pad_octal ? "\\074" : "\\74"; break;
+ case '=': result += pad_octal ? "\\075" : "\\75"; break;
+ case '>': result += pad_octal ? "\\076" : "\\76"; break;
+ default:
+ // All other non-ASCII codepoints are escaped.
+ // Original codegen uses hex for >= 0x100 and octal for others.
+ if (codepoint >= 0x20 && codepoint <= 0x7e) {
+ result += static_cast<char>(codepoint);
+ } else {
+ if (codepoint >= 0x100) {
+ result += StringPrintf("\\u%04x", codepoint);
+ } else {
+ if (pad_octal || codepoint >= 0100) {
+ result += "\\";
+ result += ('0' + ((codepoint >> 6) & 07));
+ result += ('0' + ((codepoint >> 3) & 07));
+ result += ('0' + ((codepoint >> 0) & 07));
+ } else if (codepoint >= 010) {
+ result += "\\";
+ result += ('0' + ((codepoint >> 3) & 07));
+ result += ('0' + ((codepoint >> 0) & 07));
+ } else {
+ result += "\\";
+ result += ('0' + ((codepoint >> 0) & 07));
+ }
+ }
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+string EscapeBase64(const string& in) {
+ static const char* kAlphabet =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ string result;
+
+ for (size_t i = 0; i < in.size(); i += 3) {
+ int value = (in[i] << 16) |
+ (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) |
+ (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0);
+ result += kAlphabet[(value >> 18) & 0x3f];
+ result += kAlphabet[(value >> 12) & 0x3f];
+ if ((i + 1) < in.size()) {
+ result += kAlphabet[(value >> 6) & 0x3f];
+ } else {
+ result += '=';
+ }
+ if ((i + 2) < in.size()) {
+ result += kAlphabet[(value >> 0) & 0x3f];
+ } else {
+ result += '=';
+ }
+ }
+
+ return result;
+}
+
+// Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the
+// original codegen's formatting (which is just .toString() on java.lang.Double
+// or java.lang.Float).
+string PostProcessFloat(string result) {
+ // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN.
+ if (result == "inf") {
+ return "Infinity";
+ } else if (result == "-inf") {
+ return "-Infinity";
+ } else if (result == "nan") {
+ return "NaN";
+ }
+
+ // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii)
+ // ensure that the mantissa (portion prior to the "e") has at least one
+ // fractional digit (after the decimal point), and (iii) strip any unnecessary
+ // leading zeroes and/or '+' signs from the exponent.
+ string::size_type exp_pos = result.find('e');
+ if (exp_pos != string::npos) {
+ string mantissa = result.substr(0, exp_pos);
+ string exponent = result.substr(exp_pos + 1);
+
+ // Add ".0" to mantissa if no fractional part exists.
+ if (mantissa.find('.') == string::npos) {
+ mantissa += ".0";
+ }
+
+ // Strip the sign off the exponent and store as |exp_neg|.
+ bool exp_neg = false;
+ if (!exponent.empty() && exponent[0] == '+') {
+ exponent = exponent.substr(1);
+ } else if (!exponent.empty() && exponent[0] == '-') {
+ exp_neg = true;
+ exponent = exponent.substr(1);
+ }
+
+ // Strip any leading zeroes off the exponent.
+ while (exponent.size() > 1 && exponent[0] == '0') {
+ exponent = exponent.substr(1);
+ }
+
+ return mantissa + "E" + string(exp_neg ? "-" : "") + exponent;
+ }
+
+ // Otherwise, this is an ordinary decimal number. Append ".0" if result has no
+ // decimal/fractional part in order to match output of original codegen.
+ if (result.find('.') == string::npos) {
+ result += ".0";
+ }
+
+ return result;
+}
+
+string FloatToString(float value) {
+ string result = SimpleFtoa(value);
+ return PostProcessFloat(result);
+}
+
+string DoubleToString(double value) {
+ string result = SimpleDtoa(value);
+ return PostProcessFloat(result);
+}
+
+string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
+ return orig;
+}
+
+string JSFieldDefault(const FieldDescriptor* field) {
+ assert(field->has_default_value());
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return MaybeNumberString(
+ field, SimpleItoa(field->default_value_int32()));
+ case FieldDescriptor::CPPTYPE_UINT32:
+ // The original codegen is in Java, and Java protobufs store unsigned
+ // integer values as signed integer values. In order to exactly match the
+ // output, we need to reinterpret as base-2 signed. Ugh.
+ return MaybeNumberString(
+ field, SimpleItoa(static_cast<int32>(field->default_value_uint32())));
+ case FieldDescriptor::CPPTYPE_INT64:
+ return MaybeNumberString(
+ field, SimpleItoa(field->default_value_int64()));
+ case FieldDescriptor::CPPTYPE_UINT64:
+ // See above note for uint32 -- reinterpreting as signed.
+ return MaybeNumberString(
+ field, SimpleItoa(static_cast<int64>(field->default_value_uint64())));
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return SimpleItoa(field->default_value_enum()->number());
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return field->default_value_bool() ? "true" : "false";
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return FloatToString(field->default_value_float());
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return DoubleToString(field->default_value_double());
+ case FieldDescriptor::CPPTYPE_STRING:
+ if (field->type() == FieldDescriptor::TYPE_STRING) {
+ return "\"" + EscapeJSString(field->default_value_string(), true) +
+ "\"";
+ } else {
+ return "\"" + EscapeBase64(field->default_value_string()) +
+ "\"";
+ }
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return "null";
+ }
+ GOOGLE_LOG(FATAL) << "Shouldn't reach here.";
+ return "";
+}
+
+string ProtoTypeName(const GeneratorOptions& options,
+ const FieldDescriptor* field) {
+ switch (field->type()) {
+ case FieldDescriptor::TYPE_BOOL:
+ return "bool";
+ 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_STRING:
+ return "string";
+ case FieldDescriptor::TYPE_BYTES:
+ return "bytes";
+ case FieldDescriptor::TYPE_GROUP:
+ return GetPath(options, field->message_type());
+ case FieldDescriptor::TYPE_ENUM:
+ return GetPath(options, field->enum_type());
+ case FieldDescriptor::TYPE_MESSAGE:
+ return GetPath(options, field->message_type());
+ default:
+ return "";
+ }
+}
+
+string JSIntegerTypeName(const FieldDescriptor* field) {
+ return "number";
+}
+
+string JSTypeName(const GeneratorOptions& options,
+ const FieldDescriptor* field) {
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return "boolean";
+ case FieldDescriptor::CPPTYPE_INT32:
+ return JSIntegerTypeName(field);
+ case FieldDescriptor::CPPTYPE_INT64:
+ return JSIntegerTypeName(field);
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return JSIntegerTypeName(field);
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return JSIntegerTypeName(field);
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return "number";
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return "number";
+ case FieldDescriptor::CPPTYPE_STRING:
+ return "string";
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return GetPath(options, field->enum_type());
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return GetPath(options, field->message_type());
+ default:
+ return "";
+ }
+}
+
+bool HasFieldPresence(const FieldDescriptor* field);
+
+string JSFieldTypeAnnotation(const GeneratorOptions& options,
+ const FieldDescriptor* field,
+ bool force_optional,
+ bool force_present,
+ bool singular_if_not_packed,
+ bool always_singular) {
+ bool is_primitive =
+ (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
+ field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE);
+
+ string jstype = JSTypeName(options, field);
+
+ if (field->is_repeated() &&
+ !always_singular &&
+ (field->is_packed() || !singular_if_not_packed)) {
+ if (!is_primitive) {
+ jstype = "!" + jstype;
+ }
+ jstype = "Array.<" + jstype + ">";
+ if (!force_optional) {
+ jstype = "!" + jstype;
+ }
+ }
+
+ if (field->is_optional() && is_primitive &&
+ (!field->has_default_value() || force_optional) && !force_present) {
+ jstype += "?";
+ } else if (field->is_required() && !is_primitive && !force_optional) {
+ jstype = "!" + jstype;
+ }
+
+ if (force_optional && HasFieldPresence(field)) {
+ jstype += "|undefined";
+ }
+ if (force_present && jstype[0] != '!' && !is_primitive) {
+ jstype = "!" + jstype;
+ }
+
+ return jstype;
+}
+
+string JSBinaryReaderMethodType(const FieldDescriptor* field) {
+ string name = field->type_name();
+ if (name[0] >= 'a' && name[0] <= 'z') {
+ name[0] = (name[0] - 'a') + 'A';
+ }
+
+ return name;
+}
+
+string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
+ bool is_writer) {
+ string name = JSBinaryReaderMethodType(field);
+ if (is_writer && field->type() == FieldDescriptor::TYPE_BYTES) {
+ // Override for `bytes` fields: treat string as raw bytes, not base64.
+ name = "BytesRawString";
+ }
+ if (field->is_packed()) {
+ name = "Packed" + name;
+ } else if (is_writer && field->is_repeated()) {
+ name = "Repeated" + name;
+ }
+ return name;
+}
+
+string JSBinaryReaderMethodName(const FieldDescriptor* field) {
+ return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
+}
+
+string JSBinaryWriterMethodName(const FieldDescriptor* field) {
+ return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
+}
+
+string JSReturnClause(const FieldDescriptor* desc) {
+ return "";
+}
+
+string JSReturnDoc(const GeneratorOptions& options,
+ const FieldDescriptor* desc) {
+ return "";
+}
+
+bool HasRepeatedFields(const Descriptor* desc) {
+ for (int i = 0; i < desc->field_count(); i++) {
+ if (desc->field(i)->is_repeated()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char* kRepeatedFieldArrayName = ".repeatedFields_";
+
+string RepeatedFieldsArrayName(const GeneratorOptions& options,
+ const Descriptor* desc) {
+ return HasRepeatedFields(desc) ?
+ (GetPath(options, desc) + kRepeatedFieldArrayName) : "null";
+}
+
+bool HasOneofFields(const Descriptor* desc) {
+ for (int i = 0; i < desc->field_count(); i++) {
+ if (desc->field(i)->containing_oneof()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char* kOneofGroupArrayName = ".oneofGroups_";
+
+string OneofFieldsArrayName(const GeneratorOptions& options,
+ const Descriptor* desc) {
+ return HasOneofFields(desc) ?
+ (GetPath(options, desc) + kOneofGroupArrayName) : "null";
+}
+
+string RepeatedFieldNumberList(const Descriptor* desc) {
+ std::vector<string> numbers;
+ for (int i = 0; i < desc->field_count(); i++) {
+ if (desc->field(i)->is_repeated()) {
+ numbers.push_back(JSFieldIndex(desc->field(i)));
+ }
+ }
+ return "[" + Join(numbers, ",") + "]";
+}
+
+string OneofGroupList(const Descriptor* desc) {
+ // List of arrays (one per oneof), each of which is a list of field indices
+ std::vector<string> oneof_entries;
+ for (int i = 0; i < desc->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = desc->oneof_decl(i);
+ if (IgnoreOneof(oneof)) {
+ continue;
+ }
+
+ std::vector<string> oneof_fields;
+ for (int j = 0; j < oneof->field_count(); j++) {
+ if (IgnoreField(oneof->field(j))) {
+ continue;
+ }
+ oneof_fields.push_back(JSFieldIndex(oneof->field(j)));
+ }
+ oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]");
+ }
+ return "[" + Join(oneof_entries, ",") + "]";
+}
+
+string JSOneofArray(const GeneratorOptions& options,
+ const FieldDescriptor* field) {
+ return OneofFieldsArrayName(options, field->containing_type()) + "[" +
+ JSOneofIndex(field->containing_oneof()) + "]";
+}
+
+string RelativeTypeName(const FieldDescriptor* field) {
+ assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
+ field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
+ // For a field with an enum or message type, compute a name relative to the
+ // path name of the message type containing this field.
+ string package = field->file()->package();
+ string containing_type = field->containing_type()->full_name() + ".";
+ string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) ?
+ field->enum_type()->full_name() : field->message_type()->full_name();
+
+ // |prefix| is advanced as we find separators '.' past the common package
+ // prefix that yield common prefixes in the containing type's name and this
+ // type's name.
+ int prefix = 0;
+ for (int i = 0; i < type.size() && i < containing_type.size(); i++) {
+ if (type[i] != containing_type[i]) {
+ break;
+ }
+ if (type[i] == '.' && i >= package.size()) {
+ prefix = i + 1;
+ }
+ }
+
+ return type.substr(prefix);
+}
+
+string JSExtensionsObjectName(const GeneratorOptions& options,
+ const Descriptor* desc) {
+ if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
+ return "jspb.Message.messageSetExtensions";
+ } else {
+ return GetPath(options, desc) + ".extensions";
+ }
+}
+
+string FieldDefinition(const GeneratorOptions& options,
+ const FieldDescriptor* field) {
+ string qualifier = field->is_repeated() ? "repeated" :
+ (field->is_optional() ? "optional" : "required");
+ string type, name;
+ if (field->type() == FieldDescriptor::TYPE_ENUM ||
+ field->type() == FieldDescriptor::TYPE_MESSAGE) {
+ type = RelativeTypeName(field);
+ name = field->name();
+ } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
+ type = "group";
+ name = field->message_type()->name();
+ } else {
+ type = ProtoTypeName(options, field);
+ name = field->name();
+ }
+ return StringPrintf("%s %s %s = %d;",
+ qualifier.c_str(),
+ type.c_str(),
+ name.c_str(),
+ field->number());
+}
+
+string FieldComments(const FieldDescriptor* field) {
+ string comments;
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) {
+ comments +=
+ " * Note that Boolean fields may be set to 0/1 when serialized from "
+ "a Java server.\n"
+ " * You should avoid comparisons like {@code val === true/false} in "
+ "those cases.\n";
+ }
+ if (field->is_repeated()) {
+ comments +=
+ " * If you change this array by adding, removing or replacing "
+ "elements, or if you\n"
+ " * replace the array itself, then you must call the setter to "
+ "update it.\n";
+ }
+ return comments;
+}
+
+bool ShouldGenerateExtension(const FieldDescriptor* field) {
+ return
+ field->is_extension() &&
+ !IgnoreField(field);
+}
+
+bool HasExtensions(const Descriptor* desc) {
+ if (desc->extension_count() > 0) {
+ return true;
+ }
+ for (int i = 0; i < desc->nested_type_count(); i++) {
+ if (HasExtensions(desc->nested_type(i))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool HasExtensions(const FileDescriptor* file) {
+ for (int i = 0; i < file->extension_count(); i++) {
+ if (ShouldGenerateExtension(file->extension(i))) {
+ return true;
+ }
+ }
+ for (int i = 0; i < file->message_type_count(); i++) {
+ if (HasExtensions(file->message_type(i))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IsExtendable(const Descriptor* desc) {
+ return desc->extension_range_count() > 0;
+}
+
+// Returns the max index in the underlying data storage array beyond which the
+// extension object is used.
+string GetPivot(const Descriptor* desc) {
+ static const int kDefaultPivot = (1 << 29); // max field number (29 bits)
+
+ // Find the max field number
+ int max_field_number = 0;
+ for (int i = 0; i < desc->field_count(); i++) {
+ if (!IgnoreField(desc->field(i)) &&
+ desc->field(i)->number() > max_field_number) {
+ max_field_number = desc->field(i)->number();
+ }
+ }
+
+ int pivot = -1;
+ if (IsExtendable(desc)) {
+ pivot = ((max_field_number + 1) < kDefaultPivot) ?
+ (max_field_number + 1) : kDefaultPivot;
+ }
+
+ return SimpleItoa(pivot);
+}
+
+// Returns true for fields that represent "null" as distinct from the default
+// value. See https://go/proto3#heading=h.kozewqqcqhuz for more information.
+bool HasFieldPresence(const FieldDescriptor* field) {
+ return
+ (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ||
+ (field->containing_oneof() != NULL) ||
+ (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3);
+}
+
+// For proto3 fields without presence, returns a string representing the default
+// value in JavaScript. See https://go/proto3#heading=h.kozewqqcqhuz for more
+// information.
+string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) {
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ case FieldDescriptor::CPPTYPE_INT64:
+ case FieldDescriptor::CPPTYPE_UINT32:
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ return "0";
+ }
+
+ case FieldDescriptor::CPPTYPE_ENUM:
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return "0";
+
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return "false";
+
+ case FieldDescriptor::CPPTYPE_STRING:
+ return "\"\"";
+
+ default:
+ // BYTES and MESSAGE are handled separately.
+ assert(false);
+ return "";
+ }
+}
+
+} // anonymous namespace
+
+void Generator::GenerateHeader(const GeneratorOptions& options,
+ io::Printer* printer) const {
+ printer->Print("/**\n"
+ " * @fileoverview\n"
+ " * @enhanceable\n"
+ " * @public\n"
+ " */\n"
+ "// GENERATED CODE -- DO NOT EDIT!\n"
+ "\n");
+}
+
+void Generator::FindProvides(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FileDescriptor*>& files,
+ std::set<string>* provided) const {
+ for (int i = 0; i < files.size(); i++) {
+ for (int j = 0; j < files[i]->message_type_count(); j++) {
+ FindProvidesForMessage(options, printer, files[i]->message_type(j),
+ provided);
+ }
+ for (int j = 0; j < files[i]->enum_type_count(); j++) {
+ FindProvidesForEnum(options, printer, files[i]->enum_type(j),
+ provided);
+ }
+ }
+
+ printer->Print("\n");
+}
+
+void Generator::FindProvidesForMessage(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc,
+ std::set<string>* provided) const {
+ string name = GetPath(options, desc);
+ provided->insert(name);
+
+ for (int i = 0; i < desc->enum_type_count(); i++) {
+ FindProvidesForEnum(options, printer, desc->enum_type(i),
+ provided);
+ }
+ for (int i = 0; i < desc->nested_type_count(); i++) {
+ FindProvidesForMessage(options, printer, desc->nested_type(i),
+ provided);
+ }
+}
+
+void Generator::FindProvidesForEnum(const GeneratorOptions& options,
+ io::Printer* printer,
+ const EnumDescriptor* enumdesc,
+ std::set<string>* provided) const {
+ string name = GetPath(options, enumdesc);
+ provided->insert(name);
+}
+
+void Generator::FindProvidesForFields(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FieldDescriptor*>& fields,
+ std::set<string>* provided) const {
+ for (int i = 0; i < fields.size(); i++) {
+ const FieldDescriptor* field = fields[i];
+
+ if (IgnoreField(field)) {
+ continue;
+ }
+
+ string name =
+ GetPath(options, field->file()) + "." + JSObjectFieldName(field);
+ provided->insert(name);
+ }
+}
+
+void Generator::GenerateProvides(const GeneratorOptions& options,
+ io::Printer* printer,
+ std::set<string>* provided) const {
+ for (std::set<string>::iterator it = provided->begin();
+ it != provided->end(); ++it) {
+ printer->Print("goog.provide('$name$');\n",
+ "name", *it);
+ }
+}
+
+void Generator::GenerateRequires(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc,
+ std::set<string>* provided) const {
+ std::set<string> required;
+ std::set<string> forwards;
+ bool have_message = false;
+ FindRequiresForMessage(options, desc,
+ &required, &forwards, &have_message);
+
+ GenerateRequiresImpl(options, printer, &required, &forwards, provided,
+ /* require_jspb = */ have_message,
+ /* require_extension = */ HasExtensions(desc));
+}
+
+void Generator::GenerateRequires(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FileDescriptor*>& files,
+ std::set<string>* provided) const {
+ std::set<string> required;
+ std::set<string> forwards;
+ bool have_extensions = false;
+ bool have_message = false;
+
+ for (int i = 0; i < files.size(); i++) {
+ for (int j = 0; j < files[i]->message_type_count(); j++) {
+ FindRequiresForMessage(options,
+ files[i]->message_type(j),
+ &required, &forwards, &have_message);
+ }
+ if (!have_extensions && HasExtensions(files[i])) {
+ have_extensions = true;
+ }
+
+ for (int j = 0; j < files[i]->extension_count(); j++) {
+ const FieldDescriptor* extension = files[i]->extension(j);
+ if (IgnoreField(extension)) {
+ continue;
+ }
+ if (extension->containing_type()->full_name() !=
+ "google.protobuf.bridge.MessageSet") {
+ required.insert(GetPath(options, extension->containing_type()));
+ }
+ FindRequiresForField(options, extension, &required, &forwards);
+ have_extensions = true;
+ }
+ }
+
+ GenerateRequiresImpl(options, printer, &required, &forwards, provided,
+ /* require_jspb = */ have_message,
+ /* require_extension = */ have_extensions);
+}
+
+void Generator::GenerateRequires(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FieldDescriptor*>& fields,
+ std::set<string>* provided) const {
+ std::set<string> required;
+ std::set<string> forwards;
+ for (int i = 0; i < fields.size(); i++) {
+ const FieldDescriptor* field = fields[i];
+ if (IgnoreField(field)) {
+ continue;
+ }
+ FindRequiresForExtension(options, field, &required, &forwards);
+ }
+
+ GenerateRequiresImpl(options, printer, &required, &forwards, provided,
+ /* require_jspb = */ false,
+ /* require_extension = */ fields.size() > 0);
+}
+
+void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
+ io::Printer* printer,
+ std::set<string>* required,
+ std::set<string>* forwards,
+ std::set<string>* provided,
+ bool require_jspb,
+ bool require_extension) const {
+ if (require_jspb) {
+ printer->Print(
+ "goog.require('jspb.Message');\n");
+ if (options.binary) {
+ printer->Print(
+ "goog.require('jspb.BinaryReader');\n"
+ "goog.require('jspb.BinaryWriter');\n");
+ }
+ }
+ if (require_extension) {
+ printer->Print(
+ "goog.require('jspb.ExtensionFieldInfo');\n");
+ }
+
+ std::set<string>::iterator it;
+ for (it = required->begin(); it != required->end(); ++it) {
+ if (provided->find(*it) != provided->end()) {
+ continue;
+ }
+ printer->Print("goog.require('$name$');\n",
+ "name", *it);
+ }
+
+ printer->Print("\n");
+
+ for (it = forwards->begin(); it != forwards->end(); ++it) {
+ if (provided->find(*it) != provided->end()) {
+ continue;
+ }
+ printer->Print("goog.forwardDeclare('$name$');\n",
+ "name", *it);
+ }
+}
+
+bool NamespaceOnly(const Descriptor* desc) {
+ return false;
+}
+
+void Generator::FindRequiresForMessage(
+ const GeneratorOptions& options,
+ const Descriptor* desc,
+ std::set<string>* required,
+ std::set<string>* forwards,
+ bool* have_message) const {
+
+
+ if (!NamespaceOnly(desc)) {
+ *have_message = true;
+ for (int i = 0; i < desc->field_count(); i++) {
+ const FieldDescriptor* field = desc->field(i);
+ if (IgnoreField(field)) {
+ continue;
+ }
+ FindRequiresForField(options, field, required, forwards);
+ }
+ }
+
+ for (int i = 0; i < desc->extension_count(); i++) {
+ const FieldDescriptor* field = desc->extension(i);
+ if (IgnoreField(field)) {
+ continue;
+ }
+ FindRequiresForExtension(options, field, required, forwards);
+ }
+
+ for (int i = 0; i < desc->nested_type_count(); i++) {
+ FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
+ have_message);
+ }
+}
+
+void Generator::FindRequiresForField(const GeneratorOptions& options,
+ const FieldDescriptor* field,
+ std::set<string>* required,
+ std::set<string>* forwards) const {
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
+ // N.B.: file-level extensions with enum type do *not* create
+ // dependencies, as per original codegen.
+ !(field->is_extension() && field->extension_scope() == NULL)) {
+ if (options.add_require_for_enums) {
+ required->insert(GetPath(options, field->enum_type()));
+ } else {
+ forwards->insert(GetPath(options, field->enum_type()));
+ }
+ } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ required->insert(GetPath(options, field->message_type()));
+ }
+}
+
+void Generator::FindRequiresForExtension(const GeneratorOptions& options,
+ const FieldDescriptor* field,
+ std::set<string>* required,
+ std::set<string>* forwards) const {
+ if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") {
+ required->insert(GetPath(options, field->containing_type()));
+ }
+ FindRequiresForField(options, field, required, forwards);
+}
+
+void Generator::GenerateTestOnly(const GeneratorOptions& options,
+ io::Printer* printer) const {
+ if (options.testonly) {
+ printer->Print("goog.setTestOnly();\n\n");
+ }
+ printer->Print("\n");
+}
+
+void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FileDescriptor* file) const {
+ for (int i = 0; i < file->message_type_count(); i++) {
+ GenerateClass(options, printer, file->message_type(i));
+ }
+ for (int i = 0; i < file->enum_type_count(); i++) {
+ GenerateEnum(options, printer, file->enum_type(i));
+ }
+}
+
+void Generator::GenerateClass(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ if (!NamespaceOnly(desc)) {
+ printer->Print("\n");
+ GenerateClassConstructor(options, printer, desc);
+ GenerateClassFieldInfo(options, printer, desc);
+
+
+ GenerateClassToObject(options, printer, desc);
+ if (options.binary) {
+ // These must come *before* the extension-field info generation in
+ // GenerateClassRegistration so that references to the binary
+ // serialization/deserialization functions may be placed in the extension
+ // objects.
+ GenerateClassDeserializeBinary(options, printer, desc);
+ GenerateClassSerializeBinary(options, printer, desc);
+ }
+ GenerateClassClone(options, printer, desc);
+ GenerateClassRegistration(options, printer, desc);
+ GenerateClassFields(options, printer, desc);
+ if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") {
+ GenerateClassExtensionFieldInfo(options, printer, desc);
+ }
+ }
+
+ // Recurse on nested types.
+ for (int i = 0; i < desc->enum_type_count(); i++) {
+ GenerateEnum(options, printer, desc->enum_type(i));
+ }
+ for (int i = 0; i < desc->nested_type_count(); i++) {
+ GenerateClass(options, printer, desc->nested_type(i));
+ }
+}
+
+void Generator::GenerateClassConstructor(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ printer->Print(
+ "/**\n"
+ " * Generated by JsPbCodeGenerator.\n"
+ " * @param {Array=} opt_data Optional initial data array, typically "
+ "from a\n"
+ " * server response, or constructed directly in Javascript. The array "
+ "is used\n"
+ " * in place and becomes part of the constructed object. It is not "
+ "cloned.\n"
+ " * If no data is provided, the constructed object will be empty, but "
+ "still\n"
+ " * valid.\n"
+ " * @extends {jspb.Message}\n"
+ " * @constructor\n"
+ " */\n"
+ "$classname$ = function(opt_data) {\n",
+ "classname", GetPath(options, desc));
+ string message_id = GetMessageId(desc);
+ printer->Print(
+ " jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
+ "$rptfields$, $oneoffields$);\n",
+ "messageId", !message_id.empty() ?
+ ("'" + message_id + "'") :
+ (IsResponse(desc) ? "''" : "0"),
+ "pivot", GetPivot(desc),
+ "rptfields", RepeatedFieldsArrayName(options, desc),
+ "oneoffields", OneofFieldsArrayName(options, desc));
+ printer->Print(
+ "};\n"
+ "goog.inherits($classname$, jspb.Message);\n"
+ "if (goog.DEBUG && !COMPILED) {\n"
+ " $classname$.displayName = '$classname$';\n"
+ "}\n",
+ "classname", GetPath(options, desc));
+}
+
+void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ if (HasRepeatedFields(desc)) {
+ printer->Print(
+ "/**\n"
+ " * List of repeated fields within this message type.\n"
+ " * @private {!Array<number>}\n"
+ " * @const\n"
+ " */\n"
+ "$classname$$rptfieldarray$ = $rptfields$;\n"
+ "\n",
+ "classname", GetPath(options, desc),
+ "rptfieldarray", kRepeatedFieldArrayName,
+ "rptfields", RepeatedFieldNumberList(desc));
+ }
+
+ if (HasOneofFields(desc)) {
+ printer->Print(
+ "/**\n"
+ " * Oneof group definitions for this message. Each group defines the "
+ "field\n"
+ " * numbers belonging to that group. When of these fields' value is "
+ "set, all\n"
+ " * other fields in the group are cleared. During deserialization, if "
+ "multiple\n"
+ " * fields are encountered for a group, only the last value seen will "
+ "be kept.\n"
+ " * @private {!Array<!Array<number>>}\n"
+ " * @const\n"
+ " */\n"
+ "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
+ "\n",
+ "classname", GetPath(options, desc),
+ "oneofgrouparray", kOneofGroupArrayName,
+ "oneofgroups", OneofGroupList(desc));
+
+ for (int i = 0; i < desc->oneof_decl_count(); i++) {
+ if (IgnoreOneof(desc->oneof_decl(i))) {
+ continue;
+ }
+ GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
+ }
+ }
+}
+
+void Generator::GenerateClassXid(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ printer->Print(
+ "\n"
+ "\n"
+ "$class$.prototype.messageXid = xid('$class$');\n",
+ "class", GetPath(options, desc));
+}
+
+void Generator::GenerateOneofCaseDefinition(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const OneofDescriptor* oneof) const {
+ printer->Print(
+ "/**\n"
+ " * @enum {number}\n"
+ " */\n"
+ "$classname$.$oneof$Case = {\n"
+ " $upcase$_NOT_SET: 0",
+ "classname", GetPath(options, oneof->containing_type()),
+ "oneof", JSOneofName(oneof),
+ "upcase", ToEnumCase(oneof->name()));
+
+ for (int i = 0; i < oneof->field_count(); i++) {
+ if (IgnoreField(oneof->field(i))) {
+ continue;
+ }
+
+ printer->Print(
+ ",\n"
+ " $upcase$: $number$",
+ "upcase", ToEnumCase(oneof->field(i)->name()),
+ "number", JSFieldIndex(oneof->field(i)));
+ }
+
+ printer->Print(
+ "\n"
+ "};\n"
+ "\n"
+ "/**\n"
+ " * @return {$class$.$oneof$Case}\n"
+ " */\n"
+ "$class$.prototype.get$oneof$Case = function() {\n"
+ " return /** @type {$class$.$oneof$Case} */(jspb.Message."
+ "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
+ "};\n"
+ "\n",
+ "class", GetPath(options, oneof->containing_type()),
+ "oneof", JSOneofName(oneof),
+ "oneofindex", JSOneofIndex(oneof));
+}
+
+void Generator::GenerateClassToObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ printer->Print(
+ "\n"
+ "\n"
+ "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
+ "/**\n"
+ " * Creates an object representation of this proto suitable for use in "
+ "Soy templates.\n"
+ " * Field names that are reserved in JavaScript and will be renamed to "
+ "pb_name.\n"
+ " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
+ " * For the list of reserved names please see:\n"
+ " * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.\n"
+ " * @param {boolean=} opt_includeInstance Whether to include the JSPB "
+ "instance\n"
+ " * for transitional soy proto support: http://goto/soy-param-"
+ "migration\n"
+ " * @return {!Object}\n"
+ " */\n"
+ "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
+ " return $classname$.toObject(opt_includeInstance, this);\n"
+ "};\n"
+ "\n"
+ "\n"
+ "/**\n"
+ " * Static version of the {@see toObject} method.\n"
+ " * @param {boolean|undefined} includeInstance Whether to include the "
+ "JSPB\n"
+ " * instance for transitional soy proto support:\n"
+ " * http://goto/soy-param-migration\n"
+ " * @param {!$classname$} msg The msg instance to transform.\n"
+ " * @return {!Object}\n"
+ " */\n"
+ "$classname$.toObject = function(includeInstance, msg) {\n"
+ " var f, obj = {",
+ "classname", GetPath(options, desc));
+
+ bool first = true;
+ for (int i = 0; i < desc->field_count(); i++) {
+ const FieldDescriptor* field = desc->field(i);
+ if (IgnoreField(field)) {
+ continue;
+ }
+
+ if (!first) {
+ printer->Print(",\n ");
+ } else {
+ printer->Print("\n ");
+ first = false;
+ }
+
+ GenerateClassFieldToObject(options, printer, field);
+ }
+
+ if (!first) {
+ printer->Print("\n };\n\n");
+ } else {
+ printer->Print("\n\n };\n\n");
+ }
+
+ if (IsExtendable(desc)) {
+ printer->Print(
+ " jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
+ "obj,\n"
+ " $extObject$, $class$.prototype.getExtension,\n"
+ " includeInstance);\n",
+ "extObject", JSExtensionsObjectName(options, desc),
+ "class", GetPath(options, desc));
+ }
+
+ printer->Print(
+ " if (includeInstance) {\n"
+ " obj.$$jspbMessageInstance = msg\n"
+ " }\n"
+ " return obj;\n"
+ "};\n"
+ "}\n"
+ "\n"
+ "\n",
+ "classname", GetPath(options, desc));
+}
+
+void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const {
+ printer->Print("$fieldname$: ",
+ "fieldname", JSObjectFieldName(field));
+
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ // Message field.
+ if (field->is_repeated()) {
+ {
+ printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
+ " $type$.toObject, includeInstance)",
+ "getter", JSGetterName(field),
+ "type", GetPath(options, field->message_type()));
+ }
+ } else {
+ printer->Print("(f = msg.get$getter$()) && "
+ "$type$.toObject(includeInstance, f)",
+ "getter", JSGetterName(field),
+ "type", GetPath(options, field->message_type()));
+ }
+ } else {
+ // Simple field (singular or repeated).
+ if (!HasFieldPresence(field) && !field->is_repeated()) {
+ // Delegate to the generated get<field>() method in order not to duplicate
+ // the proto3-field-default-value logic here.
+ printer->Print("msg.get$getter$()",
+ "getter", JSGetterName(field));
+ } else {
+ if (field->has_default_value()) {
+ printer->Print("jspb.Message.getField(msg, $index$) != null ? "
+ "jspb.Message.getField(msg, $index$) : $defaultValue$",
+ "index", JSFieldIndex(field),
+ "defaultValue", JSFieldDefault(field));
+ } else {
+ printer->Print("jspb.Message.getField(msg, $index$)",
+ "index", JSFieldIndex(field));
+ }
+ }
+ }
+}
+
+void Generator::GenerateClassFromObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ printer->Print(
+ "if (jspb.Message.GENERATE_FROM_OBJECT) {\n"
+ "/**\n"
+ " * Loads data from an object into a new instance of this proto.\n"
+ " * @param {!Object} obj The object representation of this proto to\n"
+ " * load the data from.\n"
+ " * @return {!$classname$}\n"
+ " */\n"
+ "$classname$.fromObject = function(obj) {\n"
+ " var f, msg = new $classname$();\n",
+ "classname", GetPath(options, desc));
+
+ for (int i = 0; i < desc->field_count(); i++) {
+ const FieldDescriptor* field = desc->field(i);
+ GenerateClassFieldFromObject(options, printer, field);
+ }
+
+ printer->Print(
+ " return msg;\n"
+ "};\n"
+ "}\n");
+}
+
+void Generator::GenerateClassFieldFromObject(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const {
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ // Message field (singular or repeated)
+ if (field->is_repeated()) {
+ {
+ printer->Print(
+ " goog.isDef(obj.$name$) && "
+ "jspb.Message.setRepeatedWrapperField(\n"
+ " msg, $index$, goog.array.map(obj.$name$, function(i) {\n"
+ " return $fieldclass$.fromObject(i);\n"
+ " }));\n",
+ "name", JSObjectFieldName(field),
+ "index", JSFieldIndex(field),
+ "fieldclass", GetPath(options, field->message_type()));
+ }
+ } else {
+ printer->Print(
+ " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
+ " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
+ "name", JSObjectFieldName(field),
+ "index", JSFieldIndex(field),
+ "fieldclass", GetPath(options, field->message_type()));
+ }
+ } else {
+ // Simple (primitive) field.
+ printer->Print(
+ " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
+ "obj.$name$);\n",
+ "name", JSObjectFieldName(field),
+ "index", JSFieldIndex(field));
+ }
+}
+
+void Generator::GenerateClassClone(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ printer->Print(
+ "/**\n"
+ " * Creates a deep clone of this proto. No data is shared with the "
+ "original.\n"
+ " * @return {!$name$} The clone.\n"
+ " */\n"
+ "$name$.prototype.cloneMessage = function() {\n"
+ " return /** @type {!$name$} */ (jspb.Message.cloneMessage(this));\n"
+ "};\n\n\n",
+ "name", GetPath(options, desc));
+}
+
+void Generator::GenerateClassRegistration(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ // Register any extensions defined inside this message type.
+ for (int i = 0; i < desc->extension_count(); i++) {
+ const FieldDescriptor* extension = desc->extension(i);
+ if (ShouldGenerateExtension(extension)) {
+ GenerateExtension(options, printer, extension);
+ }
+ }
+
+}
+
+void Generator::GenerateClassFields(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ for (int i = 0; i < desc->field_count(); i++) {
+ if (!IgnoreField(desc->field(i))) {
+ GenerateClassField(options, printer, desc->field(i));
+ }
+ }
+}
+
+void Generator::GenerateClassField(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const {
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ "/**\n"
+ " * $fielddef$\n"
+ "$comment$"
+ " * @return {$type$}\n"
+ " */\n",
+ "fielddef", FieldDefinition(options, field),
+ "comment", FieldComments(field),
+ "type", JSFieldTypeAnnotation(options, field,
+ /* force_optional = */ false,
+ /* force_present = */ false,
+ /* singular_if_not_packed = */ false,
+ /* always_singular = */ false));
+ printer->Print(
+ "$class$.prototype.get$name$ = function() {\n"
+ " return /** @type{$type$} */ (\n"
+ " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
+ "$index$$required$));\n"
+ "};\n"
+ "\n"
+ "\n",
+ "class", GetPath(options, field->containing_type()),
+ "name", JSGetterName(field),
+ "type", JSFieldTypeAnnotation(options, field,
+ /* force_optional = */ false,
+ /* force_present = */ false,
+ /* singular_if_not_packed = */ false,
+ /* always_singular = */ false),
+ "rpt", (field->is_repeated() ? "Repeated" : ""),
+ "index", JSFieldIndex(field),
+ "wrapperclass", GetPath(options, field->message_type()),
+ "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
+ ", 1" : ""));
+ printer->Print(
+ "/** @param {$optionaltype$} value $returndoc$ */\n"
+ "$class$.prototype.set$name$ = function(value) {\n"
+ " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
+ "optionaltype",
+ JSFieldTypeAnnotation(options, field,
+ /* force_optional = */ true,
+ /* force_present = */ false,
+ /* singular_if_not_packed = */ false,
+ /* always_singular = */ false),
+ "returndoc", JSReturnDoc(options, field),
+ "class", GetPath(options, field->containing_type()),
+ "name", JSGetterName(field),
+ "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
+ "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
+
+ printer->Print(
+ "this, $index$$oneofgroup$, value);$returnvalue$\n"
+ "};\n"
+ "\n"
+ "\n",
+ "index", JSFieldIndex(field),
+ "oneofgroup", (field->containing_oneof() ?
+ (", " + JSOneofArray(options, field)) : ""),
+ "returnvalue", JSReturnClause(field));
+
+ printer->Print(
+ "$class$.prototype.clear$name$ = function() {\n"
+ " this.set$name$($clearedvalue$);$returnvalue$\n"
+ "};\n"
+ "\n"
+ "\n",
+ "class", GetPath(options, field->containing_type()),
+ "name", JSGetterName(field),
+ "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
+ "returnvalue", JSReturnClause(field));
+
+ } else {
+ string typed_annotation;
+
+ // Simple (primitive) field, either singular or repeated.
+ {
+ typed_annotation = JSFieldTypeAnnotation(options, field,
+ /* force_optional = */ false,
+ /* force_present = */ !HasFieldPresence(field),
+ /* singular_if_not_packed = */ false,
+ /* always_singular = */ false),
+ printer->Print(
+ "/**\n"
+ " * $fielddef$\n"
+ "$comment$"
+ " * @return {$type$}\n"
+ " */\n",
+ "fielddef", FieldDefinition(options, field),
+ "comment", FieldComments(field),
+ "type", typed_annotation);
+ }
+
+ printer->Print(
+ "$class$.prototype.get$name$ = function() {\n",
+ "class", GetPath(options, field->containing_type()),
+ "name", JSGetterName(field));
+
+ {
+ printer->Print(
+ " return /** @type {$type$} */ (",
+ "type", typed_annotation);
+ }
+
+ // For proto3 fields without presence, use special getters that will return
+ // defaults when the field is unset, possibly constructing a value if
+ // required.
+ if (!HasFieldPresence(field) && !field->is_repeated()) {
+ printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)",
+ "index", JSFieldIndex(field),
+ "default", Proto3PrimitiveFieldDefault(field));
+ } else {
+ if (field->has_default_value()) {
+ printer->Print("jspb.Message.getField(this, $index$) != null ? "
+ "jspb.Message.getField(this, $index$) : $defaultValue$",
+ "index", JSFieldIndex(field),
+ "defaultValue", JSFieldDefault(field));
+ } else {
+ printer->Print("jspb.Message.getField(this, $index$)",
+ "index", JSFieldIndex(field));
+ }
+ }
+
+ {
+ printer->Print(
+ ");\n"
+ "};\n"
+ "\n"
+ "\n");
+ }
+
+ {
+ printer->Print(
+ "/** @param {$optionaltype$} value $returndoc$ */\n",
+ "optionaltype",
+ JSFieldTypeAnnotation(options, field,
+ /* force_optional = */ true,
+ /* force_present = */ !HasFieldPresence(field),
+ /* singular_if_not_packed = */ false,
+ /* always_singular = */ false),
+ "returndoc", JSReturnDoc(options, field));
+ }
+
+ printer->Print(
+ "$class$.prototype.set$name$ = function(value) {\n"
+ " jspb.Message.set$oneoftag$Field(this, $index$",
+ "class", GetPath(options, field->containing_type()),
+ "name", JSGetterName(field),
+ "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
+ "index", JSFieldIndex(field));
+ printer->Print(
+ "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
+ "};\n"
+ "\n"
+ "\n",
+ "type", "",
+ "typeclose", "",
+ "oneofgroup",
+ (field->containing_oneof() ? (", " + JSOneofArray(options, field))
+ : ""),
+ "returnvalue", JSReturnClause(field), "rptvalueinit",
+ (field->is_repeated() ? " || []" : ""));
+
+
+ if (HasFieldPresence(field)) {
+ printer->Print(
+ "$class$.prototype.clear$name$ = function() {\n"
+ " jspb.Message.set$oneoftag$Field(this, $index$$oneofgroup$, ",
+ "class", GetPath(options, field->containing_type()),
+ "name", JSGetterName(field),
+ "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
+ "oneofgroup", (field->containing_oneof() ?
+ (", " + JSOneofArray(options, field)) : ""),
+ "index", JSFieldIndex(field));
+ printer->Print(
+ "$clearedvalue$);$returnvalue$\n"
+ "};\n"
+ "\n"
+ "\n",
+ "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
+ "returnvalue", JSReturnClause(field));
+ }
+ }
+}
+
+void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ if (IsExtendable(desc)) {
+ printer->Print(
+ "\n"
+ "/**\n"
+ " * The extensions registered with this message class. This is a "
+ "map of\n"
+ " * extension field number to fieldInfo object.\n"
+ " *\n"
+ " * For example:\n"
+ " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
+ "ctor: proto.example.MyMessage} }\n"
+ " *\n"
+ " * fieldName contains the JsCompiler renamed field name property "
+ "so that it\n"
+ " * works in OPTIMIZED mode.\n"
+ " *\n"
+ " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n"
+ " */\n"
+ "$class$.extensions = {};\n"
+ "\n",
+ "class", GetPath(options, desc));
+ }
+}
+
+
+void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ // TODO(cfallin): Handle lazy decoding when requested by field option and/or
+ // by default for 'bytes' fields and packed repeated fields.
+
+ printer->Print(
+ "/**\n"
+ " * Deserializes binary data (in protobuf wire format).\n"
+ " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
+ " * @return {!$class$}\n"
+ " */\n"
+ "$class$.deserializeBinary = function(bytes) {\n"
+ " var reader = new jspb.BinaryReader(bytes);\n"
+ " var msg = new $class$;\n"
+ " return $class$.deserializeBinaryFromReader(msg, reader);\n"
+ "};\n"
+ "\n"
+ "\n"
+ "/**\n"
+ " * Deserializes binary data (in protobuf wire format) from the\n"
+ " * given reader into the given message object.\n"
+ " * @param {!$class$} msg The message object to deserialize into.\n"
+ " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
+ " * @return {!$class$}\n"
+ " */\n"
+ "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
+ " while (reader.nextField()) {\n"
+ " if (reader.isEndGroup()) {\n"
+ " break;\n"
+ " }\n"
+ " var field = reader.getFieldNumber();\n"
+ " switch (field) {\n",
+ "class", GetPath(options, desc));
+
+ for (int i = 0; i < desc->field_count(); i++) {
+ GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
+ }
+
+ printer->Print(
+ " default:\n");
+ if (IsExtendable(desc)) {
+ printer->Print(
+ " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n"
+ " $class$.prototype.getExtension,\n"
+ " $class$.prototype.setExtension);\n"
+ " break;\n",
+ "extobj", JSExtensionsObjectName(options, desc),
+ "class", GetPath(options, desc));
+ } else {
+ printer->Print(
+ " reader.skipField();\n"
+ " break;\n");
+ }
+
+ printer->Print(
+ " }\n"
+ " }\n"
+ " return msg;\n"
+ "};\n"
+ "\n"
+ "\n");
+}
+
+void Generator::GenerateClassDeserializeBinaryField(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const {
+
+ printer->Print(" case $num$:\n",
+ "num", SimpleItoa(field->number()));
+
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ " var value = new $fieldclass$;\n"
+ " reader.read$msgOrGroup$($grpfield$value,"
+ "$fieldclass$.deserializeBinaryFromReader);\n",
+ "fieldclass", GetPath(options, field->message_type()),
+ "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ?
+ "Group" : "Message",
+ "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ?
+ (SimpleItoa(field->number()) + ", ") : "");
+ } else {
+ printer->Print(
+ " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n",
+ "fieldtype", JSFieldTypeAnnotation(options, field, false, true,
+ /* singular_if_not_packed = */ true,
+ /* always_singular = */ false),
+ "reader", JSBinaryReaderMethodName(field));
+ }
+
+ if (field->is_repeated() && !field->is_packed()) {
+ // Repeated fields receive a |value| one at at a time; append to array
+ // returned by get$name$().
+ printer->Print(
+ " msg.get$name$().push(value);\n",
+ "name", JSGetterName(field));
+ } else {
+ // Singular fields, and packed repeated fields, receive a |value| either as
+ // the field's value or as the array of all the field's values; set this as
+ // the field's value directly.
+ printer->Print(
+ " msg.set$name$(value);\n",
+ "name", JSGetterName(field));
+ }
+
+ printer->Print(" break;\n");
+}
+
+void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const {
+ printer->Print(
+ "/**\n"
+ " * Class method variant: serializes the given message to binary data\n"
+ " * (in protobuf wire format), writing to the given BinaryWriter.\n"
+ " * @param {!$class$} message\n"
+ " * @param {!jspb.BinaryWriter} writer\n"
+ " */\n"
+ "$class$.serializeBinaryToWriter = function(message, "
+ "writer) {\n"
+ " message.serializeBinaryToWriter(writer);\n"
+ "};\n"
+ "\n"
+ "\n"
+ "/**\n"
+ " * Serializes the message to binary data (in protobuf wire format).\n"
+ " * @return {!Uint8Array}\n"
+ " */\n"
+ "$class$.prototype.serializeBinary = function() {\n"
+ " var writer = new jspb.BinaryWriter();\n"
+ " this.serializeBinaryToWriter(writer);\n"
+ " return writer.getResultBuffer();\n"
+ "};\n"
+ "\n"
+ "\n"
+ "/**\n"
+ " * Serializes the message to binary data (in protobuf wire format),\n"
+ " * writing to the given BinaryWriter.\n"
+ " * @param {!jspb.BinaryWriter} writer\n"
+ " */\n"
+ "$class$.prototype.serializeBinaryToWriter = function (writer) {\n"
+ " var f = undefined;\n",
+ "class", GetPath(options, desc));
+
+ for (int i = 0; i < desc->field_count(); i++) {
+ GenerateClassSerializeBinaryField(options, printer, desc->field(i));
+ }
+
+ if (IsExtendable(desc)) {
+ printer->Print(
+ " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n"
+ " $class$.prototype.getExtension);\n",
+ "extobj", JSExtensionsObjectName(options, desc),
+ "class", GetPath(options, desc));
+ }
+
+ printer->Print(
+ "};\n"
+ "\n"
+ "\n");
+}
+
+void Generator::GenerateClassSerializeBinaryField(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const {
+ printer->Print(
+ " f = this.get$name$();\n",
+ "name", JSGetterName(field));
+
+ if (field->is_repeated()) {
+ printer->Print(
+ " if (f.length > 0) {\n");
+ } else {
+ if (HasFieldPresence(field)) {
+ printer->Print(
+ " if (f != null) {\n");
+ } else {
+ // No field presence: serialize onto the wire only if value is
+ // non-default. Defaults are documented here:
+ // https://goto.google.com/lhdfm
+ switch (field->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ case FieldDescriptor::CPPTYPE_INT64:
+ case FieldDescriptor::CPPTYPE_UINT32:
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ {
+ printer->Print(" if (f !== 0) {\n");
+ }
+ break;
+ }
+
+ case FieldDescriptor::CPPTYPE_ENUM:
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ printer->Print(
+ " if (f !== 0.0) {\n");
+ break;
+ case FieldDescriptor::CPPTYPE_BOOL:
+ printer->Print(
+ " if (f) {\n");
+ break;
+ case FieldDescriptor::CPPTYPE_STRING:
+ printer->Print(
+ " if (f.length > 0) {\n");
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+
+ printer->Print(
+ " writer.$writer$(\n"
+ " $index$,\n"
+ " f",
+ "writer", JSBinaryWriterMethodName(field),
+ "name", JSGetterName(field),
+ "index", SimpleItoa(field->number()));
+
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ ",\n"
+ " $submsg$.serializeBinaryToWriter\n",
+ "submsg", GetPath(options, field->message_type()));
+ } else {
+ printer->Print("\n");
+ }
+ printer->Print(
+ " );\n"
+ " }\n");
+}
+
+void Generator::GenerateEnum(const GeneratorOptions& options,
+ io::Printer* printer,
+ const EnumDescriptor* enumdesc) const {
+ printer->Print(
+ "/**\n"
+ " * @enum {number}\n"
+ " */\n"
+ "$name$ = {\n",
+ "name", GetPath(options, enumdesc));
+
+ for (int i = 0; i < enumdesc->value_count(); i++) {
+ const EnumValueDescriptor* value = enumdesc->value(i);
+ printer->Print(
+ " $name$: $value$$comma$\n",
+ "name", ToEnumCase(value->name()),
+ "value", SimpleItoa(value->number()),
+ "comma", (i == enumdesc->value_count() - 1) ? "" : ",");
+ }
+
+ printer->Print(
+ "};\n"
+ "\n");
+}
+
+void Generator::GenerateExtension(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const {
+ string extension_scope =
+ (field->extension_scope() ?
+ GetPath(options, field->extension_scope()) :
+ GetPath(options, field->file()));
+
+ printer->Print(
+ "\n"
+ "/**\n"
+ " * A tuple of {field number, class constructor} for the extension\n"
+ " * field named `$name$`.\n"
+ " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n"
+ " */\n"
+ "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
+ "name", JSObjectFieldName(field),
+ "class", extension_scope,
+ "extensionType", JSFieldTypeAnnotation(
+ options, field,
+ /* force_optional = */ false,
+ /* force_present = */ true,
+ /* singular_if_not_packed = */ false,
+ /* always_singular = */ false));
+ printer->Print(
+ " $index$,\n"
+ " {$name$: 0},\n"
+ " $ctor$,\n"
+ " /** @type {?function((boolean|undefined),!jspb.Message=): "
+ "!Object} */ (\n"
+ " $toObject$),\n"
+ " $repeated$",
+ "index", SimpleItoa(field->number()),
+ "name", JSObjectFieldName(field),
+ "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
+ GetPath(options, field->message_type()) : string("null")),
+ "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
+ (GetPath(options, field->message_type()) + ".toObject") :
+ string("null")),
+ "repeated", (field->is_repeated() ? "1" : "0"));
+
+ if (options.binary) {
+ printer->Print(
+ ",\n"
+ " jspb.BinaryReader.prototype.$binaryReaderFn$,\n"
+ " jspb.BinaryWriter.prototype.$binaryWriterFn$,\n"
+ " $binaryMessageSerializeFn$,\n"
+ " $binaryMessageDeserializeFn$,\n"
+ " $isPacked$);\n",
+ "binaryReaderFn", JSBinaryReaderMethodName(field),
+ "binaryWriterFn", JSBinaryWriterMethodName(field),
+ "binaryMessageSerializeFn",
+ (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
+ (GetPath(options, field->message_type()) +
+ ".serializeBinaryToWriter") : "null",
+ "binaryMessageDeserializeFn",
+ (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
+ (GetPath(options, field->message_type()) +
+ ".deserializeBinaryFromReader") : "null",
+ "isPacked", (field->is_packed() ? "true" : "false"));
+ } else {
+ printer->Print(");\n");
+ }
+
+ printer->Print(
+ "// This registers the extension field with the extended class, so that\n"
+ "// toObject() will function correctly.\n"
+ "$extendName$[$index$] = $class$.$name$;\n"
+ "\n",
+ "extendName", JSExtensionsObjectName(options, field->containing_type()),
+ "index", SimpleItoa(field->number()),
+ "class", extension_scope,
+ "name", JSObjectFieldName(field));
+}
+
+bool GeneratorOptions::ParseFromOptions(
+ const vector< pair< string, string > >& options,
+ string* error) {
+ for (int i = 0; i < options.size(); i++) {
+ if (options[i].first == "add_require_for_enums") {
+ if (options[i].second != "") {
+ *error = "Unexpected option value for add_require_for_enums";
+ return false;
+ }
+ add_require_for_enums = true;
+ } else if (options[i].first == "binary") {
+ if (options[i].second != "") {
+ *error = "Unexpected option value for binary";
+ return false;
+ }
+ binary = true;
+ } else if (options[i].first == "testonly") {
+ if (options[i].second != "") {
+ *error = "Unexpected option value for testonly";
+ return false;
+ }
+ testonly = true;
+ } else if (options[i].first == "error_on_name_conflict") {
+ if (options[i].second != "") {
+ *error = "Unexpected option value for error_on_name_conflict";
+ return false;
+ }
+ error_on_name_conflict = true;
+ } else if (options[i].first == "output_dir") {
+ output_dir = options[i].second;
+ } else if (options[i].first == "namespace_prefix") {
+ namespace_prefix = options[i].second;
+ } else if (options[i].first == "library") {
+ library = options[i].second;
+ } else {
+ // Assume any other option is an output directory, as long as it is a bare
+ // `key` rather than a `key=value` option.
+ if (options[i].second != "") {
+ *error = "Unknown option: " + options[i].first;
+ return false;
+ }
+ output_dir = options[i].first;
+ }
+ }
+
+ return true;
+}
+
+void Generator::GenerateFilesInDepOrder(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FileDescriptor*>& files) const {
+ // Build a std::set over all files so that the DFS can detect when it recurses
+ // into a dep not specified in the user's command line.
+ std::set<const FileDescriptor*> all_files(files.begin(), files.end());
+ // Track the in-progress set of files that have been generated already.
+ std::set<const FileDescriptor*> generated;
+ for (int i = 0; i < files.size(); i++) {
+ GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
+ }
+}
+
+void Generator::GenerateFileAndDeps(
+ const GeneratorOptions& options,
+ io::Printer* printer,
+ const FileDescriptor* root,
+ std::set<const FileDescriptor*>* all_files,
+ std::set<const FileDescriptor*>* generated) const {
+ // Skip if already generated.
+ if (generated->find(root) != generated->end()) {
+ return;
+ }
+ generated->insert(root);
+
+ // Generate all dependencies before this file's content.
+ for (int i = 0; i < root->dependency_count(); i++) {
+ const FileDescriptor* dep = root->dependency(i);
+ GenerateFileAndDeps(options, printer, dep, all_files, generated);
+ }
+
+ // Generate this file's content. Only generate if the file is part of the
+ // original set requested to be generated; i.e., don't take all transitive
+ // deps down to the roots.
+ if (all_files->find(root) != all_files->end()) {
+ GenerateClassesAndEnums(options, printer, root);
+ }
+}
+
+bool Generator::GenerateAll(const vector<const FileDescriptor*>& files,
+ const string& parameter,
+ GeneratorContext* context,
+ string* error) const {
+ vector< pair< string, string > > option_pairs;
+ ParseGeneratorParameter(parameter, &option_pairs);
+ GeneratorOptions options;
+ if (!options.ParseFromOptions(option_pairs, error)) {
+ return false;
+ }
+
+
+ // We're either generating a single library file with definitions for message
+ // and enum types in *all* FileDescriptor inputs, or we're generating a single
+ // file for each type.
+ if (options.library != "") {
+ string filename = options.output_dir + "/" + options.library + ".js";
+ google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
+ GOOGLE_CHECK(output.get());
+ io::Printer printer(output.get(), '$');
+
+ // Pull out all extensions -- we need these to generate all
+ // provides/requires.
+ vector<const FieldDescriptor*> extensions;
+ for (int i = 0; i < files.size(); i++) {
+ for (int j = 0; j < files[i]->extension_count(); j++) {
+ const FieldDescriptor* extension = files[i]->extension(j);
+ extensions.push_back(extension);
+ }
+ }
+
+ GenerateHeader(options, &printer);
+
+ std::set<string> provided;
+ FindProvides(options, &printer, files, &provided);
+ FindProvidesForFields(options, &printer, extensions, &provided);
+ GenerateProvides(options, &printer, &provided);
+ GenerateTestOnly(options, &printer);
+ GenerateRequires(options, &printer, files, &provided);
+
+ GenerateFilesInDepOrder(options, &printer, files);
+
+ for (int i = 0; i < extensions.size(); i++) {
+ if (ShouldGenerateExtension(extensions[i])) {
+ GenerateExtension(options, &printer, extensions[i]);
+ }
+ }
+
+ if (printer.failed()) {
+ return false;
+ }
+ } else {
+ // Collect all types, and print each type to a separate file. Pull out
+ // free-floating extensions while we make this pass.
+ map< string, vector<const FieldDescriptor*> > extensions_by_namespace;
+
+ // If we're generating code in file-per-type mode, avoid overwriting files
+ // by choosing the last descriptor that writes each filename and permitting
+ // only those to generate code.
+
+ // Current descriptor that will generate each filename, indexed by filename.
+ map<string, const void*> desc_by_filename;
+ // Set of descriptors allowed to generate files.
+ set<const void*> allowed_descs;
+
+ for (int i = 0; i < files.size(); i++) {
+ // Collect all (descriptor, filename) pairs.
+ map<const void*, string> descs_in_file;
+ for (int j = 0; j < files[i]->message_type_count(); j++) {
+ const Descriptor* desc = files[i]->message_type(j);
+ string filename =
+ options.output_dir + "/" + ToFileName(desc->name()) + ".js";
+ descs_in_file[desc] = filename;
+ }
+ for (int j = 0; j < files[i]->enum_type_count(); j++) {
+ const EnumDescriptor* desc = files[i]->enum_type(j);
+ string filename =
+ options.output_dir + "/" + ToFileName(desc->name()) + ".js";
+ descs_in_file[desc] = filename;
+ }
+
+ // For each (descriptor, filename) pair, update the
+ // descriptors-by-filename map, and if a previous descriptor was already
+ // writing the filename, remove it from the allowed-descriptors set.
+ map<const void*, string>::iterator it;
+ for (it = descs_in_file.begin(); it != descs_in_file.end(); ++it) {
+ const void* desc = it->first;
+ const string& filename = it->second;
+ if (desc_by_filename.find(filename) != desc_by_filename.end()) {
+ if (options.error_on_name_conflict) {
+ *error = "Name conflict: file name " + filename +
+ " would be generated by two descriptors";
+ return false;
+ }
+ allowed_descs.erase(desc_by_filename[filename]);
+ }
+ desc_by_filename[filename] = desc;
+ allowed_descs.insert(desc);
+ }
+ }
+
+ // Generate code.
+ for (int i = 0; i < files.size(); i++) {
+ const FileDescriptor* file = files[i];
+ for (int j = 0; j < file->message_type_count(); j++) {
+ const Descriptor* desc = file->message_type(j);
+ if (allowed_descs.find(desc) == allowed_descs.end()) {
+ continue;
+ }
+
+ string filename = options.output_dir + "/" +
+ ToFileName(desc->name()) + ".js";
+ google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
+ context->Open(filename));
+ GOOGLE_CHECK(output.get());
+ io::Printer printer(output.get(), '$');
+
+ GenerateHeader(options, &printer);
+
+ std::set<string> provided;
+ FindProvidesForMessage(options, &printer, desc, &provided);
+ GenerateProvides(options, &printer, &provided);
+ GenerateTestOnly(options, &printer);
+ GenerateRequires(options, &printer, desc, &provided);
+
+ GenerateClass(options, &printer, desc);
+
+ if (printer.failed()) {
+ return false;
+ }
+ }
+ for (int j = 0; j < file->enum_type_count(); j++) {
+ const EnumDescriptor* enumdesc = file->enum_type(j);
+ if (allowed_descs.find(enumdesc) == allowed_descs.end()) {
+ continue;
+ }
+
+ string filename = options.output_dir + "/" +
+ ToFileName(enumdesc->name()) + ".js";
+
+ google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
+ context->Open(filename));
+ GOOGLE_CHECK(output.get());
+ io::Printer printer(output.get(), '$');
+
+ GenerateHeader(options, &printer);
+
+ std::set<string> provided;
+ FindProvidesForEnum(options, &printer, enumdesc, &provided);
+ GenerateProvides(options, &printer, &provided);
+ GenerateTestOnly(options, &printer);
+
+ GenerateEnum(options, &printer, enumdesc);
+
+ if (printer.failed()) {
+ return false;
+ }
+ }
+ // Pull out all free-floating extensions and generate files for those too.
+ for (int j = 0; j < file->extension_count(); j++) {
+ const FieldDescriptor* extension = file->extension(j);
+ extensions_by_namespace[GetPath(options, files[i])]
+ .push_back(extension);
+ }
+ }
+
+ // Generate extensions in separate files.
+ map< string, vector<const FieldDescriptor*> >::iterator it;
+ for (it = extensions_by_namespace.begin();
+ it != extensions_by_namespace.end();
+ ++it) {
+ string filename = options.output_dir + "/" +
+ ToFileName(it->first) + ".js";
+
+ google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
+ context->Open(filename));
+ GOOGLE_CHECK(output.get());
+ io::Printer printer(output.get(), '$');
+
+ GenerateHeader(options, &printer);
+
+ std::set<string> provided;
+ FindProvidesForFields(options, &printer, it->second, &provided);
+ GenerateProvides(options, &printer, &provided);
+ GenerateTestOnly(options, &printer);
+ GenerateRequires(options, &printer, it->second, &provided);
+
+ for (int j = 0; j < it->second.size(); j++) {
+ if (ShouldGenerateExtension(it->second[j])) {
+ GenerateExtension(options, &printer, it->second[j]);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace js
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/js/js_generator.h b/src/google/protobuf/compiler/js/js_generator.h
new file mode 100755
index 00000000..db2dceb3
--- /dev/null
+++ b/src/google/protobuf/compiler/js/js_generator.h
@@ -0,0 +1,265 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__
+#define GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__
+
+#include <string>
+#include <set>
+
+#include <google/protobuf/compiler/code_generator.h>
+
+namespace google {
+namespace protobuf {
+
+class Descriptor;
+class EnumDescriptor;
+class FieldDescriptor;
+class OneofDescriptor;
+class FileDescriptor;
+
+namespace io { class Printer; }
+
+namespace compiler {
+namespace js {
+
+struct GeneratorOptions {
+ // Add a `goog.requires()` call for each enum type used. If not set, a forward
+ // declaration with `goog.forwardDeclare` is produced instead.
+ bool add_require_for_enums;
+ // Set this as a test-only module via `goog.setTestOnly();`.
+ bool testonly;
+ // Output path.
+ string output_dir;
+ // Namespace prefix.
+ string namespace_prefix;
+ // Create a library with name <name>_lib.js rather than a separate .js file
+ // per type?
+ string library;
+ // Error if there are two types that would generate the same output file?
+ bool error_on_name_conflict;
+ // Enable binary-format support?
+ bool binary;
+
+ GeneratorOptions()
+ : add_require_for_enums(false),
+ testonly(false),
+ output_dir("."),
+ namespace_prefix(""),
+ library(""),
+ error_on_name_conflict(false),
+ binary(false) {}
+
+ bool ParseFromOptions(
+ const vector< pair< string, string > >& options,
+ string* error);
+};
+
+class LIBPROTOC_EXPORT Generator : public CodeGenerator {
+ public:
+ Generator() {}
+ virtual ~Generator() {}
+
+ virtual bool Generate(const FileDescriptor* file,
+ const string& parameter,
+ GeneratorContext* context,
+ string* error) const {
+ *error = "Unimplemented Generate() method. Call GenerateAll() instead.";
+ return false;
+ }
+
+ virtual bool HasGenerateAll() const { return true; }
+
+ virtual bool GenerateAll(const vector<const FileDescriptor*>& files,
+ const string& parameter,
+ GeneratorContext* context,
+ string* error) const;
+
+ private:
+ void GenerateHeader(const GeneratorOptions& options,
+ io::Printer* printer) const;
+
+ // Generate goog.provides() calls.
+ void FindProvides(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FileDescriptor*>& file,
+ std::set<string>* provided) const;
+ void FindProvidesForMessage(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc,
+ std::set<string>* provided) const;
+ void FindProvidesForEnum(const GeneratorOptions& options,
+ io::Printer* printer,
+ const EnumDescriptor* enumdesc,
+ std::set<string>* provided) const;
+ // For extension fields at file scope.
+ void FindProvidesForFields(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FieldDescriptor*>& fields,
+ std::set<string>* provided) const;
+ // Print the goog.provides() found by the methods above.
+ void GenerateProvides(const GeneratorOptions& options,
+ io::Printer* printer,
+ std::set<string>* provided) const;
+
+ // Generate goog.setTestOnly() if indicated.
+ void GenerateTestOnly(const GeneratorOptions& options,
+ io::Printer* printer) const;
+
+ // Generate goog.requires() calls.
+ void GenerateRequires(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FileDescriptor*>& file,
+ std::set<string>* provided) const;
+ void GenerateRequires(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc,
+ std::set<string>* provided) const;
+ // For extension fields at file scope.
+ void GenerateRequires(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FieldDescriptor*>& fields,
+ std::set<string>* provided) const;
+ void GenerateRequiresImpl(const GeneratorOptions& options,
+ io::Printer* printer,
+ std::set<string>* required,
+ std::set<string>* forwards,
+ std::set<string>* provided,
+ bool require_jspb,
+ bool require_extension) const;
+ void FindRequiresForMessage(const GeneratorOptions& options,
+ const Descriptor* desc,
+ std::set<string>* required,
+ std::set<string>* forwards,
+ bool* have_message) const;
+ void FindRequiresForField(const GeneratorOptions& options,
+ const FieldDescriptor* field,
+ std::set<string>* required,
+ std::set<string>* forwards) const;
+ void FindRequiresForExtension(const GeneratorOptions& options,
+ const FieldDescriptor* field,
+ std::set<string>* required,
+ std::set<string>* forwards) const;
+
+ // Generate definitions for all message classes and enums in all files,
+ // processing the files in dependence order.
+ void GenerateFilesInDepOrder(const GeneratorOptions& options,
+ io::Printer* printer,
+ const vector<const FileDescriptor*>& file) const;
+ // Helper for above.
+ void GenerateFileAndDeps(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FileDescriptor* root,
+ std::set<const FileDescriptor*>* all_files,
+ std::set<const FileDescriptor*>* generated) const;
+
+ // Generate definitions for all message classes and enums.
+ void GenerateClassesAndEnums(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FileDescriptor* file) const;
+
+ // Generate definition for one class.
+ void GenerateClass(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassConstructor(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassFieldInfo(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassXid(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateOneofCaseDefinition(const GeneratorOptions& options,
+ io::Printer* printer,
+ const OneofDescriptor* oneof) const;
+ void GenerateClassToObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassFieldToObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const;
+ void GenerateClassFromObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassFieldFromObject(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const;
+ void GenerateClassClone(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassRegistration(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassFields(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassField(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* desc) const;
+ void GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassDeserialize(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassDeserializeBinary(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassDeserializeBinaryField(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const;
+ void GenerateClassSerializeBinary(const GeneratorOptions& options,
+ io::Printer* printer,
+ const Descriptor* desc) const;
+ void GenerateClassSerializeBinaryField(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const;
+
+ // Generate definition for one enum.
+ void GenerateEnum(const GeneratorOptions& options,
+ io::Printer* printer,
+ const EnumDescriptor* enumdesc) const;
+
+ // Generate an extension definition.
+ void GenerateExtension(const GeneratorOptions& options,
+ io::Printer* printer,
+ const FieldDescriptor* field) const;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator);
+};
+
+} // namespace js
+} // namespace compiler
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__
diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc
index 584e5a40..97df536e 100644
--- a/src/google/protobuf/compiler/main.cc
+++ b/src/google/protobuf/compiler/main.cc
@@ -38,6 +38,7 @@
#include <google/protobuf/compiler/ruby/ruby_generator.h>
#include <google/protobuf/compiler/csharp/csharp_generator.h>
#include <google/protobuf/compiler/objectivec/objectivec_generator.h>
+#include <google/protobuf/compiler/js/js_generator.h>
int main(int argc, char* argv[]) {
@@ -80,5 +81,10 @@ int main(int argc, char* argv[]) {
cli.RegisterGenerator("--objc_out", &objc_generator,
"Generate Objective C header and source.");
+ // JavaScript
+ google::protobuf::compiler::js::Generator js_generator;
+ cli.RegisterGenerator("--js_out", &js_generator,
+ "Generate JavaScript source.");
+
return cli.Run(argc, argv);
}
diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc
index 98261431..121d917b 100644
--- a/src/google/protobuf/compiler/mock_code_generator.cc
+++ b/src/google/protobuf/compiler/mock_code_generator.cc
@@ -147,6 +147,12 @@ bool MockCodeGenerator::Generate(
std::cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: "
<< has_source_code_info << "." << std::endl;
abort();
+ } else if (command == "HasJsonName") {
+ FieldDescriptorProto field_descriptor_proto;
+ file->message_type(i)->field(0)->CopyTo(&field_descriptor_proto);
+ std::cerr << "Saw json_name: "
+ << field_descriptor_proto.has_json_name() << std::endl;
+ abort();
} else {
GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
}
diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
index 184a84a3..228c66f0 100644
--- a/src/google/protobuf/compiler/objectivec/objectivec_file.cc
+++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc
@@ -95,7 +95,7 @@ void FileGenerator::GenerateHeader(io::Printer *printer) {
// code is being compiled with.
printer->Print(
"#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != $protoc_gen_objc_version$\n"
- "#error This file was generated by a different version of protoc-gen-objc which is incompatible with your Protocol Buffer sources.\n"
+ "#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources.\n"
"#endif\n"
"\n",
"protoc_gen_objc_version",
diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc
index a389a4fc..90ded4de 100644
--- a/src/google/protobuf/compiler/parser.cc
+++ b/src/google/protobuf/compiler/parser.cc
@@ -458,6 +458,61 @@ void Parser::SkipRestOfBlock() {
// ===================================================================
+bool Parser::ValidateEnum(const EnumDescriptorProto* proto) {
+ bool has_allow_alias = false;
+ bool allow_alias = false;
+
+ for (int i = 0; i < proto->options().uninterpreted_option_size(); i++) {
+ const UninterpretedOption option = proto->options().uninterpreted_option(i);
+ if (option.name_size() > 1) {
+ continue;
+ }
+ if (!option.name(0).is_extension() &&
+ option.name(0).name_part() == "allow_alias") {
+ has_allow_alias = true;
+ if (option.identifier_value() == "true") {
+ allow_alias = true;
+ }
+ break;
+ }
+ }
+
+ if (has_allow_alias && !allow_alias) {
+ string error =
+ "\"" + proto->name() +
+ "\" declares 'option allow_alias = false;' which has no effect. "
+ "Please remove the declaration.";
+ // This needlessly clutters declarations with nops.
+ AddError(error);
+ return false;
+ }
+
+ set<int> used_values;
+ bool has_duplicates = false;
+ for (int i = 0; i < proto->value_size(); ++i) {
+ const EnumValueDescriptorProto enum_value = proto->value(i);
+ if (used_values.find(enum_value.number()) != used_values.end()) {
+ has_duplicates = true;
+ break;
+ } else {
+ used_values.insert(enum_value.number());
+ }
+ }
+ if (allow_alias && !has_duplicates) {
+ string error =
+ "\"" + proto->name() +
+ "\" declares support for enum aliases but no enum values share field "
+ "numbers. Please remove the unnecessary 'option allow_alias = true;' "
+ "declaration.";
+ // Generate an error if an enum declares support for duplicate enum values
+ // and does not use it protect future authors.
+ AddError(error);
+ return false;
+ }
+
+ return true;
+}
+
bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
input_ = input;
had_errors_ = false;
@@ -489,9 +544,9 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
// Store the syntax into the file.
if (file != NULL) file->set_syntax(syntax_identifier_);
} else if (!stop_after_syntax_identifier_) {
- GOOGLE_LOG(WARNING) << "No syntax specified for the proto file. "
- << "Please use 'syntax = \"proto2\";' or "
- << "'syntax = \"proto3\";' to specify a syntax "
+ GOOGLE_LOG(WARNING) << "No syntax specified for the proto file: "
+ << file->name() << ". Please use 'syntax = \"proto2\";' "
+ << "or 'syntax = \"proto3\";' to specify a syntax "
<< "version. (Defaulted to proto2 syntax.)";
syntax_identifier_ = "proto2";
}
@@ -1627,6 +1682,9 @@ bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
}
DO(ParseEnumBlock(enum_type, enum_location, containing_file));
+
+ DO(ValidateEnum(enum_type));
+
return true;
}
diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h
index 3ba1e170..2c561c23 100644
--- a/src/google/protobuf/compiler/parser.h
+++ b/src/google/protobuf/compiler/parser.h
@@ -498,6 +498,8 @@ class LIBPROTOBUF_EXPORT Parser {
}
+ bool ValidateEnum(const EnumDescriptorProto* proto);
+
// =================================================================
io::Tokenizer* input_;
diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc
index 0d729e0b..1d623dd9 100644
--- a/src/google/protobuf/compiler/parser_unittest.cc
+++ b/src/google/protobuf/compiler/parser_unittest.cc
@@ -1170,6 +1170,29 @@ TEST_F(ParseErrorTest, EnumValueOutOfRange) {
"4:19: Integer out of range.\n");
}
+TEST_F(ParseErrorTest, EnumAllowAliasFalse) {
+ ExpectHasErrors(
+ "enum Foo {\n"
+ " option allow_alias = false;\n"
+ " BAR = 1;\n"
+ " BAZ = 2;\n"
+ "}\n",
+ "5:0: \"Foo\" declares 'option allow_alias = false;' which has no effect. "
+ "Please remove the declaration.\n");
+}
+
+TEST_F(ParseErrorTest, UnnecessaryEnumAllowAlias) {
+ ExpectHasErrors(
+ "enum Foo {\n"
+ " option allow_alias = true;\n"
+ " BAR = 1;\n"
+ " BAZ = 2;\n"
+ "}\n",
+ "5:0: \"Foo\" declares support for enum aliases but no enum values share "
+ "field numbers. Please remove the unnecessary 'option allow_alias = true;' "
+ "declaration.\n");
+}
+
TEST_F(ParseErrorTest, DefaultValueMissing) {
ExpectHasErrors(
"message TestMessage {\n"
@@ -1839,6 +1862,8 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) {
"// Detached comment before TestMessage1.\n"
"\n"
"// Message comment.\n"
+ "//\n"
+ "// More detail in message comment.\n"
"message TestMessage1 {\n"
"\n"
" // Detached comment before foo.\n"
@@ -1890,11 +1915,6 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) {
pool_.BuildFileCollectingErrors(parsed_desc, &collector);
ASSERT_TRUE(descriptor != NULL);
- DebugStringOptions debug_string_options;
- debug_string_options.include_comments = true;
- const string debug_string =
- descriptor->DebugStringWithOptions(debug_string_options);
-
// Ensure that each of the comments appears somewhere in the DebugString().
// We don't test the exact comment placement or formatting, because we do not
// want to be too fragile here.
@@ -1905,6 +1925,7 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) {
"Package comment.",
"Detached comment before TestMessage1.",
"Message comment.",
+ "More detail in message comment.",
"Detached comment before foo.",
"Field comment",
"Detached comment before NestedMessage.",
@@ -1919,11 +1940,28 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) {
"RPC comment",
};
- for (int i = 0; i < GOOGLE_ARRAYSIZE(expected_comments); ++i) {
- string::size_type found_pos = debug_string.find(expected_comments[i]);
- EXPECT_TRUE(found_pos != string::npos)
- << "\"" << expected_comments[i] << "\" not found.";
+ DebugStringOptions debug_string_options;
+ debug_string_options.include_comments = true;
+
+ {
+ const string debug_string =
+ descriptor->DebugStringWithOptions(debug_string_options);
+
+ for (int i = 0; i < GOOGLE_ARRAYSIZE(expected_comments); ++i) {
+ string::size_type found_pos = debug_string.find(expected_comments[i]);
+ EXPECT_TRUE(found_pos != string::npos)
+ << "\"" << expected_comments[i] << "\" not found.";
+ }
+
+ // Result of DebugStringWithOptions should be parseable.
+ 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_)
+ << "Failed to parse:\n" << debug_string;
}
+
}
TEST_F(ParseDescriptorDebugTest, TestMaps) {
diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc
index 266c2975..a2da8eee 100644
--- a/src/google/protobuf/compiler/plugin.pb.cc
+++ b/src/google/protobuf/compiler/plugin.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/compiler/plugin.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/compiler/plugin.pb.h"
+#include <google/protobuf/compiler/plugin.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h
index ab79bdae..0a03e979 100644
--- a/src/google/protobuf/compiler/plugin.pb.h
+++ b/src/google/protobuf/compiler/plugin.pb.h
@@ -27,7 +27,7 @@
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/unknown_field_set.h>
-#include "google/protobuf/descriptor.pb.h"
+#include <google/protobuf/descriptor.pb.h>
// @@protoc_insertion_point(includes)
namespace google {
diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc
index 85429924..a30ac305 100644
--- a/src/google/protobuf/compiler/subprocess.cc
+++ b/src/google/protobuf/compiler/subprocess.cc
@@ -47,6 +47,7 @@
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/substitute.h>
+
namespace google {
namespace protobuf {
namespace compiler {
diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc
index 9a1c6fe8..78a34617 100644
--- a/src/google/protobuf/descriptor.cc
+++ b/src/google/protobuf/descriptor.cc
@@ -560,7 +560,7 @@ class FileDescriptorTables {
~FileDescriptorTables();
// Empty table, used with placeholder files.
- static const FileDescriptorTables kEmpty;
+ inline static const FileDescriptorTables& GetEmptyInstance();
// -----------------------------------------------------------------
// Finding items.
@@ -665,7 +665,32 @@ FileDescriptorTables::FileDescriptorTables()
FileDescriptorTables::~FileDescriptorTables() {}
-const FileDescriptorTables FileDescriptorTables::kEmpty;
+namespace {
+
+FileDescriptorTables* file_descriptor_tables_ = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(file_descriptor_tables_once_init_);
+
+void DeleteFileDescriptorTables() {
+ delete file_descriptor_tables_;
+ file_descriptor_tables_ = NULL;
+}
+
+void InitFileDescriptorTables() {
+ file_descriptor_tables_ = new FileDescriptorTables();
+ internal::OnShutdown(&DeleteFileDescriptorTables);
+}
+
+inline void InitFileDescriptorTablesOnce() {
+ ::google::protobuf::GoogleOnceInit(
+ &file_descriptor_tables_once_init_, &InitFileDescriptorTables);
+}
+
+} // anonymous namespace
+
+inline const FileDescriptorTables& FileDescriptorTables::GetEmptyInstance() {
+ InitFileDescriptorTablesOnce();
+ return *file_descriptor_tables_;
+}
void DescriptorPool::Tables::AddCheckpoint() {
checkpoints_.push_back(CheckPoint(this));
@@ -2050,6 +2075,7 @@ class SourceLocationCommentPrinter {
}
private:
+
bool have_source_loc_;
SourceLocation source_loc_;
DebugStringOptions options_;
@@ -2951,7 +2977,8 @@ class DescriptorBuilder {
const ServiceDescriptor* parent,
MethodDescriptor* result);
- void LogUnusedDependency(const FileDescriptor* result);
+ void LogUnusedDependency(const FileDescriptorProto& proto,
+ const FileDescriptor* result);
// Must be run only after building.
//
@@ -3535,7 +3562,7 @@ FileDescriptor* DescriptorBuilder::NewPlaceholderFile(
placeholder->package_ = &internal::GetEmptyString();
placeholder->pool_ = pool_;
placeholder->options_ = &FileOptions::default_instance();
- placeholder->tables_ = &FileDescriptorTables::kEmpty;
+ placeholder->tables_ = &FileDescriptorTables::GetEmptyInstance();
placeholder->source_code_info_ = &SourceCodeInfo::default_instance();
placeholder->is_placeholder_ = true;
placeholder->syntax_ = FileDescriptor::SYNTAX_PROTO2;
@@ -3996,7 +4023,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile(
if (!unused_dependency_.empty()) {
- LogUnusedDependency(result);
+ LogUnusedDependency(proto, result);
}
if (had_errors_) {
@@ -4143,6 +4170,7 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto,
}
}
+
void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
const Descriptor* parent,
FieldDescriptor* result,
@@ -4248,8 +4276,8 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
} else if (proto.default_value() == "nan") {
result->default_value_float_ = numeric_limits<float>::quiet_NaN();
} else {
- result->default_value_float_ =
- io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos);
+ result->default_value_float_ = io::SafeDoubleToFloat(
+ io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos));
}
break;
case FieldDescriptor::CPPTYPE_DOUBLE:
@@ -4421,6 +4449,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
AllocateOptions(proto.options(), result);
}
+
AddSymbol(result->full_name(), parent, result->name(),
proto, Symbol(result));
}
@@ -5533,11 +5562,19 @@ bool DescriptorBuilder::OptionInterpreter::InterpretOptions(
// UnknownFieldSet and wait there until the message is parsed by something
// that does know about the options.
string buf;
- options->AppendToString(&buf);
- GOOGLE_CHECK(options->ParseFromString(buf))
+ GOOGLE_CHECK(options->AppendPartialToString(&buf))
+ << "Protocol message could not be serialized.";
+ GOOGLE_CHECK(options->ParsePartialFromString(buf))
<< "Protocol message serialized itself in invalid fashion.";
+ if (!options->IsInitialized()) {
+ builder_->AddWarning(
+ options_to_interpret->element_name, *original_options,
+ DescriptorPool::ErrorCollector::OTHER,
+ "Options could not be fully parsed using the proto descriptors "
+ "compiled into this binary. Missing required fields: " +
+ options->InitializationErrorString());
+ }
}
-
return !failed;
}
@@ -6191,7 +6228,8 @@ void DescriptorBuilder::OptionInterpreter::SetUInt64(int number, uint64 value,
}
}
-void DescriptorBuilder::LogUnusedDependency(const FileDescriptor* result) {
+void DescriptorBuilder::LogUnusedDependency(const FileDescriptorProto& proto,
+ const FileDescriptor* result) {
if (!unused_dependency_.empty()) {
std::set<string> annotation_extensions;
@@ -6217,9 +6255,9 @@ void DescriptorBuilder::LogUnusedDependency(const FileDescriptor* result) {
}
// Log warnings for unused imported files.
if (i == (*it)->extension_count()) {
- GOOGLE_LOG(WARNING) << "Warning: Unused import: \"" << result->name()
- << "\" imports \"" << (*it)->name()
- << "\" which is not used.";
+ string error_message = "Import " + (*it)->name() + " but not used.";
+ AddWarning((*it)->name(), proto, DescriptorPool::ErrorCollector::OTHER,
+ error_message);
}
}
}
diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h
index e7e8c6af..7e3a7496 100644
--- a/src/google/protobuf/descriptor.h
+++ b/src/google/protobuf/descriptor.h
@@ -1645,7 +1645,7 @@ PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, reserved_range,
const Descriptor::ReservedRange*)
PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_name_count, int)
-PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions);
+PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions)
PROTOBUF_DEFINE_ACCESSOR(Descriptor, is_placeholder, bool)
PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, name)
@@ -1690,7 +1690,7 @@ 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_OPTIONS_ACCESSOR(EnumDescriptor, EnumOptions)
PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, is_placeholder, bool)
PROTOBUF_DEFINE_STRING_ACCESSOR(EnumValueDescriptor, name)
@@ -1705,14 +1705,14 @@ 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_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_OPTIONS_ACCESSOR(MethodDescriptor, MethodOptions)
PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, client_streaming, bool)
PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, server_streaming, bool)
@@ -1726,7 +1726,7 @@ 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_OPTIONS_ACCESSOR(FileDescriptor, FileOptions)
PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, is_placeholder, bool)
PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, message_type, const Descriptor*)
diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc
index ad53c03f..ff0cfcf8 100644
--- a/src/google/protobuf/descriptor.pb.cc
+++ b/src/google/protobuf/descriptor.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/descriptor.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/descriptor.pb.h"
+#include <google/protobuf/descriptor.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -92,6 +93,12 @@ const ::google::protobuf::internal::GeneratedMessageReflection*
const ::google::protobuf::Descriptor* SourceCodeInfo_Location_descriptor_ = NULL;
const ::google::protobuf::internal::GeneratedMessageReflection*
SourceCodeInfo_Location_reflection_ = NULL;
+const ::google::protobuf::Descriptor* GeneratedCodeInfo_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+ GeneratedCodeInfo_reflection_ = NULL;
+const ::google::protobuf::Descriptor* GeneratedCodeInfo_Annotation_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+ GeneratedCodeInfo_Annotation_reflection_ = NULL;
} // namespace
@@ -520,6 +527,39 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() {
sizeof(SourceCodeInfo_Location),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(SourceCodeInfo_Location, _internal_metadata_),
-1);
+ GeneratedCodeInfo_descriptor_ = file->message_type(18);
+ static const int GeneratedCodeInfo_offsets_[1] = {
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo, annotation_),
+ };
+ GeneratedCodeInfo_reflection_ =
+ ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
+ GeneratedCodeInfo_descriptor_,
+ GeneratedCodeInfo::default_instance_,
+ GeneratedCodeInfo_offsets_,
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo, _has_bits_[0]),
+ -1,
+ -1,
+ sizeof(GeneratedCodeInfo),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo, _internal_metadata_),
+ -1);
+ GeneratedCodeInfo_Annotation_descriptor_ = GeneratedCodeInfo_descriptor_->nested_type(0);
+ static const int GeneratedCodeInfo_Annotation_offsets_[4] = {
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo_Annotation, path_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo_Annotation, source_file_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo_Annotation, begin_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo_Annotation, end_),
+ };
+ GeneratedCodeInfo_Annotation_reflection_ =
+ ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
+ GeneratedCodeInfo_Annotation_descriptor_,
+ GeneratedCodeInfo_Annotation::default_instance_,
+ GeneratedCodeInfo_Annotation_offsets_,
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo_Annotation, _has_bits_[0]),
+ -1,
+ -1,
+ sizeof(GeneratedCodeInfo_Annotation),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(GeneratedCodeInfo_Annotation, _internal_metadata_),
+ -1);
}
namespace {
@@ -576,6 +616,10 @@ void protobuf_RegisterTypes(const ::std::string&) {
SourceCodeInfo_descriptor_, &SourceCodeInfo::default_instance());
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
SourceCodeInfo_Location_descriptor_, &SourceCodeInfo_Location::default_instance());
+ ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+ GeneratedCodeInfo_descriptor_, &GeneratedCodeInfo::default_instance());
+ ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+ GeneratedCodeInfo_Annotation_descriptor_, &GeneratedCodeInfo_Annotation::default_instance());
}
} // namespace
@@ -625,6 +669,10 @@ void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto() {
delete SourceCodeInfo_reflection_;
delete SourceCodeInfo_Location::default_instance_;
delete SourceCodeInfo_Location_reflection_;
+ delete GeneratedCodeInfo::default_instance_;
+ delete GeneratedCodeInfo_reflection_;
+ delete GeneratedCodeInfo_Annotation::default_instance_;
+ delete GeneratedCodeInfo_Annotation_reflection_;
}
void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
@@ -698,7 +746,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
"_type\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google.pr"
"otobuf.MethodOptions\022\037\n\020client_streaming"
"\030\005 \001(\010:\005false\022\037\n\020server_streaming\030\006 \001(\010:"
- "\005false\"\252\005\n\013FileOptions\022\024\n\014java_package\030\001"
+ "\005false\"\256\005\n\013FileOptions\022\024\n\014java_package\030\001"
" \001(\t\022\034\n\024java_outer_classname\030\010 \001(\t\022\"\n\023ja"
"va_multiple_files\030\n \001(\010:\005false\022,\n\035java_g"
"enerate_equals_and_hash\030\024 \001(\010:\005false\022%\n\026"
@@ -710,56 +758,60 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
"e\022\"\n\023py_generic_services\030\022 \001(\010:\005false\022\031\n"
"\ndeprecated\030\027 \001(\010:\005false\022\037\n\020cc_enable_ar"
"enas\030\037 \001(\010:\005false\022\031\n\021objc_class_prefix\030$"
- " \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022\'\n\037javana"
- "no_use_deprecated_package\030& \001(\010\022C\n\024unint"
- "erpreted_option\030\347\007 \003(\0132$.google.protobuf"
- ".UninterpretedOption\":\n\014OptimizeMode\022\t\n\005"
- "SPEED\020\001\022\r\n\tCODE_SIZE\020\002\022\020\n\014LITE_RUNTIME\020\003"
- "*\t\010\350\007\020\200\200\200\200\002\"\346\001\n\016MessageOptions\022&\n\027messag"
- "e_set_wire_format\030\001 \001(\010:\005false\022.\n\037no_sta"
- "ndard_descriptor_accessor\030\002 \001(\010:\005false\022\031"
- "\n\ndeprecated\030\003 \001(\010:\005false\022\021\n\tmap_entry\030\007"
- " \001(\010\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.go"
- "ogle.protobuf.UninterpretedOption*\t\010\350\007\020\200"
- "\200\200\200\002\"\230\003\n\014FieldOptions\022:\n\005ctype\030\001 \001(\0162#.g"
- "oogle.protobuf.FieldOptions.CType:\006STRIN"
- "G\022\016\n\006packed\030\002 \001(\010\022\?\n\006jstype\030\006 \001(\0162$.goog"
- "le.protobuf.FieldOptions.JSType:\tJS_NORM"
- "AL\022\023\n\004lazy\030\005 \001(\010:\005false\022\031\n\ndeprecated\030\003 "
- "\001(\010:\005false\022\023\n\004weak\030\n \001(\010:\005false\022C\n\024unint"
- "erpreted_option\030\347\007 \003(\0132$.google.protobuf"
- ".UninterpretedOption\"/\n\005CType\022\n\n\006STRING\020"
- "\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"5\n\006JSType\022"
- "\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020\001\022\r\n\tJS_NUMB"
- "ER\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013EnumOptions\022\023\n\013allow"
- "_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005false\022"
- "C\n\024uninterpreted_option\030\347\007 \003(\0132$.google."
- "protobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\""
- "}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001 \001(\010:"
- "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$."
- "google.protobuf.UninterpretedOption*\t\010\350\007"
- "\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndeprecated\030!"
- " \001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003"
- "(\0132$.google.protobuf.UninterpretedOption"
- "*\t\010\350\007\020\200\200\200\200\002\"z\n\rMethodOptions\022\031\n\ndeprecat"
+ " \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022+\n\037javana"
+ "no_use_deprecated_package\030& \001(\010B\002\030\001\022C\n\024u"
+ "ninterpreted_option\030\347\007 \003(\0132$.google.prot"
+ "obuf.UninterpretedOption\":\n\014OptimizeMode"
+ "\022\t\n\005SPEED\020\001\022\r\n\tCODE_SIZE\020\002\022\020\n\014LITE_RUNTI"
+ "ME\020\003*\t\010\350\007\020\200\200\200\200\002\"\346\001\n\016MessageOptions\022&\n\027me"
+ "ssage_set_wire_format\030\001 \001(\010:\005false\022.\n\037no"
+ "_standard_descriptor_accessor\030\002 \001(\010:\005fal"
+ "se\022\031\n\ndeprecated\030\003 \001(\010:\005false\022\021\n\tmap_ent"
+ "ry\030\007 \001(\010\022C\n\024uninterpreted_option\030\347\007 \003(\0132"
+ "$.google.protobuf.UninterpretedOption*\t\010"
+ "\350\007\020\200\200\200\200\002\"\230\003\n\014FieldOptions\022:\n\005ctype\030\001 \001(\016"
+ "2#.google.protobuf.FieldOptions.CType:\006S"
+ "TRING\022\016\n\006packed\030\002 \001(\010\022\?\n\006jstype\030\006 \001(\0162$."
+ "google.protobuf.FieldOptions.JSType:\tJS_"
+ "NORMAL\022\023\n\004lazy\030\005 \001(\010:\005false\022\031\n\ndeprecate"
+ "d\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:\005false\022C\n\024u"
+ "ninterpreted_option\030\347\007 \003(\0132$.google.prot"
+ "obuf.UninterpretedOption\"/\n\005CType\022\n\n\006STR"
+ "ING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"5\n\006JST"
+ "ype\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020\001\022\r\n\tJS_"
+ "NUMBER\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013EnumOptions\022\023\n\013a"
+ "llow_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005fa"
+ "lse\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.goo"
+ "gle.protobuf.UninterpretedOption*\t\010\350\007\020\200\200"
+ "\200\200\002\"}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001 "
+ "\001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003("
+ "\0132$.google.protobuf.UninterpretedOption*"
+ "\t\010\350\007\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndeprecat"
"ed\030! \001(\010:\005false\022C\n\024uninterpreted_option\030"
"\347\007 \003(\0132$.google.protobuf.UninterpretedOp"
- "tion*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023UninterpretedOption\022"
- ";\n\004name\030\002 \003(\0132-.google.protobuf.Uninterp"
- "retedOption.NamePart\022\030\n\020identifier_value"
- "\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022\032\n\022ne"
- "gative_int_value\030\005 \001(\003\022\024\n\014double_value\030\006"
- " \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027\n\017aggregate_"
- "value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tname_part\030\001 \002"
- "(\t\022\024\n\014is_extension\030\002 \002(\010\"\325\001\n\016SourceCodeI"
- "nfo\022:\n\010location\030\001 \003(\0132(.google.protobuf."
- "SourceCodeInfo.Location\032\206\001\n\010Location\022\020\n\004"
- "path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020\001\022\030\n\020lead"
- "ing_comments\030\003 \001(\t\022\031\n\021trailing_comments\030"
- "\004 \001(\t\022!\n\031leading_detached_comments\030\006 \003(\t"
- "BX\n\023com.google.protobufB\020DescriptorProto"
- "sH\001Z\ndescriptor\242\002\003GPB\252\002\032Google.Protobuf."
- "Reflection", 5010);
+ "tion*\t\010\350\007\020\200\200\200\200\002\"z\n\rMethodOptions\022\031\n\ndepr"
+ "ecated\030! \001(\010:\005false\022C\n\024uninterpreted_opt"
+ "ion\030\347\007 \003(\0132$.google.protobuf.Uninterpret"
+ "edOption*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023UninterpretedOpt"
+ "ion\022;\n\004name\030\002 \003(\0132-.google.protobuf.Unin"
+ "terpretedOption.NamePart\022\030\n\020identifier_v"
+ "alue\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022\032"
+ "\n\022negative_int_value\030\005 \001(\003\022\024\n\014double_val"
+ "ue\030\006 \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027\n\017aggreg"
+ "ate_value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tname_part"
+ "\030\001 \002(\t\022\024\n\014is_extension\030\002 \002(\010\"\325\001\n\016SourceC"
+ "odeInfo\022:\n\010location\030\001 \003(\0132(.google.proto"
+ "buf.SourceCodeInfo.Location\032\206\001\n\010Location"
+ "\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020\001\022\030\n\020"
+ "leading_comments\030\003 \001(\t\022\031\n\021trailing_comme"
+ "nts\030\004 \001(\t\022!\n\031leading_detached_comments\030\006"
+ " \003(\t\"\247\001\n\021GeneratedCodeInfo\022A\n\nannotation"
+ "\030\001 \003(\0132-.google.protobuf.GeneratedCodeIn"
+ "fo.Annotation\032O\n\nAnnotation\022\020\n\004path\030\001 \003("
+ "\005B\002\020\001\022\023\n\013source_file\030\002 \001(\t\022\r\n\005begin\030\003 \001("
+ "\005\022\013\n\003end\030\004 \001(\005BX\n\023com.google.protobufB\020D"
+ "escriptorProtosH\001Z\ndescriptor\242\002\003GPB\252\002\032Go"
+ "ogle.Protobuf.Reflection", 5184);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"google/protobuf/descriptor.proto", &protobuf_RegisterTypes);
FileDescriptorSet::default_instance_ = new FileDescriptorSet();
@@ -784,6 +836,8 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
UninterpretedOption_NamePart::default_instance_ = new UninterpretedOption_NamePart();
SourceCodeInfo::default_instance_ = new SourceCodeInfo();
SourceCodeInfo_Location::default_instance_ = new SourceCodeInfo_Location();
+ GeneratedCodeInfo::default_instance_ = new GeneratedCodeInfo();
+ GeneratedCodeInfo_Annotation::default_instance_ = new GeneratedCodeInfo_Annotation();
FileDescriptorSet::default_instance_->InitAsDefaultInstance();
FileDescriptorProto::default_instance_->InitAsDefaultInstance();
DescriptorProto::default_instance_->InitAsDefaultInstance();
@@ -806,6 +860,8 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
UninterpretedOption_NamePart::default_instance_->InitAsDefaultInstance();
SourceCodeInfo::default_instance_->InitAsDefaultInstance();
SourceCodeInfo_Location::default_instance_->InitAsDefaultInstance();
+ GeneratedCodeInfo::default_instance_->InitAsDefaultInstance();
+ GeneratedCodeInfo_Annotation::default_instance_->InitAsDefaultInstance();
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto);
}
@@ -8073,7 +8129,7 @@ bool FileOptions::MergePartialFromCodedStream(
break;
}
- // optional bool javanano_use_deprecated_package = 38;
+ // optional bool javanano_use_deprecated_package = 38 [deprecated = true];
case 38: {
if (tag == 304) {
parse_javanano_use_deprecated_package:
@@ -8231,7 +8287,7 @@ void FileOptions::SerializeWithCachedSizes(
37, this->csharp_namespace(), output);
}
- // optional bool javanano_use_deprecated_package = 38;
+ // optional bool javanano_use_deprecated_package = 38 [deprecated = true];
if (has_javanano_use_deprecated_package()) {
::google::protobuf::internal::WireFormatLite::WriteBool(38, this->javanano_use_deprecated_package(), output);
}
@@ -8357,7 +8413,7 @@ void FileOptions::SerializeWithCachedSizes(
37, this->csharp_namespace(), target);
}
- // optional bool javanano_use_deprecated_package = 38;
+ // optional bool javanano_use_deprecated_package = 38 [deprecated = true];
if (has_javanano_use_deprecated_package()) {
target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(38, this->javanano_use_deprecated_package(), target);
}
@@ -8468,7 +8524,7 @@ int FileOptions::ByteSize() const {
this->csharp_namespace());
}
- // optional bool javanano_use_deprecated_package = 38;
+ // optional bool javanano_use_deprecated_package = 38 [deprecated = true];
if (has_javanano_use_deprecated_package()) {
total_size += 2 + 1;
}
@@ -9109,7 +9165,7 @@ void FileOptions::clear_csharp_namespace() {
// @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.csharp_namespace)
}
-// optional bool javanano_use_deprecated_package = 38;
+// optional bool javanano_use_deprecated_package = 38 [deprecated = true];
bool FileOptions::has_javanano_use_deprecated_package() const {
return (_has_bits_[0] & 0x00004000u) != 0;
}
@@ -14056,6 +14112,812 @@ SourceCodeInfo::location() const {
#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int GeneratedCodeInfo_Annotation::kPathFieldNumber;
+const int GeneratedCodeInfo_Annotation::kSourceFileFieldNumber;
+const int GeneratedCodeInfo_Annotation::kBeginFieldNumber;
+const int GeneratedCodeInfo_Annotation::kEndFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+GeneratedCodeInfo_Annotation::GeneratedCodeInfo_Annotation()
+ : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:google.protobuf.GeneratedCodeInfo.Annotation)
+}
+
+void GeneratedCodeInfo_Annotation::InitAsDefaultInstance() {
+}
+
+GeneratedCodeInfo_Annotation::GeneratedCodeInfo_Annotation(const GeneratedCodeInfo_Annotation& from)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(NULL) {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:google.protobuf.GeneratedCodeInfo.Annotation)
+}
+
+void GeneratedCodeInfo_Annotation::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ source_file_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ begin_ = 0;
+ end_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+GeneratedCodeInfo_Annotation::~GeneratedCodeInfo_Annotation() {
+ // @@protoc_insertion_point(destructor:google.protobuf.GeneratedCodeInfo.Annotation)
+ SharedDtor();
+}
+
+void GeneratedCodeInfo_Annotation::SharedDtor() {
+ source_file_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ if (this != default_instance_) {
+ }
+}
+
+void GeneratedCodeInfo_Annotation::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* GeneratedCodeInfo_Annotation::descriptor() {
+ protobuf_AssignDescriptorsOnce();
+ return GeneratedCodeInfo_Annotation_descriptor_;
+}
+
+const GeneratedCodeInfo_Annotation& GeneratedCodeInfo_Annotation::default_instance() {
+ if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
+ return *default_instance_;
+}
+
+GeneratedCodeInfo_Annotation* GeneratedCodeInfo_Annotation::default_instance_ = NULL;
+
+GeneratedCodeInfo_Annotation* GeneratedCodeInfo_Annotation::New(::google::protobuf::Arena* arena) const {
+ GeneratedCodeInfo_Annotation* n = new GeneratedCodeInfo_Annotation;
+ if (arena != NULL) {
+ arena->Own(n);
+ }
+ return n;
+}
+
+void GeneratedCodeInfo_Annotation::Clear() {
+#define ZR_HELPER_(f) reinterpret_cast<char*>(\
+ &reinterpret_cast<GeneratedCodeInfo_Annotation*>(16)->f)
+
+#define ZR_(first, last) do {\
+ ::memset(&first, 0,\
+ ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\
+} while (0)
+
+ if (_has_bits_[0 / 32] & 14u) {
+ ZR_(begin_, end_);
+ if (has_source_file()) {
+ source_file_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ }
+
+#undef ZR_HELPER_
+#undef ZR_
+
+ path_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ if (_internal_metadata_.have_unknown_fields()) {
+ mutable_unknown_fields()->Clear();
+ }
+}
+
+bool GeneratedCodeInfo_Annotation::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ // @@protoc_insertion_point(parse_start:google.protobuf.GeneratedCodeInfo.Annotation)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated int32 path = 1 [packed = true];
+ case 1: {
+ if (tag == 10) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, this->mutable_path())));
+ } else if (tag == 8) {
+ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ 1, 10, input, this->mutable_path())));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_source_file;
+ break;
+ }
+
+ // optional string source_file = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_source_file:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_source_file()));
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+ this->source_file().data(), this->source_file().length(),
+ ::google::protobuf::internal::WireFormat::PARSE,
+ "google.protobuf.GeneratedCodeInfo.Annotation.source_file");
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(24)) goto parse_begin;
+ break;
+ }
+
+ // optional int32 begin = 3;
+ case 3: {
+ if (tag == 24) {
+ parse_begin:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &begin_)));
+ set_has_begin();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(32)) goto parse_end;
+ break;
+ }
+
+ // optional int32 end = 4;
+ case 4: {
+ if (tag == 32) {
+ parse_end:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+ input, &end_)));
+ set_has_end();
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormat::SkipField(
+ input, tag, mutable_unknown_fields()));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:google.protobuf.GeneratedCodeInfo.Annotation)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:google.protobuf.GeneratedCodeInfo.Annotation)
+ return false;
+#undef DO_
+}
+
+void GeneratedCodeInfo_Annotation::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:google.protobuf.GeneratedCodeInfo.Annotation)
+ // repeated int32 path = 1 [packed = true];
+ if (this->path_size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::WriteTag(1, ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output);
+ output->WriteVarint32(_path_cached_byte_size_);
+ }
+ for (int i = 0; i < this->path_size(); i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32NoTag(
+ this->path(i), output);
+ }
+
+ // optional string source_file = 2;
+ if (has_source_file()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+ this->source_file().data(), this->source_file().length(),
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "google.protobuf.GeneratedCodeInfo.Annotation.source_file");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 2, this->source_file(), output);
+ }
+
+ // optional int32 begin = 3;
+ if (has_begin()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->begin(), output);
+ }
+
+ // optional int32 end = 4;
+ if (has_end()) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->end(), output);
+ }
+
+ if (_internal_metadata_.have_unknown_fields()) {
+ ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+ unknown_fields(), output);
+ }
+ // @@protoc_insertion_point(serialize_end:google.protobuf.GeneratedCodeInfo.Annotation)
+}
+
+::google::protobuf::uint8* GeneratedCodeInfo_Annotation::SerializeWithCachedSizesToArray(
+ ::google::protobuf::uint8* target) const {
+ // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.GeneratedCodeInfo.Annotation)
+ // repeated int32 path = 1 [packed = true];
+ if (this->path_size() > 0) {
+ target = ::google::protobuf::internal::WireFormatLite::WriteTagToArray(
+ 1,
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
+ target);
+ target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray(
+ _path_cached_byte_size_, target);
+ }
+ for (int i = 0; i < this->path_size(); i++) {
+ target = ::google::protobuf::internal::WireFormatLite::
+ WriteInt32NoTagToArray(this->path(i), target);
+ }
+
+ // optional string source_file = 2;
+ if (has_source_file()) {
+ ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(
+ this->source_file().data(), this->source_file().length(),
+ ::google::protobuf::internal::WireFormat::SERIALIZE,
+ "google.protobuf.GeneratedCodeInfo.Annotation.source_file");
+ target =
+ ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+ 2, this->source_file(), target);
+ }
+
+ // optional int32 begin = 3;
+ if (has_begin()) {
+ target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(3, this->begin(), target);
+ }
+
+ // optional int32 end = 4;
+ if (has_end()) {
+ target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(4, this->end(), target);
+ }
+
+ if (_internal_metadata_.have_unknown_fields()) {
+ target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+ unknown_fields(), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.GeneratedCodeInfo.Annotation)
+ return target;
+}
+
+int GeneratedCodeInfo_Annotation::ByteSize() const {
+ int total_size = 0;
+
+ if (_has_bits_[1 / 32] & 14u) {
+ // optional string source_file = 2;
+ if (has_source_file()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->source_file());
+ }
+
+ // optional int32 begin = 3;
+ if (has_begin()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->begin());
+ }
+
+ // optional int32 end = 4;
+ if (has_end()) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(
+ this->end());
+ }
+
+ }
+ // repeated int32 path = 1 [packed = true];
+ {
+ int data_size = 0;
+ for (int i = 0; i < this->path_size(); i++) {
+ data_size += ::google::protobuf::internal::WireFormatLite::
+ Int32Size(this->path(i));
+ }
+ if (data_size > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int32Size(data_size);
+ }
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _path_cached_byte_size_ = data_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ total_size += data_size;
+ }
+
+ if (_internal_metadata_.have_unknown_fields()) {
+ total_size +=
+ ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+ unknown_fields());
+ }
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void GeneratedCodeInfo_Annotation::MergeFrom(const ::google::protobuf::Message& from) {
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ const GeneratedCodeInfo_Annotation* source =
+ ::google::protobuf::internal::DynamicCastToGenerated<const GeneratedCodeInfo_Annotation>(
+ &from);
+ if (source == NULL) {
+ ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+ } else {
+ MergeFrom(*source);
+ }
+}
+
+void GeneratedCodeInfo_Annotation::MergeFrom(const GeneratedCodeInfo_Annotation& from) {
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ path_.MergeFrom(from.path_);
+ if (from._has_bits_[1 / 32] & (0xffu << (1 % 32))) {
+ if (from.has_source_file()) {
+ set_has_source_file();
+ source_file_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.source_file_);
+ }
+ if (from.has_begin()) {
+ set_begin(from.begin());
+ }
+ if (from.has_end()) {
+ set_end(from.end());
+ }
+ }
+ if (from._internal_metadata_.have_unknown_fields()) {
+ mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+ }
+}
+
+void GeneratedCodeInfo_Annotation::CopyFrom(const ::google::protobuf::Message& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+void GeneratedCodeInfo_Annotation::CopyFrom(const GeneratedCodeInfo_Annotation& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool GeneratedCodeInfo_Annotation::IsInitialized() const {
+
+ return true;
+}
+
+void GeneratedCodeInfo_Annotation::Swap(GeneratedCodeInfo_Annotation* other) {
+ if (other == this) return;
+ InternalSwap(other);
+}
+void GeneratedCodeInfo_Annotation::InternalSwap(GeneratedCodeInfo_Annotation* other) {
+ path_.UnsafeArenaSwap(&other->path_);
+ source_file_.Swap(&other->source_file_);
+ std::swap(begin_, other->begin_);
+ std::swap(end_, other->end_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ std::swap(_cached_size_, other->_cached_size_);
+}
+
+::google::protobuf::Metadata GeneratedCodeInfo_Annotation::GetMetadata() const {
+ protobuf_AssignDescriptorsOnce();
+ ::google::protobuf::Metadata metadata;
+ metadata.descriptor = GeneratedCodeInfo_Annotation_descriptor_;
+ metadata.reflection = GeneratedCodeInfo_Annotation_reflection_;
+ return metadata;
+}
+
+
+// -------------------------------------------------------------------
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int GeneratedCodeInfo::kAnnotationFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+GeneratedCodeInfo::GeneratedCodeInfo()
+ : ::google::protobuf::Message(), _internal_metadata_(NULL) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:google.protobuf.GeneratedCodeInfo)
+}
+
+void GeneratedCodeInfo::InitAsDefaultInstance() {
+}
+
+GeneratedCodeInfo::GeneratedCodeInfo(const GeneratedCodeInfo& from)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(NULL) {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:google.protobuf.GeneratedCodeInfo)
+}
+
+void GeneratedCodeInfo::SharedCtor() {
+ _cached_size_ = 0;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+GeneratedCodeInfo::~GeneratedCodeInfo() {
+ // @@protoc_insertion_point(destructor:google.protobuf.GeneratedCodeInfo)
+ SharedDtor();
+}
+
+void GeneratedCodeInfo::SharedDtor() {
+ if (this != default_instance_) {
+ }
+}
+
+void GeneratedCodeInfo::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* GeneratedCodeInfo::descriptor() {
+ protobuf_AssignDescriptorsOnce();
+ return GeneratedCodeInfo_descriptor_;
+}
+
+const GeneratedCodeInfo& GeneratedCodeInfo::default_instance() {
+ if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
+ return *default_instance_;
+}
+
+GeneratedCodeInfo* GeneratedCodeInfo::default_instance_ = NULL;
+
+GeneratedCodeInfo* GeneratedCodeInfo::New(::google::protobuf::Arena* arena) const {
+ GeneratedCodeInfo* n = new GeneratedCodeInfo;
+ if (arena != NULL) {
+ arena->Own(n);
+ }
+ return n;
+}
+
+void GeneratedCodeInfo::Clear() {
+ annotation_.Clear();
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+ if (_internal_metadata_.have_unknown_fields()) {
+ mutable_unknown_fields()->Clear();
+ }
+}
+
+bool GeneratedCodeInfo::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ // @@protoc_insertion_point(parse_start:google.protobuf.GeneratedCodeInfo)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(input->IncrementRecursionDepth());
+ parse_loop_annotation:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth(
+ input, add_annotation()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(10)) goto parse_loop_annotation;
+ input->UnsafeDecrementRecursionDepth();
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormat::SkipField(
+ input, tag, mutable_unknown_fields()));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:google.protobuf.GeneratedCodeInfo)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:google.protobuf.GeneratedCodeInfo)
+ return false;
+#undef DO_
+}
+
+void GeneratedCodeInfo::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:google.protobuf.GeneratedCodeInfo)
+ // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+ for (unsigned int i = 0, n = this->annotation_size(); i < n; i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
+ 1, this->annotation(i), output);
+ }
+
+ if (_internal_metadata_.have_unknown_fields()) {
+ ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+ unknown_fields(), output);
+ }
+ // @@protoc_insertion_point(serialize_end:google.protobuf.GeneratedCodeInfo)
+}
+
+::google::protobuf::uint8* GeneratedCodeInfo::SerializeWithCachedSizesToArray(
+ ::google::protobuf::uint8* target) const {
+ // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.GeneratedCodeInfo)
+ // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+ for (unsigned int i = 0, n = this->annotation_size(); i < n; i++) {
+ target = ::google::protobuf::internal::WireFormatLite::
+ WriteMessageNoVirtualToArray(
+ 1, this->annotation(i), target);
+ }
+
+ if (_internal_metadata_.have_unknown_fields()) {
+ target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+ unknown_fields(), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.GeneratedCodeInfo)
+ return target;
+}
+
+int GeneratedCodeInfo::ByteSize() const {
+ int total_size = 0;
+
+ // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+ total_size += 1 * this->annotation_size();
+ for (int i = 0; i < this->annotation_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->annotation(i));
+ }
+
+ if (_internal_metadata_.have_unknown_fields()) {
+ total_size +=
+ ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+ unknown_fields());
+ }
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void GeneratedCodeInfo::MergeFrom(const ::google::protobuf::Message& from) {
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ const GeneratedCodeInfo* source =
+ ::google::protobuf::internal::DynamicCastToGenerated<const GeneratedCodeInfo>(
+ &from);
+ if (source == NULL) {
+ ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+ } else {
+ MergeFrom(*source);
+ }
+}
+
+void GeneratedCodeInfo::MergeFrom(const GeneratedCodeInfo& from) {
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ annotation_.MergeFrom(from.annotation_);
+ if (from._internal_metadata_.have_unknown_fields()) {
+ mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+ }
+}
+
+void GeneratedCodeInfo::CopyFrom(const ::google::protobuf::Message& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+void GeneratedCodeInfo::CopyFrom(const GeneratedCodeInfo& from) {
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool GeneratedCodeInfo::IsInitialized() const {
+
+ return true;
+}
+
+void GeneratedCodeInfo::Swap(GeneratedCodeInfo* other) {
+ if (other == this) return;
+ InternalSwap(other);
+}
+void GeneratedCodeInfo::InternalSwap(GeneratedCodeInfo* other) {
+ annotation_.UnsafeArenaSwap(&other->annotation_);
+ std::swap(_has_bits_[0], other->_has_bits_[0]);
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ std::swap(_cached_size_, other->_cached_size_);
+}
+
+::google::protobuf::Metadata GeneratedCodeInfo::GetMetadata() const {
+ protobuf_AssignDescriptorsOnce();
+ ::google::protobuf::Metadata metadata;
+ metadata.descriptor = GeneratedCodeInfo_descriptor_;
+ metadata.reflection = GeneratedCodeInfo_reflection_;
+ return metadata;
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// GeneratedCodeInfo_Annotation
+
+// repeated int32 path = 1 [packed = true];
+int GeneratedCodeInfo_Annotation::path_size() const {
+ return path_.size();
+}
+void GeneratedCodeInfo_Annotation::clear_path() {
+ path_.Clear();
+}
+ ::google::protobuf::int32 GeneratedCodeInfo_Annotation::path(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.path)
+ return path_.Get(index);
+}
+ void GeneratedCodeInfo_Annotation::set_path(int index, ::google::protobuf::int32 value) {
+ path_.Set(index, value);
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.path)
+}
+ void GeneratedCodeInfo_Annotation::add_path(::google::protobuf::int32 value) {
+ path_.Add(value);
+ // @@protoc_insertion_point(field_add:google.protobuf.GeneratedCodeInfo.Annotation.path)
+}
+ const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+GeneratedCodeInfo_Annotation::path() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.GeneratedCodeInfo.Annotation.path)
+ return path_;
+}
+ ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+GeneratedCodeInfo_Annotation::mutable_path() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.GeneratedCodeInfo.Annotation.path)
+ return &path_;
+}
+
+// optional string source_file = 2;
+bool GeneratedCodeInfo_Annotation::has_source_file() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+void GeneratedCodeInfo_Annotation::set_has_source_file() {
+ _has_bits_[0] |= 0x00000002u;
+}
+void GeneratedCodeInfo_Annotation::clear_has_source_file() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+void GeneratedCodeInfo_Annotation::clear_source_file() {
+ source_file_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ clear_has_source_file();
+}
+ const ::std::string& GeneratedCodeInfo_Annotation::source_file() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+ return source_file_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void GeneratedCodeInfo_Annotation::set_source_file(const ::std::string& value) {
+ set_has_source_file();
+ source_file_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+ void GeneratedCodeInfo_Annotation::set_source_file(const char* value) {
+ set_has_source_file();
+ source_file_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+ void GeneratedCodeInfo_Annotation::set_source_file(const char* value, size_t size) {
+ set_has_source_file();
+ source_file_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+ ::std::string* GeneratedCodeInfo_Annotation::mutable_source_file() {
+ set_has_source_file();
+ // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+ return source_file_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* GeneratedCodeInfo_Annotation::release_source_file() {
+ clear_has_source_file();
+ return source_file_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void GeneratedCodeInfo_Annotation::set_allocated_source_file(::std::string* source_file) {
+ if (source_file != NULL) {
+ set_has_source_file();
+ } else {
+ clear_has_source_file();
+ }
+ source_file_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), source_file);
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+
+// optional int32 begin = 3;
+bool GeneratedCodeInfo_Annotation::has_begin() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+void GeneratedCodeInfo_Annotation::set_has_begin() {
+ _has_bits_[0] |= 0x00000004u;
+}
+void GeneratedCodeInfo_Annotation::clear_has_begin() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+void GeneratedCodeInfo_Annotation::clear_begin() {
+ begin_ = 0;
+ clear_has_begin();
+}
+ ::google::protobuf::int32 GeneratedCodeInfo_Annotation::begin() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.begin)
+ return begin_;
+}
+ void GeneratedCodeInfo_Annotation::set_begin(::google::protobuf::int32 value) {
+ set_has_begin();
+ begin_ = value;
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.begin)
+}
+
+// optional int32 end = 4;
+bool GeneratedCodeInfo_Annotation::has_end() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+void GeneratedCodeInfo_Annotation::set_has_end() {
+ _has_bits_[0] |= 0x00000008u;
+}
+void GeneratedCodeInfo_Annotation::clear_has_end() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+void GeneratedCodeInfo_Annotation::clear_end() {
+ end_ = 0;
+ clear_has_end();
+}
+ ::google::protobuf::int32 GeneratedCodeInfo_Annotation::end() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.end)
+ return end_;
+}
+ void GeneratedCodeInfo_Annotation::set_end(::google::protobuf::int32 value) {
+ set_has_end();
+ end_ = value;
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.end)
+}
+
+// -------------------------------------------------------------------
+
+// GeneratedCodeInfo
+
+// repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+int GeneratedCodeInfo::annotation_size() const {
+ return annotation_.size();
+}
+void GeneratedCodeInfo::clear_annotation() {
+ annotation_.Clear();
+}
+const ::google::protobuf::GeneratedCodeInfo_Annotation& GeneratedCodeInfo::annotation(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_.Get(index);
+}
+::google::protobuf::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::mutable_annotation(int index) {
+ // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_.Mutable(index);
+}
+::google::protobuf::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::add_annotation() {
+ // @@protoc_insertion_point(field_add:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_.Add();
+}
+::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation >*
+GeneratedCodeInfo::mutable_annotation() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.GeneratedCodeInfo.annotation)
+ return &annotation_;
+}
+const ::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation >&
+GeneratedCodeInfo::annotation() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_;
+}
+
+#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
+
// @@protoc_insertion_point(namespace_scope)
} // namespace protobuf
diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h
index 60255162..3fe07bf5 100644
--- a/src/google/protobuf/descriptor.pb.h
+++ b/src/google/protobuf/descriptor.pb.h
@@ -50,6 +50,8 @@ class FieldOptions;
class FileDescriptorProto;
class FileDescriptorSet;
class FileOptions;
+class GeneratedCodeInfo;
+class GeneratedCodeInfo_Annotation;
class MessageOptions;
class MethodDescriptorProto;
class MethodOptions;
@@ -2015,12 +2017,12 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message {
::std::string* release_csharp_namespace();
void set_allocated_csharp_namespace(::std::string* csharp_namespace);
- // optional bool javanano_use_deprecated_package = 38;
- bool has_javanano_use_deprecated_package() const;
- void clear_javanano_use_deprecated_package();
+ // optional bool javanano_use_deprecated_package = 38 [deprecated = true];
+ bool has_javanano_use_deprecated_package() const PROTOBUF_DEPRECATED;
+ void clear_javanano_use_deprecated_package() PROTOBUF_DEPRECATED;
static const int kJavananoUseDeprecatedPackageFieldNumber = 38;
- bool javanano_use_deprecated_package() const;
- void set_javanano_use_deprecated_package(bool value);
+ bool javanano_use_deprecated_package() const PROTOBUF_DEPRECATED;
+ void set_javanano_use_deprecated_package(bool value) PROTOBUF_DEPRECATED;
// repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999;
int uninterpreted_option_size() const;
@@ -3390,6 +3392,228 @@ class LIBPROTOBUF_EXPORT SourceCodeInfo : public ::google::protobuf::Message {
void InitAsDefaultInstance();
static SourceCodeInfo* default_instance_;
};
+// -------------------------------------------------------------------
+
+class LIBPROTOBUF_EXPORT GeneratedCodeInfo_Annotation : public ::google::protobuf::Message {
+ public:
+ GeneratedCodeInfo_Annotation();
+ virtual ~GeneratedCodeInfo_Annotation();
+
+ GeneratedCodeInfo_Annotation(const GeneratedCodeInfo_Annotation& from);
+
+ inline GeneratedCodeInfo_Annotation& operator=(const GeneratedCodeInfo_Annotation& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+
+ inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const ::google::protobuf::Descriptor* descriptor();
+ static const GeneratedCodeInfo_Annotation& default_instance();
+
+ void Swap(GeneratedCodeInfo_Annotation* other);
+
+ // implements Message ----------------------------------------------
+
+ inline GeneratedCodeInfo_Annotation* New() const { return New(NULL); }
+
+ GeneratedCodeInfo_Annotation* New(::google::protobuf::Arena* arena) const;
+ void CopyFrom(const ::google::protobuf::Message& from);
+ void MergeFrom(const ::google::protobuf::Message& from);
+ void CopyFrom(const GeneratedCodeInfo_Annotation& from);
+ void MergeFrom(const GeneratedCodeInfo_Annotation& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(GeneratedCodeInfo_Annotation* other);
+ private:
+ inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+ return _internal_metadata_.arena();
+ }
+ inline void* MaybeArenaPtr() const {
+ return _internal_metadata_.raw_arena_ptr();
+ }
+ public:
+
+ ::google::protobuf::Metadata GetMetadata() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // repeated int32 path = 1 [packed = true];
+ int path_size() const;
+ void clear_path();
+ static const int kPathFieldNumber = 1;
+ ::google::protobuf::int32 path(int index) const;
+ void set_path(int index, ::google::protobuf::int32 value);
+ void add_path(::google::protobuf::int32 value);
+ const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+ path() const;
+ ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+ mutable_path();
+
+ // optional string source_file = 2;
+ bool has_source_file() const;
+ void clear_source_file();
+ static const int kSourceFileFieldNumber = 2;
+ const ::std::string& source_file() const;
+ void set_source_file(const ::std::string& value);
+ void set_source_file(const char* value);
+ void set_source_file(const char* value, size_t size);
+ ::std::string* mutable_source_file();
+ ::std::string* release_source_file();
+ void set_allocated_source_file(::std::string* source_file);
+
+ // optional int32 begin = 3;
+ bool has_begin() const;
+ void clear_begin();
+ static const int kBeginFieldNumber = 3;
+ ::google::protobuf::int32 begin() const;
+ void set_begin(::google::protobuf::int32 value);
+
+ // optional int32 end = 4;
+ bool has_end() const;
+ void clear_end();
+ static const int kEndFieldNumber = 4;
+ ::google::protobuf::int32 end() const;
+ void set_end(::google::protobuf::int32 value);
+
+ // @@protoc_insertion_point(class_scope:google.protobuf.GeneratedCodeInfo.Annotation)
+ private:
+ inline void set_has_source_file();
+ inline void clear_has_source_file();
+ inline void set_has_begin();
+ inline void clear_has_begin();
+ inline void set_has_end();
+ inline void clear_has_end();
+
+ ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedField< ::google::protobuf::int32 > path_;
+ mutable int _path_cached_byte_size_;
+ ::google::protobuf::internal::ArenaStringPtr source_file_;
+ ::google::protobuf::int32 begin_;
+ ::google::protobuf::int32 end_;
+ friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
+ friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+ friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
+
+ void InitAsDefaultInstance();
+ static GeneratedCodeInfo_Annotation* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LIBPROTOBUF_EXPORT GeneratedCodeInfo : public ::google::protobuf::Message {
+ public:
+ GeneratedCodeInfo();
+ virtual ~GeneratedCodeInfo();
+
+ GeneratedCodeInfo(const GeneratedCodeInfo& from);
+
+ inline GeneratedCodeInfo& operator=(const GeneratedCodeInfo& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+
+ inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const ::google::protobuf::Descriptor* descriptor();
+ static const GeneratedCodeInfo& default_instance();
+
+ void Swap(GeneratedCodeInfo* other);
+
+ // implements Message ----------------------------------------------
+
+ inline GeneratedCodeInfo* New() const { return New(NULL); }
+
+ GeneratedCodeInfo* New(::google::protobuf::Arena* arena) const;
+ void CopyFrom(const ::google::protobuf::Message& from);
+ void MergeFrom(const ::google::protobuf::Message& from);
+ void CopyFrom(const GeneratedCodeInfo& from);
+ void MergeFrom(const GeneratedCodeInfo& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(GeneratedCodeInfo* other);
+ private:
+ inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+ return _internal_metadata_.arena();
+ }
+ inline void* MaybeArenaPtr() const {
+ return _internal_metadata_.raw_arena_ptr();
+ }
+ public:
+
+ ::google::protobuf::Metadata GetMetadata() const;
+
+ // nested types ----------------------------------------------------
+
+ typedef GeneratedCodeInfo_Annotation Annotation;
+
+ // accessors -------------------------------------------------------
+
+ // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+ int annotation_size() const;
+ void clear_annotation();
+ static const int kAnnotationFieldNumber = 1;
+ const ::google::protobuf::GeneratedCodeInfo_Annotation& annotation(int index) const;
+ ::google::protobuf::GeneratedCodeInfo_Annotation* mutable_annotation(int index);
+ ::google::protobuf::GeneratedCodeInfo_Annotation* add_annotation();
+ ::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation >*
+ mutable_annotation();
+ const ::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation >&
+ annotation() const;
+
+ // @@protoc_insertion_point(class_scope:google.protobuf.GeneratedCodeInfo)
+ private:
+
+ ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation > annotation_;
+ friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
+ friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+ friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
+
+ void InitAsDefaultInstance();
+ static GeneratedCodeInfo* default_instance_;
+};
// ===================================================================
@@ -5970,7 +6194,7 @@ inline void FileOptions::set_allocated_csharp_namespace(::std::string* csharp_na
// @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.csharp_namespace)
}
-// optional bool javanano_use_deprecated_package = 38;
+// optional bool javanano_use_deprecated_package = 38 [deprecated = true];
inline bool FileOptions::has_javanano_use_deprecated_package() const {
return (_has_bits_[0] & 0x00004000u) != 0;
}
@@ -7194,6 +7418,175 @@ SourceCodeInfo::location() const {
return location_;
}
+// -------------------------------------------------------------------
+
+// GeneratedCodeInfo_Annotation
+
+// repeated int32 path = 1 [packed = true];
+inline int GeneratedCodeInfo_Annotation::path_size() const {
+ return path_.size();
+}
+inline void GeneratedCodeInfo_Annotation::clear_path() {
+ path_.Clear();
+}
+inline ::google::protobuf::int32 GeneratedCodeInfo_Annotation::path(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.path)
+ return path_.Get(index);
+}
+inline void GeneratedCodeInfo_Annotation::set_path(int index, ::google::protobuf::int32 value) {
+ path_.Set(index, value);
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.path)
+}
+inline void GeneratedCodeInfo_Annotation::add_path(::google::protobuf::int32 value) {
+ path_.Add(value);
+ // @@protoc_insertion_point(field_add:google.protobuf.GeneratedCodeInfo.Annotation.path)
+}
+inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+GeneratedCodeInfo_Annotation::path() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.GeneratedCodeInfo.Annotation.path)
+ return path_;
+}
+inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+GeneratedCodeInfo_Annotation::mutable_path() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.GeneratedCodeInfo.Annotation.path)
+ return &path_;
+}
+
+// optional string source_file = 2;
+inline bool GeneratedCodeInfo_Annotation::has_source_file() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void GeneratedCodeInfo_Annotation::set_has_source_file() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void GeneratedCodeInfo_Annotation::clear_has_source_file() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void GeneratedCodeInfo_Annotation::clear_source_file() {
+ source_file_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ clear_has_source_file();
+}
+inline const ::std::string& GeneratedCodeInfo_Annotation::source_file() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+ return source_file_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void GeneratedCodeInfo_Annotation::set_source_file(const ::std::string& value) {
+ set_has_source_file();
+ source_file_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+inline void GeneratedCodeInfo_Annotation::set_source_file(const char* value) {
+ set_has_source_file();
+ source_file_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+inline void GeneratedCodeInfo_Annotation::set_source_file(const char* value, size_t size) {
+ set_has_source_file();
+ source_file_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+inline ::std::string* GeneratedCodeInfo_Annotation::mutable_source_file() {
+ set_has_source_file();
+ // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+ return source_file_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* GeneratedCodeInfo_Annotation::release_source_file() {
+ clear_has_source_file();
+ return source_file_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void GeneratedCodeInfo_Annotation::set_allocated_source_file(::std::string* source_file) {
+ if (source_file != NULL) {
+ set_has_source_file();
+ } else {
+ clear_has_source_file();
+ }
+ source_file_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), source_file);
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.GeneratedCodeInfo.Annotation.source_file)
+}
+
+// optional int32 begin = 3;
+inline bool GeneratedCodeInfo_Annotation::has_begin() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void GeneratedCodeInfo_Annotation::set_has_begin() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void GeneratedCodeInfo_Annotation::clear_has_begin() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void GeneratedCodeInfo_Annotation::clear_begin() {
+ begin_ = 0;
+ clear_has_begin();
+}
+inline ::google::protobuf::int32 GeneratedCodeInfo_Annotation::begin() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.begin)
+ return begin_;
+}
+inline void GeneratedCodeInfo_Annotation::set_begin(::google::protobuf::int32 value) {
+ set_has_begin();
+ begin_ = value;
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.begin)
+}
+
+// optional int32 end = 4;
+inline bool GeneratedCodeInfo_Annotation::has_end() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void GeneratedCodeInfo_Annotation::set_has_end() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void GeneratedCodeInfo_Annotation::clear_has_end() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void GeneratedCodeInfo_Annotation::clear_end() {
+ end_ = 0;
+ clear_has_end();
+}
+inline ::google::protobuf::int32 GeneratedCodeInfo_Annotation::end() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.Annotation.end)
+ return end_;
+}
+inline void GeneratedCodeInfo_Annotation::set_end(::google::protobuf::int32 value) {
+ set_has_end();
+ end_ = value;
+ // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.end)
+}
+
+// -------------------------------------------------------------------
+
+// GeneratedCodeInfo
+
+// repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1;
+inline int GeneratedCodeInfo::annotation_size() const {
+ return annotation_.size();
+}
+inline void GeneratedCodeInfo::clear_annotation() {
+ annotation_.Clear();
+}
+inline const ::google::protobuf::GeneratedCodeInfo_Annotation& GeneratedCodeInfo::annotation(int index) const {
+ // @@protoc_insertion_point(field_get:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_.Get(index);
+}
+inline ::google::protobuf::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::mutable_annotation(int index) {
+ // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_.Mutable(index);
+}
+inline ::google::protobuf::GeneratedCodeInfo_Annotation* GeneratedCodeInfo::add_annotation() {
+ // @@protoc_insertion_point(field_add:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_.Add();
+}
+inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation >*
+GeneratedCodeInfo::mutable_annotation() {
+ // @@protoc_insertion_point(field_mutable_list:google.protobuf.GeneratedCodeInfo.annotation)
+ return &annotation_;
+}
+inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::GeneratedCodeInfo_Annotation >&
+GeneratedCodeInfo::annotation() const {
+ // @@protoc_insertion_point(field_list:google.protobuf.GeneratedCodeInfo.annotation)
+ return annotation_;
+}
+
#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS
// -------------------------------------------------------------------
@@ -7237,6 +7630,10 @@ SourceCodeInfo::location() const {
// -------------------------------------------------------------------
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
// @@protoc_insertion_point(namespace_scope)
diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto
index c59a6022..3e664d59 100644
--- a/src/google/protobuf/descriptor.proto
+++ b/src/google/protobuf/descriptor.proto
@@ -379,7 +379,7 @@ message FileOptions {
// Whether the nano proto compiler should generate in the deprecated non-nano
// suffixed package.
- optional bool javanano_use_deprecated_package = 38;
+ optional bool javanano_use_deprecated_package = 38 [deprecated = true];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
@@ -777,3 +777,29 @@ message SourceCodeInfo {
repeated string leading_detached_comments = 6;
}
}
+
+// Describes the relationship between generated code and its original source
+// file. A GeneratedCodeInfo message is associated with only one generated
+// source file, but may contain references to different source .proto files.
+message GeneratedCodeInfo {
+ // An Annotation connects some span of text in generated code to an element
+ // of its generating .proto file.
+ repeated Annotation annotation = 1;
+ message Annotation {
+ // Identifies the element in the original source .proto file. This field
+ // is formatted the same as SourceCodeInfo.Location.path.
+ repeated int32 path = 1 [packed=true];
+
+ // Identifies the filesystem path to the original source .proto.
+ optional string source_file = 2;
+
+ // Identifies the starting offset in bytes in the generated code
+ // that relates to the identified object.
+ optional int32 begin = 3;
+
+ // Identifies the ending offset in bytes in the generated code that
+ // relates to the identified offset. The end offset should be one past
+ // the last relevant byte (so the length of the text = end - begin).
+ optional int32 end = 4;
+ }
+}
diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc
index ccd0650d..be8e0b72 100644
--- a/src/google/protobuf/descriptor_unittest.cc
+++ b/src/google/protobuf/descriptor_unittest.cc
@@ -178,6 +178,61 @@ void AddEmptyEnum(FileDescriptorProto* file, const string& name) {
AddEnumValue(AddEnum(file, name), name + "_DUMMY", 1);
}
+class MockErrorCollector : public DescriptorPool::ErrorCollector {
+ public:
+ MockErrorCollector() {}
+ ~MockErrorCollector() {}
+
+ string text_;
+ string warning_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 OPTION_NAME : location_name = "OPTION_NAME" ; break;
+ case OPTION_VALUE : location_name = "OPTION_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);
+ }
+
+ // implements ErrorCollector ---------------------------------------
+ void AddWarning(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 OPTION_NAME : location_name = "OPTION_NAME" ; break;
+ case OPTION_VALUE : location_name = "OPTION_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(
+ &warning_text_, "$0: $1: $2: $3\n",
+ filename, element_name, location_name, message);
+ }
+};
+
// ===================================================================
// Test simple files.
@@ -3120,77 +3175,95 @@ TEST(CustomOptions, UnusedImportWarning) {
->file()->CopyTo(&file_proto);
ASSERT_TRUE(pool.BuildFile(file_proto) != NULL);
-
pool.AddUnusedImportTrackFile("custom_options_import.proto");
ASSERT_TRUE(TextFormat::ParseFromString(
"name: \"custom_options_import.proto\" "
"package: \"protobuf_unittest\" "
"dependency: \"google/protobuf/unittest_custom_options.proto\" ",
&file_proto));
- pool.BuildFile(file_proto);
-}
-// ===================================================================
+ MockErrorCollector error_collector;
+ EXPECT_TRUE(pool.BuildFileCollectingErrors(file_proto, &error_collector));
+ EXPECT_EQ("", error_collector.warning_text_);
+}
-// 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.
+// Verifies that proto files can correctly be parsed, even if the
+// custom options defined in the file are incompatible with those
+// compiled in the binary. See http://b/19276250.
+TEST(CustomOptions, OptionsWithRequiredEnums) {
+ DescriptorPool pool;
-class MockErrorCollector : public DescriptorPool::ErrorCollector {
- public:
- MockErrorCollector() {}
- ~MockErrorCollector() {}
+ FileDescriptorProto file_proto;
+ MessageOptions::descriptor()->file()->CopyTo(&file_proto);
+ ASSERT_TRUE(pool.BuildFile(file_proto) != NULL);
- string text_;
- string warning_text_;
+ // Create a new file descriptor proto containing a subset of the
+ // messages defined in google/protobuf/unittest_custom_options.proto.
+ file_proto.Clear();
+ file_proto.set_name("unittest_custom_options.proto");
+ file_proto.set_package("protobuf_unittest");
+ file_proto.add_dependency("google/protobuf/descriptor.proto");
- // 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 OPTION_NAME : location_name = "OPTION_NAME" ; break;
- case OPTION_VALUE : location_name = "OPTION_VALUE" ; break;
- case INPUT_TYPE : location_name = "INPUT_TYPE" ; break;
- case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break;
- case OTHER : location_name = "OTHER" ; break;
- }
+ // Add the "required_enum_opt" extension.
+ FieldDescriptorProto* extension = file_proto.add_extension();
+ protobuf_unittest::OldOptionType::descriptor()->file()
+ ->FindExtensionByName("required_enum_opt")->CopyTo(extension);
+
+ // Add a test message that uses the "required_enum_opt" option.
+ DescriptorProto* test_message_type = file_proto.add_message_type();
+ protobuf_unittest::TestMessageWithRequiredEnumOption::descriptor()
+ ->CopyTo(test_message_type);
+
+ // Instruct the extension to use NewOptionType instead of
+ // OldOptionType, and add the descriptor of NewOptionType.
+ extension->set_type_name(".protobuf_unittest.NewOptionType");
+ DescriptorProto* new_option_type = file_proto.add_message_type();
+ protobuf_unittest::NewOptionType::descriptor()
+ ->CopyTo(new_option_type);
+
+ // Replace the value of the "required_enum_opt" option used in the
+ // test message with an enum value that only exists in NewOptionType.
+ ASSERT_TRUE(TextFormat::ParseFromString(
+ "uninterpreted_option { "
+ " name { "
+ " name_part: 'required_enum_opt' "
+ " is_extension: true "
+ " } "
+ " aggregate_value: 'value: NEW_VALUE' "
+ "}",
+ test_message_type->mutable_options()));
- strings::SubstituteAndAppend(
- &text_, "$0: $1: $2: $3\n",
- filename, element_name, location_name, message);
- }
+ // Add the file descriptor to the pool.
+ ASSERT_TRUE(pool.BuildFile(file_proto) != NULL);
- // implements ErrorCollector ---------------------------------------
- void AddWarning(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 OPTION_NAME : location_name = "OPTION_NAME" ; break;
- case OPTION_VALUE : location_name = "OPTION_VALUE" ; break;
- case INPUT_TYPE : location_name = "INPUT_TYPE" ; break;
- case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break;
- case OTHER : location_name = "OTHER" ; break;
- }
+ // Find the test message.
+ const Descriptor* test_message = pool.FindMessageTypeByName(
+ "protobuf_unittest.TestMessageWithRequiredEnumOption");
+ ASSERT_TRUE(test_message != NULL);
+
+ const MessageOptions& options = test_message->options();
+ // Extract the "required_enum_opt" option. Since the binary does not
+ // know that the extension was updated, this will still return an
+ // OldOptionType message.
+ ASSERT_TRUE(
+ options.HasExtension(protobuf_unittest::required_enum_opt));
+ const protobuf_unittest::OldOptionType& old_enum_opt =
+ options.GetExtension(protobuf_unittest::required_enum_opt);
+
+ // Confirm that the required enum field is missing.
+ EXPECT_FALSE(old_enum_opt.IsInitialized());
+ EXPECT_FALSE(old_enum_opt.has_value());
+
+ string buf;
+ // Verify that the required enum field does show up when the option
+ // is re-parsed as a NewOptionType message;
+ protobuf_unittest::NewOptionType new_enum_opt;
+ EXPECT_TRUE(old_enum_opt.AppendPartialToString(&buf));
+ EXPECT_TRUE(new_enum_opt.ParseFromString(buf));
+ EXPECT_EQ(protobuf_unittest::NewOptionType::NEW_VALUE, new_enum_opt.value());
+}
- strings::SubstituteAndAppend(
- &warning_text_, "$0: $1: $2: $3\n",
- filename, element_name, location_name, message);
- }
-};
+// ===================================================================
class ValidationErrorTest : public testing::Test {
protected:
@@ -5123,7 +5196,6 @@ TEST_F(ValidationErrorTest, AllowEnumAlias) {
}
TEST_F(ValidationErrorTest, UnusedImportWarning) {
-
pool_.AddUnusedImportTrackFile("bar.proto");
BuildFile(
"name: \"bar.proto\" "
@@ -5155,7 +5227,7 @@ TEST_F(ValidationErrorTest, UnusedImportWarning) {
// }
//
pool_.AddUnusedImportTrackFile("forward.proto");
- BuildFile(
+ BuildFileWithWarnings(
"name: \"forward.proto\""
"dependency: \"base.proto\""
"dependency: \"bar.proto\""
@@ -5165,7 +5237,8 @@ TEST_F(ValidationErrorTest, UnusedImportWarning) {
"message_type {"
" name: \"Forward\""
" field { name:\"base\" number:1 label:LABEL_OPTIONAL type_name:\"Base\" }"
- "}");
+ "}",
+ "forward.proto: bar.proto: OTHER: Import bar.proto but not used.\n");
}
namespace {
diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc
index 2e22ccb1..b325944e 100644
--- a/src/google/protobuf/duration.pb.cc
+++ b/src/google/protobuf/duration.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/duration.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/duration.pb.h"
+#include <google/protobuf/duration.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -111,10 +112,10 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Duration::kSecondsFieldNumber;
const int Duration::kNanosFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Duration::Duration()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
diff --git a/src/google/protobuf/duration.proto b/src/google/protobuf/duration.proto
index 7f172aa6..78bcc74b 100644
--- a/src/google/protobuf/duration.proto
+++ b/src/google/protobuf/duration.proto
@@ -27,15 +27,16 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_generate_equals_and_hash = true;
-option java_multiple_files = true;
-option java_outer_classname = "DurationProto";
-option java_package = "com.google.protobuf";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DurationProto";
+option java_multiple_files = true;
+option java_generate_equals_and_hash = true;
option objc_class_prefix = "GPB";
// A Duration represents a signed, fixed-length span of time represented
diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc
index 091fc975..bb400476 100644
--- a/src/google/protobuf/dynamic_message.cc
+++ b/src/google/protobuf/dynamic_message.cc
@@ -69,8 +69,8 @@
#include <google/protobuf/stubs/shared_ptr.h>
#endif
-#include <google/protobuf/stubs/scoped_ptr.h>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/scoped_ptr.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/descriptor.h>
diff --git a/src/google/protobuf/empty.pb.cc b/src/google/protobuf/empty.pb.cc
index 50cbd9a8..f2eec782 100644
--- a/src/google/protobuf/empty.pb.cc
+++ b/src/google/protobuf/empty.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/empty.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/empty.pb.h"
+#include <google/protobuf/empty.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -79,9 +80,9 @@ void protobuf_AddDesc_google_2fprotobuf_2fempty_2eproto() {
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\033google/protobuf/empty.proto\022\017google.pr"
- "otobuf\"\007\n\005EmptyBM\n\023com.google.protobufB\n"
- "EmptyProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Protobuf."
- "WellKnownTypesb\006proto3", 142);
+ "otobuf\"\007\n\005EmptyBP\n\023com.google.protobufB\n"
+ "EmptyProtoP\001\240\001\001\370\001\001\242\002\003GPB\252\002\036Google.Protob"
+ "uf.WellKnownTypesb\006proto3", 145);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"google/protobuf/empty.proto", &protobuf_RegisterTypes);
Empty::default_instance_ = new Empty();
@@ -108,8 +109,8 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
-#endif // !_MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Empty::Empty()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -117,6 +118,14 @@ Empty::Empty()
// @@protoc_insertion_point(constructor:google.protobuf.Empty)
}
+Empty::Empty(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.Empty)
+}
+
void Empty::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -140,10 +149,20 @@ Empty::~Empty() {
}
void Empty::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void Empty::ArenaDtor(void* object) {
+ Empty* _this = reinterpret_cast< Empty* >(object);
+ (void)_this;
+}
+void Empty::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void Empty::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -162,11 +181,7 @@ const Empty& Empty::default_instance() {
Empty* Empty::default_instance_ = NULL;
Empty* Empty::New(::google::protobuf::Arena* arena) const {
- Empty* n = new Empty;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<Empty>(arena);
}
void Empty::Clear() {
@@ -255,6 +270,18 @@ bool Empty::IsInitialized() const {
void Empty::Swap(Empty* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ Empty temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void Empty::UnsafeArenaSwap(Empty* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void Empty::InternalSwap(Empty* other) {
diff --git a/src/google/protobuf/empty.pb.h b/src/google/protobuf/empty.pb.h
index 20876bea..868009fc 100644
--- a/src/google/protobuf/empty.pb.h
+++ b/src/google/protobuf/empty.pb.h
@@ -53,9 +53,14 @@ class LIBPROTOBUF_EXPORT Empty : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const Empty& default_instance();
+ void UnsafeArenaSwap(Empty* other);
void Swap(Empty* other);
// implements Message ----------------------------------------------
@@ -82,6 +87,11 @@ class LIBPROTOBUF_EXPORT Empty : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(Empty* other);
+ protected:
+ explicit Empty(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -101,6 +111,9 @@ class LIBPROTOBUF_EXPORT Empty : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
mutable int _cached_size_;
friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fempty_2eproto();
diff --git a/src/google/protobuf/empty.proto b/src/google/protobuf/empty.proto
index 9dddc6c5..b96daf28 100644
--- a/src/google/protobuf/empty.proto
+++ b/src/google/protobuf/empty.proto
@@ -27,16 +27,18 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_multiple_files = true;
-option java_outer_classname = "EmptyProto";
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option java_package = "com.google.protobuf";
+option java_outer_classname = "EmptyProto";
+option java_multiple_files = true;
option java_generate_equals_and_hash = true;
-option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
// A generic empty message that you can re-use to avoid defining duplicated
// empty messages in your APIs. A typical example is to use it as the request
diff --git a/src/google/protobuf/field_mask.pb.cc b/src/google/protobuf/field_mask.pb.cc
index d8f4ee91..01a6ce56 100644
--- a/src/google/protobuf/field_mask.pb.cc
+++ b/src/google/protobuf/field_mask.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/field_mask.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/field_mask.pb.h"
+#include <google/protobuf/field_mask.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -110,9 +111,9 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int FieldMask::kPathsFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
FieldMask::FieldMask()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
diff --git a/src/google/protobuf/field_mask.proto b/src/google/protobuf/field_mask.proto
index 8b21c692..908c8a86 100644
--- a/src/google/protobuf/field_mask.proto
+++ b/src/google/protobuf/field_mask.proto
@@ -27,16 +27,17 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_generate_equals_and_hash = true;
-option java_multiple_files = true;
-option java_outer_classname = "FieldMaskProto";
-option java_package = "com.google.protobuf";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "FieldMaskProto";
+option java_multiple_files = true;
option objc_class_prefix = "GPB";
+option java_generate_equals_and_hash = true;
// `FieldMask` represents a set of symbolic field paths, for example:
//
@@ -69,7 +70,7 @@ option objc_class_prefix = "GPB";
// z: 8
//
// The result will not contain specific values for fields x,y and z
-// (there value will be set to the default, and omitted in proto text
+// (their value will be set to the default, and omitted in proto text
// output):
//
//
diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc
index 4bcd354f..e3a34d0a 100644
--- a/src/google/protobuf/io/coded_stream.cc
+++ b/src/google/protobuf/io/coded_stream.cc
@@ -629,6 +629,24 @@ CodedOutputStream::CodedOutputStream(ZeroCopyOutputStream* output)
had_error_ = false;
}
+CodedOutputStream::CodedOutputStream(ZeroCopyOutputStream* output,
+ bool do_eager_refresh)
+ : output_(output),
+ buffer_(NULL),
+ buffer_size_(0),
+ total_bytes_(0),
+ had_error_(false),
+ aliasing_enabled_(false) {
+ if (do_eager_refresh) {
+ // Eagerly Refresh() so buffer space is immediately available.
+ Refresh();
+ // The Refresh() may have failed. If the client doesn't write any data,
+ // though, don't consider this an error. If the client does write data, then
+ // another Refresh() will be attempted and it will set the error once again.
+ had_error_ = false;
+ }
+}
+
CodedOutputStream::~CodedOutputStream() {
Trim();
}
diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h
index 361c406b..e3771003 100644
--- a/src/google/protobuf/io/coded_stream.h
+++ b/src/google/protobuf/io/coded_stream.h
@@ -109,6 +109,7 @@
#ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
#define GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
+#include <assert.h>
#include <string>
#include <utility>
#ifdef _MSC_VER
@@ -116,7 +117,7 @@
#if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
#define PROTOBUF_LITTLE_ENDIAN 1
#endif
- #if _MSC_VER >= 1300
+ #if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
// If MSVC has "/RTCc" set, it will complain about truncating casts at
// runtime. This file contains some intentional truncating casts.
#pragma runtime_checks("c", off)
@@ -665,6 +666,7 @@ class LIBPROTOBUF_EXPORT CodedOutputStream {
public:
// Create an CodedOutputStream that writes to the given ZeroCopyOutputStream.
explicit CodedOutputStream(ZeroCopyOutputStream* output);
+ CodedOutputStream(ZeroCopyOutputStream* output, bool do_eager_refresh);
// Destroy the CodedOutputStream and position the underlying
// ZeroCopyOutputStream immediately after the last byte written.
@@ -1035,7 +1037,7 @@ inline const uint8* CodedInputStream::ExpectTagFromArray(
inline void CodedInputStream::GetDirectBufferPointerInline(const void** data,
int* size) {
*data = buffer_;
- *size = buffer_end_ - buffer_;
+ *size = static_cast<int>(buffer_end_ - buffer_);
}
inline bool CodedInputStream::ExpectAtEnd() {
@@ -1231,7 +1233,7 @@ inline MessageFactory* CodedInputStream::GetExtensionFactory() {
}
inline int CodedInputStream::BufferSize() const {
- return buffer_end_ - buffer_;
+ return static_cast<int>(buffer_end_ - buffer_);
}
inline CodedInputStream::CodedInputStream(ZeroCopyInputStream* input)
@@ -1284,9 +1286,9 @@ inline bool CodedInputStream::IsFlat() const {
} // namespace protobuf
-#if defined(_MSC_VER) && _MSC_VER >= 1300
+#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
#pragma runtime_checks("c", restore)
-#endif // _MSC_VER
+#endif // _MSC_VER && !defined(__INTEL_COMPILER)
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
diff --git a/src/google/protobuf/io/strtod.cc b/src/google/protobuf/io/strtod.cc
index 579de9aa..a90bb9a3 100644
--- a/src/google/protobuf/io/strtod.cc
+++ b/src/google/protobuf/io/strtod.cc
@@ -32,6 +32,7 @@
#include <cstdio>
#include <cstring>
+#include <limits>
#include <string>
#include <google/protobuf/stubs/logging.h>
@@ -109,6 +110,16 @@ double NoLocaleStrtod(const char* text, char** original_endptr) {
return result;
}
+float SafeDoubleToFloat(double value) {
+ if (value > std::numeric_limits<float>::max()) {
+ return std::numeric_limits<float>::infinity();
+ } else if (value < -std::numeric_limits<float>::max()) {
+ return -std::numeric_limits<float>::infinity();
+ } else {
+ return static_cast<float>(value);
+ }
+}
+
} // namespace io
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/io/strtod.h b/src/google/protobuf/io/strtod.h
index c2efc8d3..f56e41c8 100644
--- a/src/google/protobuf/io/strtod.h
+++ b/src/google/protobuf/io/strtod.h
@@ -43,6 +43,11 @@ namespace io {
// uses a dot as the decimal separator.
double NoLocaleStrtod(const char* str, char** endptr);
+// Casts a double value to a float value. If the value is outside of the
+// representable range of float, it will be converted to positive or negative
+// infinity.
+float SafeDoubleToFloat(double value);
+
} // namespace io
} // namespace protobuf
diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc
index 7ccd633d..3d57707c 100644
--- a/src/google/protobuf/io/tokenizer.cc
+++ b/src/google/protobuf/io/tokenizer.cc
@@ -375,7 +375,7 @@ void Tokenizer::ConsumeString(char delimiter) {
// 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')) {
+ } else if (TryConsume('x')) {
if (!TryConsumeOne<HexDigit>()) {
AddError("Expected hex digits for escape sequence.");
}
diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc
index 6526056a..20d50a2c 100644
--- a/src/google/protobuf/io/tokenizer_unittest.cc
+++ b/src/google/protobuf/io/tokenizer_unittest.cc
@@ -875,6 +875,8 @@ ErrorCase kErrorCases[] = {
// String errors.
{ "'\\l' foo", true,
"0:2: Invalid escape sequence in string literal.\n" },
+ { "'\\X' 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,
diff --git a/src/google/protobuf/io/zero_copy_stream_impl_lite.cc b/src/google/protobuf/io/zero_copy_stream_impl_lite.cc
index 686e63f2..083beca4 100644
--- a/src/google/protobuf/io/zero_copy_stream_impl_lite.cc
+++ b/src/google/protobuf/io/zero_copy_stream_impl_lite.cc
@@ -157,6 +157,7 @@ StringOutputStream::~StringOutputStream() {
}
bool StringOutputStream::Next(void** data, int* size) {
+ GOOGLE_CHECK_NE(NULL, target_);
int old_size = target_->size();
// Grow the string.
@@ -188,14 +189,44 @@ bool StringOutputStream::Next(void** data, int* size) {
void StringOutputStream::BackUp(int count) {
GOOGLE_CHECK_GE(count, 0);
+ GOOGLE_CHECK_NE(NULL, target_);
GOOGLE_CHECK_LE(count, target_->size());
target_->resize(target_->size() - count);
}
int64 StringOutputStream::ByteCount() const {
+ GOOGLE_CHECK_NE(NULL, target_);
return target_->size();
}
+void StringOutputStream::SetString(string* target) {
+ target_ = target;
+}
+
+// ===================================================================
+
+LazyStringOutputStream::LazyStringOutputStream(
+ ResultCallback<string*>* callback)
+ : StringOutputStream(NULL),
+ callback_(GOOGLE_CHECK_NOTNULL(callback)),
+ string_is_set_(false) {
+}
+
+LazyStringOutputStream::~LazyStringOutputStream() {
+}
+
+bool LazyStringOutputStream::Next(void** data, int* size) {
+ if (!string_is_set_) {
+ SetString(callback_->Run());
+ string_is_set_ = true;
+ }
+ return StringOutputStream::Next(data, size);
+}
+
+int64 LazyStringOutputStream::ByteCount() const {
+ return string_is_set_ ? StringOutputStream::ByteCount() : 0;
+}
+
// ===================================================================
CopyingInputStream::~CopyingInputStream() {}
diff --git a/src/google/protobuf/io/zero_copy_stream_impl_lite.h b/src/google/protobuf/io/zero_copy_stream_impl_lite.h
index a598ef2e..1c397dea 100644
--- a/src/google/protobuf/io/zero_copy_stream_impl_lite.h
+++ b/src/google/protobuf/io/zero_copy_stream_impl_lite.h
@@ -148,6 +148,9 @@ class LIBPROTOBUF_EXPORT StringOutputStream : public ZeroCopyOutputStream {
void BackUp(int count);
int64 ByteCount() const;
+ protected:
+ void SetString(string* target);
+
private:
static const int kMinimumSize = 16;
@@ -156,6 +159,27 @@ class LIBPROTOBUF_EXPORT StringOutputStream : public ZeroCopyOutputStream {
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOutputStream);
};
+// LazyStringOutputStream is a StringOutputStream with lazy acquisition of
+// the output string from a callback. The string is owned externally, and not
+// deleted in the stream destructor.
+class LIBPROTOBUF_EXPORT LazyStringOutputStream : public StringOutputStream {
+ public:
+ // Callback should be permanent (non-self-deleting). Ownership is transferred
+ // to the LazyStringOutputStream.
+ explicit LazyStringOutputStream(ResultCallback<string*>* callback);
+ ~LazyStringOutputStream();
+
+ // implements ZeroCopyOutputStream, overriding StringOutputStream -----------
+ bool Next(void** data, int* size);
+ int64 ByteCount() const;
+
+ private:
+ const scoped_ptr<ResultCallback<string*> > callback_;
+ bool string_is_set_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LazyStringOutputStream);
+};
+
// Note: There is no StringInputStream. Instead, just create an
// ArrayInputStream as follows:
// ArrayInputStream input(str.data(), str.size());
diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h
index 52fba603..dfc62420 100644
--- a/src/google/protobuf/map.h
+++ b/src/google/protobuf/map.h
@@ -188,7 +188,6 @@ class LIBPROTOBUF_EXPORT MapKey {
case FieldDescriptor::CPPTYPE_ENUM:
case FieldDescriptor::CPPTYPE_MESSAGE:
GOOGLE_LOG(FATAL) << "Can't get here.";
- return false;
}
GOOGLE_LOG(FATAL) << "Can't get here.";
return false;
@@ -497,7 +496,7 @@ class Map {
insert(other.begin(), other.end());
}
template <class InputIt>
- explicit Map(const InputIt& first, const InputIt& last)
+ Map(const InputIt& first, const InputIt& last)
: arena_(NULL),
allocator_(arena_),
elements_(0, hasher(), key_equal(), allocator_),
@@ -584,21 +583,22 @@ class Map {
private:
typedef void DestructorSkippable_;
- Arena* arena_;
+ Arena* const arena_;
template <typename X>
friend class MapAllocator;
};
- public:
typedef MapAllocator<std::pair<const Key, MapPair<Key, T>*> > Allocator;
+ typedef hash_map<Key, value_type*, hash<Key>, equal_to<Key>, Allocator>
+ InnerMap;
+ public:
// Iterators
class const_iterator
: public std::iterator<std::forward_iterator_tag, value_type, ptrdiff_t,
const value_type*, const value_type&> {
- typedef typename hash_map<Key, value_type*, hash<Key>, equal_to<Key>,
- Allocator>::const_iterator InnerIt;
+ typedef typename InnerMap::const_iterator InnerIt;
public:
const_iterator() {}
@@ -625,8 +625,7 @@ class Map {
};
class iterator : public std::iterator<std::forward_iterator_tag, value_type> {
- typedef typename hash_map<Key, value_type*, hasher, equal_to<Key>,
- Allocator>::iterator InnerIt;
+ typedef typename InnerMap::iterator InnerIt;
public:
iterator() {}
@@ -742,8 +741,7 @@ class Map {
// Erase
size_type erase(const key_type& key) {
- typename hash_map<Key, value_type*, hash<Key>, equal_to<Key>,
- Allocator>::iterator it = elements_.find(key);
+ typename InnerMap::iterator it = elements_.find(key);
if (it == elements_.end()) {
return 0;
} else {
@@ -813,7 +811,7 @@ class Map {
Arena* arena_;
Allocator allocator_;
- hash_map<Key, value_type*, hash<Key>, equal_to<Key>, Allocator> elements_;
+ InnerMap elements_;
int default_enum_value_;
friend class ::google::protobuf::Arena;
@@ -852,7 +850,6 @@ struct hash<google::protobuf::MapKey> {
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
GOOGLE_LOG(FATAL) << "Can't get here.";
- return 0;
}
GOOGLE_LOG(FATAL) << "Can't get here.";
return 0;
@@ -877,7 +874,6 @@ struct hash<google::protobuf::MapKey> {
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
GOOGLE_LOG(FATAL) << "Can't get here.";
- return true;
}
GOOGLE_LOG(FATAL) << "Can't get here.";
return true;
diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h
index 7c27afd9..a4d9277e 100644
--- a/src/google/protobuf/message.h
+++ b/src/google/protobuf/message.h
@@ -739,9 +739,9 @@ class LIBPROTOBUF_EXPORT Reflection {
// specifyed by 'field' passing ownership to the message.
// TODO(tmarek): Make virtual after all subclasses have been
// updated.
- virtual void AddAllocatedMessage(Message* message,
- const FieldDescriptor* field,
- Message* new_entry) const {}
+ virtual void AddAllocatedMessage(Message* /* message */,
+ const FieldDescriptor* /*field */,
+ Message* /* new_entry */) const {}
// Get a RepeatedFieldRef object that can be used to read the underlying
@@ -947,27 +947,27 @@ class LIBPROTOBUF_EXPORT Reflection {
// TODO(jieluo) - make the map APIs pure virtual after updating
// all the subclasses.
// Returns true if key is in map. Returns false if key is not in map field.
- virtual bool ContainsMapKey(const Message& message,
- const FieldDescriptor* field,
- const MapKey& key) const {
+ virtual bool ContainsMapKey(const Message& /* message*/,
+ const FieldDescriptor* /* field */,
+ const MapKey& /* key */) const {
return false;
}
// If key is in map field: Saves the value pointer to val and returns
// false. If key in not in map field: Insert the key into map, saves
// value pointer to val and retuns true.
- virtual bool InsertOrLookupMapValue(Message* message,
- const FieldDescriptor* field,
- const MapKey& key,
- MapValueRef* val) const {
+ virtual bool InsertOrLookupMapValue(Message* /* message */,
+ const FieldDescriptor* /* field */,
+ const MapKey& /* key */,
+ MapValueRef* /* val */) const {
return false;
}
// Delete and returns true if key is in the map field. Returns false
// otherwise.
- virtual bool DeleteMapValue(Message* message,
- const FieldDescriptor* field,
- const MapKey& key) const {
+ virtual bool DeleteMapValue(Message* /* mesage */,
+ const FieldDescriptor* /* field */,
+ const MapKey& /* key */) const {
return false;
}
@@ -987,15 +987,15 @@ class LIBPROTOBUF_EXPORT Reflection {
// Get the number of <key, value> pair of a map field. The result may be
// different from FieldSize which can have duplicate keys.
- virtual int MapSize(const Message& message,
- const FieldDescriptor* field) const {
+ virtual int MapSize(const Message& /* message */,
+ const FieldDescriptor* /* field */) const {
return 0;
}
// Help method for MapIterator.
friend class MapIterator;
virtual internal::MapFieldBase* MapData(
- Message* message, const FieldDescriptor* field) const {
+ Message* /* message */, const FieldDescriptor* /* field */) const {
return NULL;
}
diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h
index 5530fefe..5447fa42 100644
--- a/src/google/protobuf/repeated_field.h
+++ b/src/google/protobuf/repeated_field.h
@@ -208,10 +208,19 @@ class RepeatedField {
// sizeof(*this)
int SpaceUsedExcludingSelf() const;
- // Remove the element referenced by position.
+ // Removes the element referenced by position.
+ //
+ // Returns an iterator to the element immediately following the removed
+ // element.
+ //
+ // Invalidates all iterators at or after the removed element, including end().
iterator erase(const_iterator position);
- // Remove the elements in the range [first, last).
+ // Removes the elements in the range [first, last).
+ //
+ // Returns an iterator to the element immediately following the removed range.
+ //
+ // Invalidates all iterators at or after the removed range, including end().
iterator erase(const_iterator first, const_iterator last);
// Get the Arena on which this RepeatedField stores its elements.
@@ -565,7 +574,7 @@ class GenericTypeHandler {
template <typename GenericType>
GenericType* GenericTypeHandler<GenericType>::NewFromPrototype(
- const GenericType* prototype, ::google::protobuf::Arena* arena) {
+ const GenericType* /* prototype */, ::google::protobuf::Arena* arena) {
return New(arena);
}
template <typename GenericType>
@@ -618,7 +627,7 @@ void GenericTypeHandler<MessageLite>::Merge(const MessageLite& from,
// Message specialization bodies defined in message.cc. This split is necessary
// to allow proto2-lite (which includes this header) to be independent of
// Message.
-DECLARE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(Message);
+DECLARE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(Message)
#undef DECLARE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES
@@ -665,7 +674,7 @@ class LIBPROTOBUF_EXPORT StringTypeHandlerBase {
static inline ::google::protobuf::Arena* GetArena(string*) {
return NULL;
}
- static inline void* GetMaybeArenaPointer(string* value) {
+ static inline void* GetMaybeArenaPointer(string* /* value */) {
return NULL;
}
static inline void Delete(string* value, Arena* arena) {
@@ -683,7 +692,7 @@ class LIBPROTOBUF_EXPORT StringTypeHandlerBase {
class StringTypeHandler : public StringTypeHandlerBase {
public:
static int SpaceUsed(const string& value) {
- return sizeof(value) + StringSpaceUsedExcludingSelf(value);
+ return static_cast<int>(sizeof(value)) + StringSpaceUsedExcludingSelf(value);
}
};
@@ -885,10 +894,19 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase {
// so will trigger a GOOGLE_DCHECK-failure.
Element* ReleaseCleared();
- // Remove the element referenced by position.
+ // Removes the element referenced by position.
+ //
+ // Returns an iterator to the element immediately following the removed
+ // element.
+ //
+ // Invalidates all iterators at or after the removed element, including end().
iterator erase(const_iterator position);
// Removes the elements in the range [first, last).
+ //
+ // Returns an iterator to the element immediately following the removed range.
+ //
+ // Invalidates all iterators at or after the removed range, including end().
iterator erase(const_iterator first, const_iterator last);
// Gets the arena on which this RepeatedPtrField stores its elements.
diff --git a/src/google/protobuf/source_context.pb.cc b/src/google/protobuf/source_context.pb.cc
index c8397639..f2eb7ae7 100644
--- a/src/google/protobuf/source_context.pb.cc
+++ b/src/google/protobuf/source_context.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/source_context.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/source_context.pb.h"
+#include <google/protobuf/source_context.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -110,9 +111,9 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int SourceContext::kFileNameFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
SourceContext::SourceContext()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
diff --git a/src/google/protobuf/source_context.proto b/src/google/protobuf/source_context.proto
index e9a27d65..d76252ca 100644
--- a/src/google/protobuf/source_context.proto
+++ b/src/google/protobuf/source_context.proto
@@ -27,15 +27,16 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_multiple_files = true;
-option java_outer_classname = "SourceContextProto";
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option java_package = "com.google.protobuf";
+option java_outer_classname = "SourceContextProto";
+option java_multiple_files = true;
option java_generate_equals_and_hash = true;
-option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option objc_class_prefix = "GPB";
// `SourceContext` represents information about the source of a
diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc
index d5f89122..e020597a 100644
--- a/src/google/protobuf/struct.pb.cc
+++ b/src/google/protobuf/struct.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/struct.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/struct.pb.h"
+#include <google/protobuf/struct.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -212,9 +213,9 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Struct::kFieldsFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Struct::Struct()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -480,14 +481,14 @@ Struct::mutable_fields() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Value::kNullValueFieldNumber;
const int Value::kNumberValueFieldNumber;
const int Value::kStringValueFieldNumber;
const int Value::kBoolValueFieldNumber;
const int Value::kStructValueFieldNumber;
const int Value::kListValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Value::Value()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1218,9 +1219,9 @@ Value::KindCase Value::kind_case() const {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int ListValue::kValuesFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
ListValue::ListValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
diff --git a/src/google/protobuf/struct.proto b/src/google/protobuf/struct.proto
index b3e9e699..8562e2c1 100644
--- a/src/google/protobuf/struct.proto
+++ b/src/google/protobuf/struct.proto
@@ -27,15 +27,16 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_generate_equals_and_hash = true;
-option java_multiple_files = true;
-option java_outer_classname = "StructProto";
-option java_package = "com.google.protobuf";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "StructProto";
+option java_multiple_files = true;
+option java_generate_equals_and_hash = true;
option objc_class_prefix = "GPB";
diff --git a/src/google/protobuf/stubs/atomicops.h b/src/google/protobuf/stubs/atomicops.h
index 31e2b782..cb93227f 100644
--- a/src/google/protobuf/stubs/atomicops.h
+++ b/src/google/protobuf/stubs/atomicops.h
@@ -76,7 +76,7 @@ typedef int32 Atomic32;
#ifdef GOOGLE_PROTOBUF_ARCH_64_BIT
// We need to be able to go between Atomic64 and AtomicWord implicitly. This
// means Atomic64 and AtomicWord should be the same type on 64-bit.
-#if defined(__ILP32__) || defined(GOOGLE_PROTOBUF_OS_NACL) || defined(GOOGLE_PROTOBUF_ARCH_SPARC)
+#if defined(__ILP32__) || defined(GOOGLE_PROTOBUF_OS_NACL)
// NaCl's intptr_t is not actually 64-bits on 64-bit!
// http://code.google.com/p/nativeclient/issues/detail?id=1162
// sparcv9's pointer type is 32bits
diff --git a/src/google/protobuf/stubs/callback.h b/src/google/protobuf/stubs/callback.h
index 6da530d3..87271c5e 100644
--- a/src/google/protobuf/stubs/callback.h
+++ b/src/google/protobuf/stubs/callback.h
@@ -78,6 +78,18 @@ class LIBPROTOBUF_EXPORT Closure {
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Closure);
};
+template<typename R>
+class ResultCallback {
+ public:
+ ResultCallback() {}
+ virtual ~ResultCallback() {}
+
+ virtual R Run() = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback);
+};
+
template<typename R, typename A1>
class LIBPROTOBUF_EXPORT ResultCallback1 {
public:
@@ -240,6 +252,50 @@ class MethodClosure2 : public Closure {
Arg2 arg2_;
};
+template<typename R>
+class FunctionResultCallback_0_0 : public ResultCallback<R> {
+ public:
+ typedef R (*FunctionType)();
+
+ FunctionResultCallback_0_0(FunctionType function, bool self_deleting)
+ : function_(function), self_deleting_(self_deleting) {}
+ ~FunctionResultCallback_0_0() {}
+
+ R Run() {
+ bool needs_delete = self_deleting_; // read in case callback deletes
+ R result = function_();
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ FunctionType function_;
+ bool self_deleting_;
+};
+
+template<typename R, typename P1>
+class FunctionResultCallback_1_0 : public ResultCallback<R> {
+ public:
+ typedef R (*FunctionType)(P1);
+
+ FunctionResultCallback_1_0(FunctionType function, bool self_deleting,
+ P1 p1)
+ : function_(function), self_deleting_(self_deleting), p1_(p1) {}
+ ~FunctionResultCallback_1_0() {}
+
+ R Run() {
+ bool needs_delete = self_deleting_; // read in case callback deletes
+ R result = function_(p1_);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ FunctionType function_;
+ bool self_deleting_;
+ P1 p1_;
+};
+
template<typename R, typename Arg1>
class FunctionResultCallback_0_1 : public ResultCallback1<R, Arg1> {
public:
@@ -408,6 +464,33 @@ inline Closure* NewPermanentCallback(
object, method, false, arg1, arg2);
}
+// See ResultCallback
+template<typename R>
+inline ResultCallback<R>* NewCallback(R (*function)()) {
+ return new internal::FunctionResultCallback_0_0<R>(function, true);
+}
+
+// See ResultCallback
+template<typename R>
+inline ResultCallback<R>* NewPermanentCallback(R (*function)()) {
+ return new internal::FunctionResultCallback_0_0<R>(function, false);
+}
+
+// See ResultCallback
+template<typename R, typename P1>
+inline ResultCallback<R>* NewCallback(R (*function)(P1), P1 p1) {
+ return new internal::FunctionResultCallback_1_0<R, P1>(
+ function, true, p1);
+}
+
+// See ResultCallback
+template<typename R, typename P1>
+inline ResultCallback<R>* NewPermanentCallback(
+ R (*function)(P1), P1 p1) {
+ return new internal::FunctionResultCallback_1_0<R, P1>(
+ function, false, p1);
+}
+
// See ResultCallback1
template<typename R, typename A1>
inline ResultCallback1<R, A1>* NewCallback(R (*function)(A1)) {
diff --git a/src/google/protobuf/stubs/platform_macros.h b/src/google/protobuf/stubs/platform_macros.h
index 338d22f0..22b35723 100644
--- a/src/google/protobuf/stubs/platform_macros.h
+++ b/src/google/protobuf/stubs/platform_macros.h
@@ -65,7 +65,7 @@
#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
#elif defined(sparc)
#define GOOGLE_PROTOBUF_ARCH_SPARC 1
-#ifdef SOLARIS_64BIT_ENABLED
+#if defined(__sparc_v9__) || defined(__sparcv9) || defined(__arch64__)
#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
#else
#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
@@ -97,6 +97,8 @@ GOOGLE_PROTOBUF_PLATFORM_ERROR
#if TARGET_OS_IPHONE
#define GOOGLE_PROTOBUF_OS_IPHONE
#endif
+#elif defined(__EMSCRIPTEN__)
+#define GOOGLE_PROTOBUF_OS_EMSCRIPTEN
#elif defined(__native_client__)
#define GOOGLE_PROTOBUF_OS_NACL
#elif defined(sun)
diff --git a/src/google/protobuf/stubs/port.h b/src/google/protobuf/stubs/port.h
index b35a3afe..1036dff1 100644
--- a/src/google/protobuf/stubs/port.h
+++ b/src/google/protobuf/stubs/port.h
@@ -53,7 +53,7 @@
#if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
#define PROTOBUF_LITTLE_ENDIAN 1
#endif
- #if defined(_MSC_VER) && _MSC_VER >= 1300
+ #if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER)
// If MSVC has "/RTCc" set, it will complain about truncating casts at
// runtime. This file contains some intentional truncating casts.
#pragma runtime_checks("c", off)
diff --git a/src/google/protobuf/stubs/statusor.h b/src/google/protobuf/stubs/statusor.h
index a9d2b374..ad848701 100644
--- a/src/google/protobuf/stubs/statusor.h
+++ b/src/google/protobuf/stubs/statusor.h
@@ -224,14 +224,14 @@ inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<T>& other) {
template<typename T>
template<typename U>
inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
- : status_(other.status_), value_(other.value_) {
+ : status_(other.status_), value_(other.status_.ok() ? other.value_ : NULL) {
}
template<typename T>
template<typename U>
inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
status_ = other.status_;
- value_ = other.value_;
+ if (status_.ok()) value_ = other.value_;
return *this;
}
diff --git a/src/google/protobuf/stubs/stringpiece_unittest.cc b/src/google/protobuf/stubs/stringpiece_unittest.cc
index 43737a57..a52d81f8 100644
--- a/src/google/protobuf/stubs/stringpiece_unittest.cc
+++ b/src/google/protobuf/stubs/stringpiece_unittest.cc
@@ -746,23 +746,6 @@ TEST(StringPiece, Comparisons2) {
EXPECT_TRUE(abc.ends_with("nopqrstuvwxyz"));
}
-TEST(StringPiece, HashFunction) {
- hash_set<StringPiece> set;
-
- set.insert(StringPiece("hello"));
- EXPECT_EQ(1, set.size());
-
- // Insert a StringPiece of the same value again and should NOT increment
- // size of the set.
- set.insert(StringPiece("hello"));
- EXPECT_EQ(1, set.size());
-
- // Insert a StringPiece with different value and check that size of the set
- // has been increment by one.
- set.insert(StringPiece("world"));
- EXPECT_EQ(2, set.size());
-}
-
TEST(ComparisonOpsTest, StringCompareNotAmbiguous) {
EXPECT_EQ("hello", string("hello"));
EXPECT_LT("hello", string("world"));
diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc
index 8442f2ce..7ba92e8f 100644
--- a/src/google/protobuf/stubs/strutil.cc
+++ b/src/google/protobuf/stubs/strutil.cc
@@ -524,27 +524,81 @@ int CEscapeInternal(const char* src, int src_len, char* dest,
return used;
}
-int CEscapeString(const char* src, int src_len, char* dest, int dest_len) {
- return CEscapeInternal(src, src_len, dest, dest_len, false, false);
+// Calculates the length of the C-style escaped version of 'src'.
+// Assumes that non-printable characters are escaped using octal sequences, and
+// that UTF-8 bytes are not handled specially.
+static inline size_t CEscapedLength(StringPiece src) {
+ static char c_escaped_len[256] = {
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4, // \t, \n, \r
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // ", '
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // '0'..'9'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'..'O'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, // 'P'..'Z', '\'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'..'o'
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, // 'p'..'z', DEL
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ };
+
+ size_t escaped_len = 0;
+ for (int i = 0; i < src.size(); ++i) {
+ unsigned char c = static_cast<unsigned char>(src[i]);
+ escaped_len += c_escaped_len[c];
+ }
+ return escaped_len;
}
// ----------------------------------------------------------------------
-// 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.
+// Escapes 'src' using C-style escape sequences, and appends the escaped string
+// to 'dest'. This version is faster than calling CEscapeInternal as it computes
+// the required space using a lookup table, and also does not do any special
+// handling for Hex or UTF-8 characters.
// ----------------------------------------------------------------------
+void CEscapeAndAppend(StringPiece src, string* dest) {
+ size_t escaped_len = CEscapedLength(src);
+ if (escaped_len == src.size()) {
+ dest->append(src.data(), src.size());
+ return;
+ }
+
+ size_t cur_dest_len = dest->size();
+ dest->resize(cur_dest_len + escaped_len);
+ char* append_ptr = &(*dest)[cur_dest_len];
+
+ for (int i = 0; i < src.size(); ++i) {
+ unsigned char c = static_cast<unsigned char>(src[i]);
+ switch (c) {
+ case '\n': *append_ptr++ = '\\'; *append_ptr++ = 'n'; break;
+ case '\r': *append_ptr++ = '\\'; *append_ptr++ = 'r'; break;
+ case '\t': *append_ptr++ = '\\'; *append_ptr++ = 't'; break;
+ case '\"': *append_ptr++ = '\\'; *append_ptr++ = '\"'; break;
+ case '\'': *append_ptr++ = '\\'; *append_ptr++ = '\''; break;
+ case '\\': *append_ptr++ = '\\'; *append_ptr++ = '\\'; break;
+ default:
+ if (!isprint(c)) {
+ *append_ptr++ = '\\';
+ *append_ptr++ = '0' + c / 64;
+ *append_ptr++ = '0' + (c % 64) / 8;
+ *append_ptr++ = '0' + c % 8;
+ } else {
+ *append_ptr++ = c;
+ }
+ break;
+ }
+ }
+}
+
string CEscape(const string& src) {
- const int dest_length = src.size() * 4 + 1; // Maximum possible expansion
- scoped_array<char> dest(new char[dest_length]);
- const int len = CEscapeInternal(src.data(), src.size(),
- dest.get(), dest_length, false, false);
- GOOGLE_DCHECK_GE(len, 0);
- return string(dest.get(), len);
+ string dest;
+ CEscapeAndAppend(src, &dest);
+ return dest;
}
namespace strings {
diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h
index b22066b6..27d47575 100644
--- a/src/google/protobuf/stubs/strutil.h
+++ b/src/google/protobuf/stubs/strutil.h
@@ -314,26 +314,20 @@ LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest,
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.
+// CEscape()
+// Escapes 'src' using C-style escape sequences and returns the resulting
+// string.
//
-// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped.
+// Escaped chars: \n, \r, \t, ", ', \, and !isprint().
// ----------------------------------------------------------------------
-LIBPROTOBUF_EXPORT int CEscapeString(const char* src, int src_len,
- char* dest, int dest_len);
+LIBPROTOBUF_EXPORT string CEscape(const string& src);
// ----------------------------------------------------------------------
-// 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.
+// CEscapeAndAppend()
+// Escapes 'src' using C-style escape sequences, and appends the escaped
+// string to 'dest'.
// ----------------------------------------------------------------------
-LIBPROTOBUF_EXPORT string CEscape(const string& src);
+LIBPROTOBUF_EXPORT void CEscapeAndAppend(StringPiece src, string* dest);
namespace strings {
// Like CEscape() but does not escape bytes with the upper bit set.
diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc
index 38b9069b..b0a5ce63 100644
--- a/src/google/protobuf/text_format.cc
+++ b/src/google/protobuf/text_format.cc
@@ -46,6 +46,7 @@
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/io/strtod.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -380,6 +381,7 @@ class TextFormat::Parser::ParserImpl {
string full_type_name, prefix;
DO(ConsumeAnyTypeUrl(&full_type_name, &prefix));
DO(Consume("]"));
+ TryConsume(":"); // ':' is optional between message labels and values.
string serialized_value;
DO(ConsumeAnyValue(full_type_name,
message->GetDescriptor()->file()->pool(),
@@ -389,7 +391,6 @@ class TextFormat::Parser::ParserImpl {
string(prefix + full_type_name));
reflection->SetString(message, any_value_field, serialized_value);
return true;
- // Fall through.
}
if (TryConsume("[")) {
// Extension.
@@ -656,7 +657,7 @@ class TextFormat::Parser::ParserImpl {
case FieldDescriptor::CPPTYPE_FLOAT: {
double value;
DO(ConsumeDouble(&value));
- SET_FIELD(Float, static_cast<float>(value));
+ SET_FIELD(Float, io::SafeDoubleToFloat(value));
break;
}
@@ -1357,7 +1358,10 @@ string TextFormat::FieldValuePrinter::PrintDouble(double val) const {
return SimpleDtoa(val);
}
string TextFormat::FieldValuePrinter::PrintString(const string& val) const {
- return StrCat("\"", CEscape(val), "\"");
+ string printed("\"");
+ CEscapeAndAppend(val, &printed);
+ printed.push_back('\"');
+ return printed;
}
string TextFormat::FieldValuePrinter::PrintBytes(const string& val) const {
return PrintString(val);
@@ -1423,7 +1427,8 @@ TextFormat::Printer::Printer()
use_short_repeated_primitives_(false),
hide_unknown_fields_(false),
print_message_fields_in_index_order_(false),
- expand_any_(false) {
+ expand_any_(false),
+ truncate_string_field_longer_than_(0LL) {
SetUseUtf8StringEscaping(false);
}
@@ -1775,11 +1780,21 @@ void TextFormat::Printer::PrintFieldValue(
? reflection->GetRepeatedStringReference(
message, field, index, &scratch)
: reflection->GetStringReference(message, field, &scratch);
+ int64 size = value.size();
+ if (truncate_string_field_longer_than_ > 0) {
+ size = std::min(truncate_string_field_longer_than_,
+ static_cast<int64>(value.size()));
+ }
+ string truncated_value(value.substr(0, size) + "...<truncated>...");
+ const string* value_to_print = &value;
+ if (size < value.size()) {
+ value_to_print = &truncated_value;
+ }
if (field->type() == FieldDescriptor::TYPE_STRING) {
- generator.Print(printer->PrintString(value));
+ generator.Print(printer->PrintString(*value_to_print));
} else {
GOOGLE_DCHECK_EQ(field->type(), FieldDescriptor::TYPE_BYTES);
- generator.Print(printer->PrintBytes(value));
+ generator.Print(printer->PrintBytes(*value_to_print));
}
break;
}
@@ -1926,14 +1941,10 @@ void TextFormat::Printer::PrintUnknownFields(
} else {
// This field is not parseable as a Message.
// So it is probably just a plain string.
- generator.Print(": \"");
- generator.Print(CEscape(value));
- generator.Print("\"");
- if (single_line_mode_) {
- generator.Print(" ");
- } else {
- generator.Print("\n");
- }
+ string printed(": \"");
+ CEscapeAndAppend(value, &printed);
+ printed.append(single_line_mode_ ? "\" " : "\"\n");
+ generator.Print(printed);
}
break;
}
diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h
index 6717aecd..ef3d4a8f 100644
--- a/src/google/protobuf/text_format.h
+++ b/src/google/protobuf/text_format.h
@@ -219,6 +219,18 @@ class LIBPROTOBUF_EXPORT TextFormat {
expand_any_ = expand;
}
+ // If non-zero, we truncate all string fields that are longer than this
+ // threshold. This is useful when the proto message has very long strings,
+ // e.g., dump of encoded image file.
+ //
+ // NOTE(hfgong): Setting a non-zero value breaks round-trip safe
+ // property of TextFormat::Printer. That is, from the printed message, we
+ // cannot fully recover the original string field any more.
+ void SetTruncateStringFieldLongerThan(
+ const int64 truncate_string_field_longer_than) {
+ truncate_string_field_longer_than_ = truncate_string_field_longer_than;
+ }
+
// Register a custom field-specific FieldValuePrinter for fields
// with a particular FieldDescriptor.
// Returns "true" if the registration succeeded, or "false", if there is
@@ -286,6 +298,8 @@ class LIBPROTOBUF_EXPORT TextFormat {
bool expand_any_;
+ int64 truncate_string_field_longer_than_;
+
google::protobuf::scoped_ptr<const FieldValuePrinter> default_field_value_printer_;
typedef map<const FieldDescriptor*,
const FieldValuePrinter*> CustomPrinterMap;
diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc
index 2ee0ec29..c1c4402c 100644
--- a/src/google/protobuf/timestamp.pb.cc
+++ b/src/google/protobuf/timestamp.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/timestamp.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/timestamp.pb.h"
+#include <google/protobuf/timestamp.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -82,9 +83,9 @@ void protobuf_AddDesc_google_2fprotobuf_2ftimestamp_2eproto() {
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\037google/protobuf/timestamp.proto\022\017googl"
"e.protobuf\"+\n\tTimestamp\022\017\n\007seconds\030\001 \001(\003"
- "\022\r\n\005nanos\030\002 \001(\005BQ\n\023com.google.protobufB\016"
- "TimestampProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Proto"
- "buf.WellKnownTypesb\006proto3", 186);
+ "\022\r\n\005nanos\030\002 \001(\005BT\n\023com.google.protobufB\016"
+ "TimestampProtoP\001\240\001\001\370\001\001\242\002\003GPB\252\002\036Google.Pr"
+ "otobuf.WellKnownTypesb\006proto3", 189);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"google/protobuf/timestamp.proto", &protobuf_RegisterTypes);
Timestamp::default_instance_ = new Timestamp();
@@ -111,10 +112,10 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Timestamp::kSecondsFieldNumber;
const int Timestamp::kNanosFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Timestamp::Timestamp()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -122,6 +123,14 @@ Timestamp::Timestamp()
// @@protoc_insertion_point(constructor:google.protobuf.Timestamp)
}
+Timestamp::Timestamp(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.Timestamp)
+}
+
void Timestamp::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -147,10 +156,20 @@ Timestamp::~Timestamp() {
}
void Timestamp::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void Timestamp::ArenaDtor(void* object) {
+ Timestamp* _this = reinterpret_cast< Timestamp* >(object);
+ (void)_this;
+}
+void Timestamp::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void Timestamp::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -169,11 +188,7 @@ const Timestamp& Timestamp::default_instance() {
Timestamp* Timestamp::default_instance_ = NULL;
Timestamp* Timestamp::New(::google::protobuf::Arena* arena) const {
- Timestamp* n = new Timestamp;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<Timestamp>(arena);
}
void Timestamp::Clear() {
@@ -349,6 +364,18 @@ bool Timestamp::IsInitialized() const {
void Timestamp::Swap(Timestamp* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ Timestamp temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void Timestamp::UnsafeArenaSwap(Timestamp* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void Timestamp::InternalSwap(Timestamp* other) {
diff --git a/src/google/protobuf/timestamp.pb.h b/src/google/protobuf/timestamp.pb.h
index 85fc1242..7bf62597 100644
--- a/src/google/protobuf/timestamp.pb.h
+++ b/src/google/protobuf/timestamp.pb.h
@@ -53,9 +53,14 @@ class LIBPROTOBUF_EXPORT Timestamp : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const Timestamp& default_instance();
+ void UnsafeArenaSwap(Timestamp* other);
void Swap(Timestamp* other);
// implements Message ----------------------------------------------
@@ -82,6 +87,11 @@ class LIBPROTOBUF_EXPORT Timestamp : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(Timestamp* other);
+ protected:
+ explicit Timestamp(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -113,6 +123,9 @@ class LIBPROTOBUF_EXPORT Timestamp : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::int64 seconds_;
::google::protobuf::int32 nanos_;
diff --git a/src/google/protobuf/timestamp.proto b/src/google/protobuf/timestamp.proto
index 06b60e6f..b51fc3fa 100644
--- a/src/google/protobuf/timestamp.proto
+++ b/src/google/protobuf/timestamp.proto
@@ -27,15 +27,17 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
-option java_generate_equals_and_hash = true;
-option java_multiple_files = true;
-option java_outer_classname = "TimestampProto";
-option java_package = "com.google.protobuf";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TimestampProto";
+option java_multiple_files = true;
+option java_generate_equals_and_hash = true;
option objc_class_prefix = "GPB";
// A Timestamp represents a point in time independent of any time zone
@@ -92,6 +94,7 @@ option objc_class_prefix = "GPB";
// nanos = int((now - seconds) * 10**9)
// timestamp = Timestamp(seconds=seconds, nanos=nanos)
//
+//
message Timestamp {
// Represents seconds of UTC time since Unix epoch
diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc
index 8f993561..7b47b3bd 100644
--- a/src/google/protobuf/type.pb.cc
+++ b/src/google/protobuf/type.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/type.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/type.pb.h"
+#include <google/protobuf/type.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -70,7 +71,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2ftype_2eproto() {
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Type, _internal_metadata_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Type, _is_default_instance_));
Field_descriptor_ = file->message_type(1);
- static const int Field_offsets_[9] = {
+ static const int Field_offsets_[10] = {
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, kind_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, cardinality_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, number_),
@@ -80,6 +81,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2ftype_2eproto() {
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, packed_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, options_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, json_name_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, default_value_),
};
Field_reflection_ =
::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection(
@@ -203,37 +205,37 @@ void protobuf_AddDesc_google_2fprotobuf_2ftype_2eproto() {
"\030\004 \003(\0132\027.google.protobuf.Option\0226\n\016sourc"
"e_context\030\005 \001(\0132\036.google.protobuf.Source"
"Context\022\'\n\006syntax\030\006 \001(\0162\027.google.protobu"
- "f.Syntax\"\276\005\n\005Field\022)\n\004kind\030\001 \001(\0162\033.googl"
+ "f.Syntax\"\325\005\n\005Field\022)\n\004kind\030\001 \001(\0162\033.googl"
"e.protobuf.Field.Kind\0227\n\013cardinality\030\002 \001"
"(\0162\".google.protobuf.Field.Cardinality\022\016"
"\n\006number\030\003 \001(\005\022\014\n\004name\030\004 \001(\t\022\020\n\010type_url"
"\030\006 \001(\t\022\023\n\013oneof_index\030\007 \001(\005\022\016\n\006packed\030\010 "
"\001(\010\022(\n\007options\030\t \003(\0132\027.google.protobuf.O"
- "ption\022\021\n\tjson_name\030\n \001(\t\"\310\002\n\004Kind\022\020\n\014TYP"
- "E_UNKNOWN\020\000\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FLO"
- "AT\020\002\022\016\n\nTYPE_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_GROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nT"
- "YPE_BYTES\020\014\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_ENU"
- "M\020\016\022\021\n\rTYPE_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64\020"
- "\020\022\017\n\013TYPE_SINT32\020\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013C"
- "ardinality\022\027\n\023CARDINALITY_UNKNOWN\020\000\022\030\n\024C"
- "ARDINALITY_OPTIONAL\020\001\022\030\n\024CARDINALITY_REQ"
- "UIRED\020\002\022\030\n\024CARDINALITY_REPEATED\020\003\"\316\001\n\004En"
- "um\022\014\n\004name\030\001 \001(\t\022-\n\tenumvalue\030\002 \003(\0132\032.go"
- "ogle.protobuf.EnumValue\022(\n\007options\030\003 \003(\013"
- "2\027.google.protobuf.Option\0226\n\016source_cont"
- "ext\030\004 \001(\0132\036.google.protobuf.SourceContex"
- "t\022\'\n\006syntax\030\005 \001(\0162\027.google.protobuf.Synt"
- "ax\"S\n\tEnumValue\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030"
- "\002 \001(\005\022(\n\007options\030\003 \003(\0132\027.google.protobuf"
- ".Option\";\n\006Option\022\014\n\004name\030\001 \001(\t\022#\n\005value"
- "\030\002 \001(\0132\024.google.protobuf.Any*.\n\006Syntax\022\021"
- "\n\rSYNTAX_PROTO2\020\000\022\021\n\rSYNTAX_PROTO3\020\001BL\n\023"
- "com.google.protobufB\tTypeProtoP\001\240\001\001\242\002\003GP"
- "B\252\002\036Google.Protobuf.WellKnownTypesb\006prot"
- "o3", 1522);
+ "ption\022\021\n\tjson_name\030\n \001(\t\022\025\n\rdefault_valu"
+ "e\030\013 \001(\t\"\310\002\n\004Kind\022\020\n\014TYPE_UNKNOWN\020\000\022\017\n\013TY"
+ "PE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT6"
+ "4\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014"
+ "TYPE_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_GROUP\020\n"
+ "\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TY"
+ "PE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXE"
+ "D32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020"
+ "\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013Cardinality\022\027\n\023CAR"
+ "DINALITY_UNKNOWN\020\000\022\030\n\024CARDINALITY_OPTION"
+ "AL\020\001\022\030\n\024CARDINALITY_REQUIRED\020\002\022\030\n\024CARDIN"
+ "ALITY_REPEATED\020\003\"\316\001\n\004Enum\022\014\n\004name\030\001 \001(\t\022"
+ "-\n\tenumvalue\030\002 \003(\0132\032.google.protobuf.Enu"
+ "mValue\022(\n\007options\030\003 \003(\0132\027.google.protobu"
+ "f.Option\0226\n\016source_context\030\004 \001(\0132\036.googl"
+ "e.protobuf.SourceContext\022\'\n\006syntax\030\005 \001(\016"
+ "2\027.google.protobuf.Syntax\"S\n\tEnumValue\022\014"
+ "\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\022(\n\007options\030"
+ "\003 \003(\0132\027.google.protobuf.Option\";\n\006Option"
+ "\022\014\n\004name\030\001 \001(\t\022#\n\005value\030\002 \001(\0132\024.google.p"
+ "rotobuf.Any*.\n\006Syntax\022\021\n\rSYNTAX_PROTO2\020\000"
+ "\022\021\n\rSYNTAX_PROTO3\020\001BL\n\023com.google.protob"
+ "ufB\tTypeProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Protob"
+ "uf.WellKnownTypesb\006proto3", 1545);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"google/protobuf/type.proto", &protobuf_RegisterTypes);
Type::default_instance_ = new Type();
@@ -282,14 +284,14 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Type::kNameFieldNumber;
const int Type::kFieldsFieldNumber;
const int Type::kOneofsFieldNumber;
const int Type::kOptionsFieldNumber;
const int Type::kSourceContextFieldNumber;
const int Type::kSyntaxFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Type::Type()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -967,7 +969,7 @@ bool Field_Kind_IsValid(int value) {
}
}
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const Field_Kind Field::TYPE_UNKNOWN;
const Field_Kind Field::TYPE_DOUBLE;
const Field_Kind Field::TYPE_FLOAT;
@@ -990,7 +992,7 @@ const Field_Kind Field::TYPE_SINT64;
const Field_Kind Field::Kind_MIN;
const Field_Kind Field::Kind_MAX;
const int Field::Kind_ARRAYSIZE;
-#endif // _MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
const ::google::protobuf::EnumDescriptor* Field_Cardinality_descriptor() {
protobuf_AssignDescriptorsOnce();
return Field_Cardinality_descriptor_;
@@ -1007,7 +1009,7 @@ bool Field_Cardinality_IsValid(int value) {
}
}
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const Field_Cardinality Field::CARDINALITY_UNKNOWN;
const Field_Cardinality Field::CARDINALITY_OPTIONAL;
const Field_Cardinality Field::CARDINALITY_REQUIRED;
@@ -1015,8 +1017,8 @@ const Field_Cardinality Field::CARDINALITY_REPEATED;
const Field_Cardinality Field::Cardinality_MIN;
const Field_Cardinality Field::Cardinality_MAX;
const int Field::Cardinality_ARRAYSIZE;
-#endif // _MSC_VER
-#ifndef _MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Field::kKindFieldNumber;
const int Field::kCardinalityFieldNumber;
const int Field::kNumberFieldNumber;
@@ -1026,7 +1028,8 @@ const int Field::kOneofIndexFieldNumber;
const int Field::kPackedFieldNumber;
const int Field::kOptionsFieldNumber;
const int Field::kJsonNameFieldNumber;
-#endif // !_MSC_VER
+const int Field::kDefaultValueFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Field::Field()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1058,6 +1061,7 @@ void Field::SharedCtor() {
oneof_index_ = 0;
packed_ = false;
json_name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ default_value_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
Field::~Field() {
@@ -1069,6 +1073,7 @@ void Field::SharedDtor() {
name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
type_url_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
json_name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ default_value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
if (this != default_instance_) {
}
}
@@ -1113,6 +1118,7 @@ void Field::Clear() {
type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
packed_ = false;
json_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
#undef ZR_HELPER_
#undef ZR_
@@ -1270,6 +1276,23 @@ bool Field::MergePartialFromCodedStream(
} else {
goto handle_unusual;
}
+ if (input->ExpectTag(90)) goto parse_default_value;
+ break;
+ }
+
+ // optional string default_value = 11;
+ case 11: {
+ if (tag == 90) {
+ parse_default_value:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_default_value()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->default_value().data(), this->default_value().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "google.protobuf.Field.default_value"));
+ } else {
+ goto handle_unusual;
+ }
if (input->ExpectAtEnd()) goto success;
break;
}
@@ -1361,6 +1384,16 @@ void Field::SerializeWithCachedSizes(
10, this->json_name(), output);
}
+ // optional string default_value = 11;
+ if (this->default_value().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->default_value().data(), this->default_value().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "google.protobuf.Field.default_value");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 11, this->default_value(), output);
+ }
+
// @@protoc_insertion_point(serialize_end:google.protobuf.Field)
}
@@ -1434,6 +1467,17 @@ void Field::SerializeWithCachedSizes(
10, this->json_name(), target);
}
+ // optional string default_value = 11;
+ if (this->default_value().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->default_value().data(), this->default_value().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "google.protobuf.Field.default_value");
+ target =
+ ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+ 11, this->default_value(), target);
+ }
+
// @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Field)
return target;
}
@@ -1493,6 +1537,13 @@ int Field::ByteSize() const {
this->json_name());
}
+ // optional string default_value = 11;
+ if (this->default_value().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->default_value());
+ }
+
// repeated .google.protobuf.Option options = 9;
total_size += 1 * this->options_size();
for (int i = 0; i < this->options_size(); i++) {
@@ -1549,6 +1600,10 @@ void Field::MergeFrom(const Field& from) {
json_name_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.json_name_);
}
+ if (from.default_value().size() > 0) {
+
+ default_value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.default_value_);
+ }
}
void Field::CopyFrom(const ::google::protobuf::Message& from) {
@@ -1582,6 +1637,7 @@ void Field::InternalSwap(Field* other) {
std::swap(packed_, other->packed_);
options_.UnsafeArenaSwap(&other->options_);
json_name_.Swap(&other->json_name_);
+ default_value_.Swap(&other->default_value_);
_internal_metadata_.Swap(&other->_internal_metadata_);
std::swap(_cached_size_, other->_cached_size_);
}
@@ -1826,17 +1882,60 @@ void Field::clear_json_name() {
// @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.json_name)
}
+// optional string default_value = 11;
+void Field::clear_default_value() {
+ default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& Field::default_value() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.Field.default_value)
+ return default_value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Field::set_default_value(const ::std::string& value) {
+
+ default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:google.protobuf.Field.default_value)
+}
+ void Field::set_default_value(const char* value) {
+
+ default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:google.protobuf.Field.default_value)
+}
+ void Field::set_default_value(const char* value, size_t size) {
+
+ default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.Field.default_value)
+}
+ ::std::string* Field::mutable_default_value() {
+
+ // @@protoc_insertion_point(field_mutable:google.protobuf.Field.default_value)
+ return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* Field::release_default_value() {
+
+ return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Field::set_allocated_default_value(::std::string* default_value) {
+ if (default_value != NULL) {
+
+ } else {
+
+ }
+ default_value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), default_value);
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.default_value)
+}
+
#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Enum::kNameFieldNumber;
const int Enum::kEnumvalueFieldNumber;
const int Enum::kOptionsFieldNumber;
const int Enum::kSourceContextFieldNumber;
const int Enum::kSyntaxFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Enum::Enum()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -2379,11 +2478,11 @@ void Enum::clear_syntax() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int EnumValue::kNameFieldNumber;
const int EnumValue::kNumberFieldNumber;
const int EnumValue::kOptionsFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
EnumValue::EnumValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -2775,10 +2874,10 @@ EnumValue::options() const {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Option::kNameFieldNumber;
const int Option::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Option::Option()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h
index deda9213..76fe8a65 100644
--- a/src/google/protobuf/type.pb.h
+++ b/src/google/protobuf/type.pb.h
@@ -28,8 +28,8 @@
#include <google/protobuf/extension_set.h>
#include <google/protobuf/generated_enum_reflection.h>
#include <google/protobuf/unknown_field_set.h>
-#include "google/protobuf/any.pb.h"
-#include "google/protobuf/source_context.pb.h"
+#include <google/protobuf/any.pb.h>
+#include <google/protobuf/source_context.pb.h>
// @@protoc_insertion_point(includes)
namespace google {
@@ -471,6 +471,17 @@ class LIBPROTOBUF_EXPORT Field : public ::google::protobuf::Message {
::std::string* release_json_name();
void set_allocated_json_name(::std::string* json_name);
+ // optional string default_value = 11;
+ void clear_default_value();
+ static const int kDefaultValueFieldNumber = 11;
+ const ::std::string& default_value() const;
+ void set_default_value(const ::std::string& value);
+ void set_default_value(const char* value);
+ void set_default_value(const char* value, size_t size);
+ ::std::string* mutable_default_value();
+ ::std::string* release_default_value();
+ void set_allocated_default_value(::std::string* default_value);
+
// @@protoc_insertion_point(class_scope:google.protobuf.Field)
private:
@@ -484,6 +495,7 @@ class LIBPROTOBUF_EXPORT Field : public ::google::protobuf::Message {
::google::protobuf::internal::ArenaStringPtr type_url_;
::google::protobuf::RepeatedPtrField< ::google::protobuf::Option > options_;
::google::protobuf::internal::ArenaStringPtr json_name_;
+ ::google::protobuf::internal::ArenaStringPtr default_value_;
bool packed_;
mutable int _cached_size_;
friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2ftype_2eproto();
@@ -1264,6 +1276,49 @@ inline void Field::set_allocated_json_name(::std::string* json_name) {
// @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.json_name)
}
+// optional string default_value = 11;
+inline void Field::clear_default_value() {
+ default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& Field::default_value() const {
+ // @@protoc_insertion_point(field_get:google.protobuf.Field.default_value)
+ return default_value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Field::set_default_value(const ::std::string& value) {
+
+ default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:google.protobuf.Field.default_value)
+}
+inline void Field::set_default_value(const char* value) {
+
+ default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:google.protobuf.Field.default_value)
+}
+inline void Field::set_default_value(const char* value, size_t size) {
+
+ default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:google.protobuf.Field.default_value)
+}
+inline ::std::string* Field::mutable_default_value() {
+
+ // @@protoc_insertion_point(field_mutable:google.protobuf.Field.default_value)
+ return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* Field::release_default_value() {
+
+ return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Field::set_allocated_default_value(::std::string* default_value) {
+ if (default_value != NULL) {
+
+ } else {
+
+ }
+ default_value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), default_value);
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.default_value)
+}
+
// -------------------------------------------------------------------
// Enum
diff --git a/src/google/protobuf/type.proto b/src/google/protobuf/type.proto
index 4df95762..1c9cf53d 100644
--- a/src/google/protobuf/type.proto
+++ b/src/google/protobuf/type.proto
@@ -27,6 +27,7 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
syntax = "proto3";
package google.protobuf;
@@ -34,22 +35,22 @@ package google.protobuf;
import "google/protobuf/any.proto";
import "google/protobuf/source_context.proto";
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option java_package = "com.google.protobuf";
option java_outer_classname = "TypeProto";
option java_multiple_files = true;
option java_generate_equals_and_hash = true;
-option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option objc_class_prefix = "GPB";
-// A light-weight descriptor for a proto message type.
+// A protocol buffer message type.
message Type {
// The fully qualified message name.
string name = 1;
// The list of fields.
repeated Field fields = 2;
- // The list of oneof definitions.
- repeated string oneofs = 3; // The list of oneofs declared in this Type
- // The proto options.
+ // The list of types appearing in `oneof` definitions in this type.
+ repeated string oneofs = 3;
+ // The protocol buffer options.
repeated Option options = 4;
// The source context.
SourceContext source_context = 5;
@@ -57,9 +58,9 @@ message Type {
Syntax syntax = 6;
}
-// Field represents a single field of a message type.
+// A single field of a message type.
message Field {
- // Kind represents a basic field type.
+ // Basic field types.
enum Kind {
// Field type unknown.
TYPE_UNKNOWN = 0;
@@ -81,7 +82,7 @@ message Field {
TYPE_BOOL = 8;
// Field type string.
TYPE_STRING = 9;
- // Field type group (deprecated proto2 type)
+ // Field type group. Proto2 syntax only, and deprecated.
TYPE_GROUP = 10;
// Field type message.
TYPE_MESSAGE = 11;
@@ -101,38 +102,40 @@ message Field {
TYPE_SINT64 = 18;
};
- // Cardinality represents whether a field is optional, required, or
- // repeated.
+ // Whether a field is optional, required, or repeated.
enum Cardinality {
- // The field cardinality is unknown. Typically an error condition.
+ // For fields with unknown cardinality.
CARDINALITY_UNKNOWN = 0;
// For optional fields.
CARDINALITY_OPTIONAL = 1;
- // For required fields. Not used for proto3.
+ // For required fields. Proto2 syntax only.
CARDINALITY_REQUIRED = 2;
// For repeated fields.
CARDINALITY_REPEATED = 3;
};
- // The field kind.
+ // The field type.
Kind kind = 1;
- // The field cardinality, i.e. optional/required/repeated.
+ // The field cardinality.
Cardinality cardinality = 2;
- // The proto field number.
+ // The field number.
int32 number = 3;
// The field name.
string name = 4;
- // The type URL (without the scheme) when the type is MESSAGE or ENUM,
- // such as `type.googleapis.com/google.protobuf.Empty`.
+ // The field type URL, without the scheme, for message or enumeration
+ // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
string type_url = 6;
- // Index in Type.oneofs. Starts at 1. Zero means no oneof mapping.
+ // The index of the field type in `Type.oneofs`, for message or enumeration
+ // types. The first type has index 1; zero means the type is not in the list.
int32 oneof_index = 7;
// Whether to use alternative packed wire representation.
bool packed = 8;
- // The proto options.
+ // The protocol buffer options.
repeated Option options = 9;
- // The JSON name for this field.
+ // The field JSON name.
string json_name = 10;
+ // The string value of the default value of this field. Proto2 syntax only.
+ string default_value = 11;
}
// Enum type definition.
@@ -141,7 +144,7 @@ message Enum {
string name = 1;
// Enum value definitions.
repeated EnumValue enumvalue = 2;
- // Proto options for the enum type.
+ // Protocol buffer options.
repeated Option options = 3;
// The source context.
SourceContext source_context = 4;
@@ -155,22 +158,23 @@ message EnumValue {
string name = 1;
// Enum value number.
int32 number = 2;
- // Proto options for the enum value.
+ // Protocol buffer options.
repeated Option options = 3;
}
-// Proto option attached to messages/fields/enums etc.
+// A protocol buffer option, which can be attached to a message, field,
+// enumeration, etc.
message Option {
- // Proto option name.
+ // The option's name. For example, `"java_package"`.
string name = 1;
- // Proto option value.
+ // The option's value. For example, `"com.google.protobuf"`.
Any value = 2;
}
-// Syntax specifies the syntax in which a service element was defined.
+// The syntax in which a protocol buffer element is defined.
enum Syntax {
- // Syntax "proto2"
+ // Syntax `proto2`.
SYNTAX_PROTO2 = 0;
- // Syntax "proto3"
+ // Syntax `proto3`.
SYNTAX_PROTO3 = 1;
}
diff --git a/src/google/protobuf/unittest_custom_options.proto b/src/google/protobuf/unittest_custom_options.proto
index d4d6e869..4cc0e362 100644
--- a/src/google/protobuf/unittest_custom_options.proto
+++ b/src/google/protobuf/unittest_custom_options.proto
@@ -392,3 +392,30 @@ message NestedOptionType {
optional int32 nested_extension = 7912573 [(field_opt2) = 1005];
}
}
+
+// Custom message option that has a required enum field.
+// WARNING: this is strongly discouraged!
+message OldOptionType {
+ enum TestEnum {
+ OLD_VALUE = 0;
+ }
+ required TestEnum value = 1;
+}
+
+// Updated version of the custom option above.
+message NewOptionType {
+ enum TestEnum {
+ OLD_VALUE = 0;
+ NEW_VALUE = 1;
+ }
+ required TestEnum value = 1;
+}
+
+extend google.protobuf.MessageOptions {
+ optional OldOptionType required_enum_opt = 106161807;
+}
+
+// Test message using the "required_enum_opt" option defined above.
+message TestMessageWithRequiredEnumOption {
+ option (required_enum_opt) = { value: OLD_VALUE };
+}
diff --git a/src/google/protobuf/unittest_well_known_types.proto b/src/google/protobuf/unittest_well_known_types.proto
index 2cb7775c..c9075244 100644
--- a/src/google/protobuf/unittest_well_known_types.proto
+++ b/src/google/protobuf/unittest_well_known_types.proto
@@ -39,6 +39,8 @@ message TestWellKnownTypes {
google.protobuf.BoolValue bool_field = 16;
google.protobuf.StringValue string_field = 17;
google.protobuf.BytesValue bytes_field = 18;
+ // Part of struct, but useful to be able to test separately
+ google.protobuf.Value value_field = 19;
}
// A repeated field for each well-known type.
diff --git a/src/google/protobuf/util/field_comparator.h b/src/google/protobuf/util/field_comparator.h
index ee676265..8b83c69f 100644
--- a/src/google/protobuf/util/field_comparator.h
+++ b/src/google/protobuf/util/field_comparator.h
@@ -93,7 +93,7 @@ class LIBPROTOBUF_EXPORT FieldComparator {
// Basic implementation of FieldComparator. Supports four modes of floating
// point value comparison: exact, approximate using MathUtil::AlmostEqual
-// method, and arbitrarilly precise using MathUtil::WithinFracionOrMargin.
+// method, and arbitrarilly precise using MathUtil::WithinFractionOrMargin.
class LIBPROTOBUF_EXPORT DefaultFieldComparator : public FieldComparator {
public:
enum FloatComparison {
diff --git a/src/google/protobuf/util/field_comparator_test.cc b/src/google/protobuf/util/field_comparator_test.cc
index d3d34602..23f7d51d 100644
--- a/src/google/protobuf/util/field_comparator_test.cc
+++ b/src/google/protobuf/util/field_comparator_test.cc
@@ -35,7 +35,12 @@
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/mathutil.h>
-
+// This gtest header is put after mathutil.h intentionally. We have to do
+// this because mathutil.h includes mathlimits.h which requires cmath not
+// being included to compile on some versions of gcc:
+// https://github.com/google/protobuf/blob/818c5eee08840355d70d2f3bdf1a2f17986a5e70/src/google/protobuf/stubs/mathlimits.h#L48
+// and the opensource version gtest.h header includes cmath transitively
+// somehow.
#include <gtest/gtest.h>
namespace google {
diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc
index ea360798..b557429f 100644
--- a/src/google/protobuf/util/internal/datapiece.cc
+++ b/src/google/protobuf/util/internal/datapiece.cc
@@ -35,8 +35,8 @@
#include <google/protobuf/descriptor.h>
#include <google/protobuf/util/internal/utility.h>
#include <google/protobuf/stubs/strutil.h>
-#include <google/protobuf/stubs/mathutil.h>
#include <google/protobuf/stubs/mathlimits.h>
+#include <google/protobuf/stubs/mathutil.h>
namespace google {
namespace protobuf {
@@ -57,13 +57,8 @@ inline Status InvalidArgument(StringPiece value_str) {
return Status(util::error::INVALID_ARGUMENT, value_str);
}
-// For general conversion between
-// int32, int64, uint32, uint64, double and float
-// except conversion between double and float.
template <typename To, typename From>
-StatusOr<To> NumberConvertAndCheck(From before) {
- if (::google::protobuf::internal::is_same<From, To>::value) return before;
- To after = static_cast<To>(before);
+StatusOr<To> ValidateNumberConversion(To after, From before) {
if (after == before &&
MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
return after;
@@ -76,6 +71,27 @@ StatusOr<To> NumberConvertAndCheck(From before) {
}
}
+// For general conversion between
+// int32, int64, uint32, uint64, double and float
+// except conversion between double and float.
+template <typename To, typename From>
+StatusOr<To> NumberConvertAndCheck(From before) {
+ if (::google::protobuf::internal::is_same<From, To>::value) return before;
+
+ To after = static_cast<To>(before);
+ return ValidateNumberConversion(after, before);
+}
+
+// For conversion to integer types (int32, int64, uint32, uint64) from floating
+// point types (double, float) only.
+template <typename To, typename From>
+StatusOr<To> FloatingPointToIntConvertAndCheck(From before) {
+ if (::google::protobuf::internal::is_same<From, To>::value) return before;
+
+ To after = static_cast<To>(before);
+ return ValidateNumberConversion(after, before);
+}
+
// For conversion between double and float only.
template <typename To, typename From>
StatusOr<To> FloatingPointConvertAndCheck(From before) {
@@ -96,30 +112,50 @@ StatusOr<To> FloatingPointConvertAndCheck(From before) {
} // namespace
StatusOr<int32> DataPiece::ToInt32() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<int32>(safe_strto32);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<int32, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<int32, float>(float_);
+
return GenericConvert<int32>();
}
StatusOr<uint32> DataPiece::ToUint32() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<uint32>(safe_strtou32);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<uint32>(safe_strtou32);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<uint32, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<uint32, float>(float_);
+
return GenericConvert<uint32>();
}
StatusOr<int64> DataPiece::ToInt64() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<int64>(safe_strto64);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<int64, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<int64, float>(float_);
+
return GenericConvert<int64>();
}
StatusOr<uint64> DataPiece::ToUint64() const {
- if (type_ == TYPE_STRING) {
- return StringToNumber<uint64>(safe_strtou64);
- }
+ if (type_ == TYPE_STRING) return StringToNumber<uint64>(safe_strtou64);
+
+ if (type_ == TYPE_DOUBLE)
+ return FloatingPointToIntConvertAndCheck<uint64, double>(double_);
+
+ if (type_ == TYPE_FLOAT)
+ return FloatingPointToIntConvertAndCheck<uint64, float>(float_);
+
return GenericConvert<uint64>();
}
diff --git a/src/google/protobuf/util/internal/datapiece.h b/src/google/protobuf/util/internal/datapiece.h
index 2ab3fa88..f22bfe70 100644
--- a/src/google/protobuf/util/internal/datapiece.h
+++ b/src/google/protobuf/util/internal/datapiece.h
@@ -98,7 +98,8 @@ class LIBPROTOBUF_EXPORT DataPiece {
static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); }
- virtual ~DataPiece() {}
+ virtual ~DataPiece() {
+ }
// Accessors
Type type() const { return type_; }
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc
index 97b248ff..a63e560d 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter.cc
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc
@@ -33,6 +33,7 @@
#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
#include <google/protobuf/stubs/map_util.h>
namespace google {
@@ -42,13 +43,25 @@ using util::Status;
using util::StatusOr;
namespace converter {
+namespace {
+// Helper function to convert string value to given data type by calling the
+// passed converter function on the DataPiece created from "value" argument.
+// If value is empty or if conversion fails, the default_value is returned.
+template <typename T>
+T ConvertTo(StringPiece value, StatusOr<T> (DataPiece::*converter_fn)() const,
+ T default_value) {
+ if (value.empty()) return default_value;
+ StatusOr<T> result = (DataPiece(value).*converter_fn)();
+ return result.ok() ? result.ValueOrDie() : default_value;
+}
+} // namespace
+
DefaultValueObjectWriter::DefaultValueObjectWriter(
TypeResolver* type_resolver, const google::protobuf::Type& type,
ObjectWriter* ow)
: typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
own_typeinfo_(true),
type_(type),
- disable_normalize_(false),
current_(NULL),
root_(NULL),
ow_(ow) {}
@@ -165,12 +178,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull(
return this;
}
-DefaultValueObjectWriter*
-DefaultValueObjectWriter::DisableCaseNormalizationForNextKey() {
- disable_normalize_ = true;
- return this;
-}
-
DefaultValueObjectWriter::Node::Node(const string& name,
const google::protobuf::Type* type,
NodeKind kind, const DataPiece& data,
@@ -178,7 +185,6 @@ DefaultValueObjectWriter::Node::Node(const string& name,
: name_(name),
type_(type),
kind_(kind),
- disable_normalize_(false),
is_any_(false),
data_(data),
is_placeholder_(is_placeholder) {}
@@ -198,10 +204,6 @@ DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild(
}
void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) {
- if (disable_normalize_) {
- ow->DisableCaseNormalizationForNextKey();
- }
-
if (kind_ == PRIMITIVE) {
ObjectWriter::RenderDataPieceTo(data_, name_, ow);
return;
@@ -324,6 +326,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren(
}
}
}
+
if (!is_map &&
field.cardinality() ==
google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
@@ -336,11 +339,11 @@ void DefaultValueObjectWriter::Node::PopulateChildren(
// If the child field is of primitive type, sets its data to the default
// value of its type.
- google::protobuf::scoped_ptr<Node> child(
- new Node(field.json_name(), field_type, kind,
- kind == PRIMITIVE ? CreateDefaultDataPieceForField(field)
- : DataPiece::NullData(),
- true));
+ google::protobuf::scoped_ptr<Node> child(new Node(
+ field.json_name(), field_type, kind,
+ kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo)
+ : DataPiece::NullData(),
+ true));
new_children.push_back(child.release());
}
// Adds all leftover nodes in children_ to the beginning of new_child.
@@ -363,41 +366,68 @@ void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) {
}
}
+DataPiece DefaultValueObjectWriter::FindEnumDefault(
+ const google::protobuf::Field& field, const TypeInfo* typeinfo) {
+ if (!field.default_value().empty()) return DataPiece(field.default_value());
+
+ const google::protobuf::Enum* enum_type =
+ typeinfo->GetEnumByTypeUrl(field.type_url());
+ if (!enum_type) {
+ GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url()
+ << "'";
+ return DataPiece::NullData();
+ }
+ // We treat the first value as the default if none is specified.
+ return enum_type->enumvalue_size() > 0
+ ? DataPiece(enum_type->enumvalue(0).name())
+ : DataPiece::NullData();
+}
+
DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField(
- const google::protobuf::Field& field) {
+ const google::protobuf::Field& field, const TypeInfo* typeinfo) {
switch (field.kind()) {
case google::protobuf::Field_Kind_TYPE_DOUBLE: {
- return DataPiece(static_cast<double>(0));
+ return DataPiece(ConvertTo<double>(
+ field.default_value(), &DataPiece::ToDouble, static_cast<double>(0)));
}
case google::protobuf::Field_Kind_TYPE_FLOAT: {
- return DataPiece(static_cast<float>(0));
+ return DataPiece(ConvertTo<float>(
+ field.default_value(), &DataPiece::ToFloat, static_cast<float>(0)));
}
case google::protobuf::Field_Kind_TYPE_INT64:
case google::protobuf::Field_Kind_TYPE_SINT64:
case google::protobuf::Field_Kind_TYPE_SFIXED64: {
- return DataPiece(static_cast<int64>(0));
+ return DataPiece(ConvertTo<int64>(
+ field.default_value(), &DataPiece::ToInt64, static_cast<int64>(0)));
}
case google::protobuf::Field_Kind_TYPE_UINT64:
case google::protobuf::Field_Kind_TYPE_FIXED64: {
- return DataPiece(static_cast<uint64>(0));
+ return DataPiece(ConvertTo<uint64>(
+ field.default_value(), &DataPiece::ToUint64, static_cast<uint64>(0)));
}
case google::protobuf::Field_Kind_TYPE_INT32:
case google::protobuf::Field_Kind_TYPE_SINT32:
case google::protobuf::Field_Kind_TYPE_SFIXED32: {
- return DataPiece(static_cast<int32>(0));
+ return DataPiece(ConvertTo<int32>(
+ field.default_value(), &DataPiece::ToInt32, static_cast<int32>(0)));
}
case google::protobuf::Field_Kind_TYPE_BOOL: {
- return DataPiece(false);
+ return DataPiece(
+ ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false));
}
case google::protobuf::Field_Kind_TYPE_STRING: {
- return DataPiece(string());
+ return DataPiece(field.default_value());
}
case google::protobuf::Field_Kind_TYPE_BYTES: {
- return DataPiece("", false);
+ return DataPiece(field.default_value(), false);
}
case google::protobuf::Field_Kind_TYPE_UINT32:
case google::protobuf::Field_Kind_TYPE_FIXED32: {
- return DataPiece(static_cast<uint32>(0));
+ return DataPiece(ConvertTo<uint32>(
+ field.default_value(), &DataPiece::ToUint32, static_cast<uint32>(0)));
+ }
+ case google::protobuf::Field_Kind_TYPE_ENUM: {
+ return FindEnumDefault(field, typeinfo);
}
default: { return DataPiece::NullData(); }
}
@@ -408,7 +438,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
if (current_ == NULL) {
root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(),
false));
- root_->set_disable_normalize(GetAndResetDisableNormalize());
root_->PopulateChildren(typeinfo_);
current_ = root_.get();
return this;
@@ -428,7 +457,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
}
child->set_is_placeholder(false);
- child->set_disable_normalize(GetAndResetDisableNormalize());
if (child->kind() == OBJECT && child->number_of_children() == 0) {
child->PopulateChildren(typeinfo_);
}
@@ -454,21 +482,18 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartList(
if (current_ == NULL) {
root_.reset(
new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false));
- root_->set_disable_normalize(GetAndResetDisableNormalize());
current_ = root_.get();
return this;
}
MaybePopulateChildrenOfAny(current_);
Node* child = current_->FindChild(name);
if (child == NULL || child->kind() != LIST) {
- GOOGLE_LOG(WARNING) << "Cannot find field '" << name << "'.";
google::protobuf::scoped_ptr<Node> node(
new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false));
child = node.get();
current_->AddChild(node.release());
}
child->set_is_placeholder(false);
- child->set_disable_normalize(GetAndResetDisableNormalize());
stack_.push(current_);
current_ = child;
@@ -526,7 +551,6 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name,
} else {
child->set_data(data);
}
- child->set_disable_normalize(GetAndResetDisableNormalize());
}
} // namespace converter
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h
index bcb526e8..695b9dd8 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter.h
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.h
@@ -98,8 +98,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
virtual DefaultValueObjectWriter* RenderNull(StringPiece name);
- virtual DefaultValueObjectWriter* DisableCaseNormalizationForNextKey();
-
private:
enum NodeKind {
PRIMITIVE = 0,
@@ -149,10 +147,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
void set_data(const DataPiece& data) { data_ = data; }
- void set_disable_normalize(bool disable_normalize) {
- disable_normalize_ = disable_normalize;
- }
-
bool is_any() { return is_any_; }
void set_is_any(bool is_any) { is_any_ = is_any; }
@@ -176,8 +170,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
const google::protobuf::Type* type_;
// The kind of this node.
NodeKind kind_;
- // Whether to disable case normalization of the name.
- bool disable_normalize_;
// Whether this is a node for "Any".
bool is_any_;
// The data of this node when it is a leaf node.
@@ -201,16 +193,17 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
// Creates a DataPiece containing the default value of the type of the field.
static DataPiece CreateDefaultDataPieceForField(
- const google::protobuf::Field& field);
-
- // Returns disable_normalize_ and reset it to false.
- bool GetAndResetDisableNormalize() {
- return disable_normalize_ ? (disable_normalize_ = false, true) : false;
- }
+ const google::protobuf::Field& field, const TypeInfo* typeinfo);
// Adds or replaces the data_ of a primitive child node.
void RenderDataPiece(StringPiece name, const DataPiece& data);
+ // Returns the default enum value as a DataPiece, or the first enum value if
+ // there is no default. For proto3, where we cannot specify an explicit
+ // default, a zero value will always be returned.
+ static DataPiece FindEnumDefault(const google::protobuf::Field& field,
+ const TypeInfo* typeinfo);
+
// Type information for all the types used in the descriptor. Used to find
// google::protobuf::Type of nested messages/enums.
const TypeInfo* typeinfo_;
@@ -221,8 +214,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
// Holds copies of strings passed to RenderString.
vector<string*> string_values_;
- // Whether to disable case normalization of the next node.
- bool disable_normalize_;
// The current Node. Owned by its parents.
Node* current_;
// The root Node.
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
index 237d0722..8254c0fa 100644
--- a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
+++ b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
@@ -43,21 +43,19 @@ namespace testing {
using google::protobuf::testing::DefaultValueTest;
-// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are
-// in the marshalling_test.cc and translator_integration_test.cc.
-class DefaultValueObjectWriterTest
+// Base class for setting up required state for running default values tests on
+// different descriptors.
+class BaseDefaultValueObjectWriterTest
: public ::testing::TestWithParam<testing::TypeInfoSource> {
protected:
- DefaultValueObjectWriterTest()
+ explicit BaseDefaultValueObjectWriterTest(const Descriptor* descriptor)
: helper_(GetParam()), mock_(), expects_(&mock_) {
- helper_.ResetTypeInfo(DefaultValueTest::descriptor());
+ helper_.ResetTypeInfo(descriptor);
testing_.reset(helper_.NewDefaultValueWriter(
- string(kTypeServiceBaseUrl) + "/" +
- DefaultValueTest::descriptor()->full_name(),
- &mock_));
+ string(kTypeServiceBaseUrl) + "/" + descriptor->full_name(), &mock_));
}
- virtual ~DefaultValueObjectWriterTest() {}
+ virtual ~BaseDefaultValueObjectWriterTest() {}
TypeInfoTestHelper helper_;
MockObjectWriter mock_;
@@ -65,6 +63,15 @@ class DefaultValueObjectWriterTest
google::protobuf::scoped_ptr<DefaultValueObjectWriter> testing_;
};
+// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are
+// in the marshalling_test.cc and translator_integration_test.cc.
+class DefaultValueObjectWriterTest : public BaseDefaultValueObjectWriterTest {
+ protected:
+ DefaultValueObjectWriterTest()
+ : BaseDefaultValueObjectWriterTest(DefaultValueTest::descriptor()) {}
+ virtual ~DefaultValueObjectWriterTest() {}
+};
+
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
DefaultValueObjectWriterTest,
::testing::Values(
@@ -74,6 +81,8 @@ TEST_P(DefaultValueObjectWriterTest, Empty) {
// Set expectation
expects_.StartObject("")
->RenderDouble("doubleValue", 0.0)
+ ->StartList("repeatedDouble")
+ ->EndList()
->RenderFloat("floatValue", 0.0)
->RenderInt64("int64Value", 0)
->RenderUint64("uint64Value", 0)
@@ -82,6 +91,7 @@ TEST_P(DefaultValueObjectWriterTest, Empty) {
->RenderBool("boolValue", false)
->RenderString("stringValue", "")
->RenderBytes("bytesValue", "")
+ ->RenderString("enumValue", "ENUM_FIRST")
->EndObject();
// Actual testing
@@ -92,6 +102,8 @@ TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) {
// Set expectation
expects_.StartObject("")
->RenderDouble("doubleValue", 1.0)
+ ->StartList("repeatedDouble")
+ ->EndList()
->RenderFloat("floatValue", 0.0)
->RenderInt64("int64Value", 0)
->RenderUint64("uint64Value", 0)
@@ -99,6 +111,7 @@ TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) {
->RenderUint32("uint32Value", 0)
->RenderBool("boolValue", false)
->RenderString("stringValue", "")
+ ->RenderString("enumValue", "ENUM_FIRST")
->EndObject();
// Actual testing
@@ -109,6 +122,8 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
// Set expectation
expects_.StartObject("")
->RenderDouble("doubleValue", 1.0)
+ ->StartList("repeatedDouble")
+ ->EndList()
->RenderFloat("floatValue", 0.0)
->RenderInt64("int64Value", 0)
->RenderUint64("uint64Value", 0)
@@ -120,6 +135,7 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
->StartObject("unknownObject")
->RenderString("unknown", "def")
->EndObject()
+ ->RenderString("enumValue", "ENUM_FIRST")
->EndObject();
// Actual testing
@@ -132,6 +148,7 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
->EndObject();
}
+
} // namespace testing
} // namespace converter
} // namespace util
diff --git a/src/google/protobuf/util/internal/error_listener.h b/src/google/protobuf/util/internal/error_listener.h
index 2699684d..3f063936 100644
--- a/src/google/protobuf/util/internal/error_listener.h
+++ b/src/google/protobuf/util/internal/error_listener.h
@@ -31,11 +31,13 @@
#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
+#include <algorithm>
#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <string>
+#include <vector>
#include <google/protobuf/stubs/callback.h>
#include <google/protobuf/stubs/common.h>
diff --git a/src/google/protobuf/util/internal/json_escaping.cc b/src/google/protobuf/util/internal/json_escaping.cc
index 36dc8ef9..24bd554e 100644
--- a/src/google/protobuf/util/internal/json_escaping.cc
+++ b/src/google/protobuf/util/internal/json_escaping.cc
@@ -186,7 +186,7 @@ bool ReadCodePoint(StringPiece str, int index,
uint32 *cp, int* num_left, int *num_read) {
if (*num_left == 0) {
// Last read was complete. Start reading a new unicode code point.
- *cp = str[index++];
+ *cp = static_cast<uint8>(str[index++]);
*num_read = 1;
// The length of the code point is determined from reading the first byte.
//
@@ -235,7 +235,7 @@ bool ReadCodePoint(StringPiece str, int index,
*num_read = 0;
}
while (*num_left > 0 && index < str.size()) {
- uint32 ch = str[index++];
+ uint32 ch = static_cast<uint8>(str[index++]);
--(*num_left);
++(*num_read);
*cp = (*cp << 6) | (ch & 0x3f);
diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc
index dcd60601..9d820162 100644
--- a/src/google/protobuf/util/internal/json_objectwriter_test.cc
+++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc
@@ -47,8 +47,7 @@ class JsonObjectWriterTest : public ::testing::Test {
JsonObjectWriterTest()
: str_stream_(new StringOutputStream(&output_)),
out_stream_(new CodedOutputStream(str_stream_)),
- ow_(NULL) {
- }
+ ow_(NULL) {}
virtual ~JsonObjectWriterTest() {
delete ow_;
@@ -64,36 +63,34 @@ class JsonObjectWriterTest : public ::testing::Test {
TEST_F(JsonObjectWriterTest, EmptyRootObject) {
ow_ = new JsonObjectWriter("", out_stream_);
- ow_->StartObject("")
- ->EndObject();
+ ow_->StartObject("")->EndObject();
EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, EmptyObject) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->RenderString("test", "value")
- ->StartObject("empty")
- ->EndObject()
- ->EndObject();
+ ->RenderString("test", "value")
+ ->StartObject("empty")
+ ->EndObject()
+ ->EndObject();
EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}",
output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, EmptyRootList) {
ow_ = new JsonObjectWriter("", out_stream_);
- ow_->StartList("")
- ->EndList();
+ ow_->StartList("")->EndList();
EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, EmptyList) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->RenderString("test", "value")
- ->StartList("empty")
- ->EndList()
- ->EndObject();
+ ->RenderString("test", "value")
+ ->StartList("empty")
+ ->EndList()
+ ->EndObject();
EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -101,10 +98,10 @@ TEST_F(JsonObjectWriterTest, EmptyList) {
TEST_F(JsonObjectWriterTest, ObjectInObject) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->StartObject("nested")
- ->RenderString("field", "value")
- ->EndObject()
- ->EndObject();
+ ->StartObject("nested")
+ ->RenderString("field", "value")
+ ->EndObject()
+ ->EndObject();
EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -112,10 +109,10 @@ TEST_F(JsonObjectWriterTest, ObjectInObject) {
TEST_F(JsonObjectWriterTest, ListInObject) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->StartList("nested")
- ->RenderString("", "value")
- ->EndList()
- ->EndObject();
+ ->StartList("nested")
+ ->RenderString("", "value")
+ ->EndList()
+ ->EndObject();
EXPECT_EQ("{\"nested\":[\"value\"]}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -123,10 +120,10 @@ TEST_F(JsonObjectWriterTest, ListInObject) {
TEST_F(JsonObjectWriterTest, ObjectInList) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartList("")
- ->StartObject("")
- ->RenderString("field", "value")
- ->EndObject()
- ->EndList();
+ ->StartObject("")
+ ->RenderString("field", "value")
+ ->EndObject()
+ ->EndList();
EXPECT_EQ("[{\"field\":\"value\"}]",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -134,10 +131,10 @@ TEST_F(JsonObjectWriterTest, ObjectInList) {
TEST_F(JsonObjectWriterTest, ListInList) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartList("")
- ->StartList("")
- ->RenderString("", "value")
- ->EndList()
- ->EndList();
+ ->StartList("")
+ ->RenderString("", "value")
+ ->EndList()
+ ->EndList();
EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount()));
}
@@ -156,14 +153,18 @@ TEST_F(JsonObjectWriterTest, RenderPrimitives) {
->EndObject();
EXPECT_EQ(
"{\"bool\":true,"
- "\"double\":" + ValueAsString<double>(1.7976931348623157e+308) + ","
- "\"float\":" + ValueAsString<float>(3.4028235e+38) + ","
- "\"int\":-2147483648,"
- "\"long\":\"-9223372036854775808\","
- "\"bytes\":\"YWJyYWNhZGFicmE=\","
- "\"string\":\"string\","
- "\"emptybytes\":\"\","
- "\"emptystring\":\"\"}",
+ "\"double\":" +
+ ValueAsString<double>(std::numeric_limits<double>::max()) +
+ ","
+ "\"float\":" +
+ ValueAsString<float>(std::numeric_limits<float>::max()) +
+ ","
+ "\"int\":-2147483648,"
+ "\"long\":\"-9223372036854775808\","
+ "\"bytes\":\"YWJyYWNhZGFicmE=\","
+ "\"string\":\"string\","
+ "\"emptybytes\":\"\","
+ "\"emptystring\":\"\"}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -181,81 +182,83 @@ TEST_F(JsonObjectWriterTest, BytesEncodesAsNonWebSafeBase64) {
TEST_F(JsonObjectWriterTest, PrettyPrintList) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->StartList("items")
- ->RenderString("", "item1")
- ->RenderString("", "item2")
- ->RenderString("", "item3")
- ->EndList()
- ->StartList("empty")
- ->EndList()
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"items\": [\n"
- " \"item1\",\n"
- " \"item2\",\n"
- " \"item3\"\n"
- " ],\n"
- " \"empty\": []\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->StartList("items")
+ ->RenderString("", "item1")
+ ->RenderString("", "item2")
+ ->RenderString("", "item3")
+ ->EndList()
+ ->StartList("empty")
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"items\": [\n"
+ " \"item1\",\n"
+ " \"item2\",\n"
+ " \"item3\"\n"
+ " ],\n"
+ " \"empty\": []\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, PrettyPrintObject) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->StartObject("items")
- ->RenderString("key1", "item1")
- ->RenderString("key2", "item2")
- ->RenderString("key3", "item3")
- ->EndObject()
- ->StartObject("empty")
- ->EndObject()
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"items\": {\n"
- " \"key1\": \"item1\",\n"
- " \"key2\": \"item2\",\n"
- " \"key3\": \"item3\"\n"
- " },\n"
- " \"empty\": {}\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->StartObject("items")
+ ->RenderString("key1", "item1")
+ ->RenderString("key2", "item2")
+ ->RenderString("key3", "item3")
+ ->EndObject()
+ ->StartObject("empty")
+ ->EndObject()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"items\": {\n"
+ " \"key1\": \"item1\",\n"
+ " \"key2\": \"item2\",\n"
+ " \"key3\": \"item3\"\n"
+ " },\n"
+ " \"empty\": {}\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->StartList("list")
- ->StartObject("")
- ->EndObject()
- ->EndList()
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"list\": [\n"
- " {}\n"
- " ]\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->StartList("list")
+ ->StartObject("")
+ ->EndObject()
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"list\": [\n"
+ " {}\n"
+ " ]\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) {
ow_ = new JsonObjectWriter(" ", out_stream_);
ow_->StartObject("")
- ->RenderBool("bool", true)
- ->RenderInt32("int", 42)
- ->EndObject();
- EXPECT_EQ("{\n"
- " \"bool\": true,\n"
- " \"int\": 42\n"
- "}\n",
- output_.substr(0, out_stream_->ByteCount()));
+ ->RenderBool("bool", true)
+ ->RenderInt32("int", 42)
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"bool\": true,\n"
+ " \"int\": 42\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
}
TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) {
ow_ = new JsonObjectWriter("", out_stream_);
- ow_->StartObject("")
- ->RenderString("string", "'<>&amp;\\\"\r\n")
- ->EndObject();
+ ow_->StartObject("")->RenderString("string", "'<>&amp;\\\"\r\n")->EndObject();
EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&amp;\\\\\\\"\\r\\n\"}",
output_.substr(0, out_stream_->ByteCount()));
}
@@ -263,13 +266,13 @@ TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) {
TEST_F(JsonObjectWriterTest, Stringification) {
ow_ = new JsonObjectWriter("", out_stream_);
ow_->StartObject("")
- ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN())
- ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN())
- ->RenderDouble("double_pos", std::numeric_limits<double>::infinity())
- ->RenderFloat("float_pos", std::numeric_limits<float>::infinity())
- ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity())
- ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity())
- ->EndObject();
+ ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN())
+ ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN())
+ ->RenderDouble("double_pos", std::numeric_limits<double>::infinity())
+ ->RenderFloat("float_pos", std::numeric_limits<float>::infinity())
+ ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity())
+ ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity())
+ ->EndObject();
EXPECT_EQ(
"{\"double_nan\":\"NaN\","
"\"float_nan\":\"NaN\","
diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc
index a7ef7fe2..df916751 100644
--- a/src/google/protobuf/util/internal/json_stream_parser.cc
+++ b/src/google/protobuf/util/internal/json_stream_parser.cc
@@ -157,10 +157,10 @@ util::Status JsonStreamParser::FinishParse() {
char* coerced = internal::UTF8CoerceToStructurallyValid(leftover_, utf8.get(), ' ');
p_ = json_ = StringPiece(coerced, leftover_.size());
} else {
+ p_ = json_ = leftover_;
if (!internal::IsStructurallyValidUTF8(leftover_)) {
return ReportFailure("Encountered non UTF-8 code points.");
}
- p_ = json_ = leftover_;
}
// Parse the remainder in finishing mode, which reports errors for things like
diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc
index c833ed1f..3414826e 100644
--- a/src/google/protobuf/util/internal/json_stream_parser_test.cc
+++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc
@@ -348,6 +348,7 @@ TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) {
for (int i = 0; i <= json.length(); ++i) {
DoErrorTest(json, i, "Encountered non UTF-8 code points.");
}
+ DoErrorTest("\xFF{}", 0, "Encountered non UTF-8 code points.");
}
#ifndef _MSC_VER
diff --git a/src/google/protobuf/util/internal/object_writer.h b/src/google/protobuf/util/internal/object_writer.h
index 20bd3627..e695f45e 100644
--- a/src/google/protobuf/util/internal/object_writer.h
+++ b/src/google/protobuf/util/internal/object_writer.h
@@ -101,11 +101,6 @@ class LIBPROTOBUF_EXPORT ObjectWriter {
// Renders a Null value.
virtual ObjectWriter* RenderNull(StringPiece name) = 0;
- // Disables case normalization. Any RenderTYPE call after calling this
- // function will output the name field as-is. No normalization is attempted on
- // it. This setting is reset immediately after the next RenderTYPE is called.
- virtual ObjectWriter* DisableCaseNormalizationForNextKey() { return this; }
-
// Renders a DataPiece object to a ObjectWriter.
static void RenderDataPieceTo(const DataPiece& data, StringPiece name,
ObjectWriter* ow);
diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc
new file mode 100644
index 00000000..47e0009e
--- /dev/null
+++ b/src/google/protobuf/util/internal/proto_writer.cc
@@ -0,0 +1,744 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/util/internal/proto_writer.h>
+
+#include <functional>
+#include <stack>
+
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/object_location_tracker.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/statusor.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::internal::WireFormatLite;
+using google::protobuf::io::CodedOutputStream;
+using util::error::INVALID_ARGUMENT;
+using util::Status;
+using util::StatusOr;
+
+
+ProtoWriter::ProtoWriter(TypeResolver* type_resolver,
+ const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener)
+ : master_type_(type),
+ typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+ own_typeinfo_(true),
+ done_(false),
+ element_(NULL),
+ size_insert_(),
+ output_(output),
+ buffer_(),
+ adapter_(&buffer_),
+ stream_(new CodedOutputStream(&adapter_)),
+ listener_(listener),
+ invalid_depth_(0),
+ tracker_(new ObjectLocationTracker()) {}
+
+ProtoWriter::ProtoWriter(const TypeInfo* typeinfo,
+ const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener)
+ : master_type_(type),
+ typeinfo_(typeinfo),
+ own_typeinfo_(false),
+ done_(false),
+ element_(NULL),
+ size_insert_(),
+ output_(output),
+ buffer_(),
+ adapter_(&buffer_),
+ stream_(new CodedOutputStream(&adapter_)),
+ listener_(listener),
+ invalid_depth_(0),
+ tracker_(new ObjectLocationTracker()) {}
+
+ProtoWriter::~ProtoWriter() {
+ if (own_typeinfo_) {
+ delete typeinfo_;
+ }
+ if (element_ == NULL) return;
+ // Cleanup explicitly in order to avoid destructor stack overflow when input
+ // is deeply nested.
+ // Cast to BaseElement to avoid doing additional checks (like missing fields)
+ // during pop().
+ google::protobuf::scoped_ptr<BaseElement> element(
+ static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
+ while (element != NULL) {
+ element.reset(element->pop<BaseElement>());
+ }
+}
+
+namespace {
+
+// Writes an INT32 field, including tag to the stream.
+inline Status WriteInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// writes an SFIXED32 field, including tag, to the stream.
+inline Status WriteSFixed32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// Writes an SINT32 field, including tag, to the stream.
+inline Status WriteSInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// Writes a FIXED32 field, including tag, to the stream.
+inline Status WriteFixed32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint32> u32 = data.ToUint32();
+ if (u32.ok()) {
+ WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
+ }
+ return u32.status();
+}
+
+// Writes a UINT32 field, including tag, to the stream.
+inline Status WriteUInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint32> u32 = data.ToUint32();
+ if (u32.ok()) {
+ WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
+ }
+ return u32.status();
+}
+
+// Writes an INT64 field, including tag, to the stream.
+inline Status WriteInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes an SFIXED64 field, including tag, to the stream.
+inline Status WriteSFixed64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes an SINT64 field, including tag, to the stream.
+inline Status WriteSInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes a FIXED64 field, including tag, to the stream.
+inline Status WriteFixed64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint64> u64 = data.ToUint64();
+ if (u64.ok()) {
+ WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
+ }
+ return u64.status();
+}
+
+// Writes a UINT64 field, including tag, to the stream.
+inline Status WriteUInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint64> u64 = data.ToUint64();
+ if (u64.ok()) {
+ WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
+ }
+ return u64.status();
+}
+
+// Writes a DOUBLE field, including tag, to the stream.
+inline Status WriteDouble(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<double> d = data.ToDouble();
+ if (d.ok()) {
+ WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
+ }
+ return d.status();
+}
+
+// Writes a FLOAT field, including tag, to the stream.
+inline Status WriteFloat(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<float> f = data.ToFloat();
+ if (f.ok()) {
+ WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
+ }
+ return f.status();
+}
+
+// Writes a BOOL field, including tag, to the stream.
+inline Status WriteBool(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<bool> b = data.ToBool();
+ if (b.ok()) {
+ WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
+ }
+ return b.status();
+}
+
+// Writes a BYTES field, including tag, to the stream.
+inline Status WriteBytes(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<string> c = data.ToBytes();
+ if (c.ok()) {
+ WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
+ }
+ return c.status();
+}
+
+// Writes a STRING field, including tag, to the stream.
+inline Status WriteString(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<string> s = data.ToString();
+ if (s.ok()) {
+ WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
+ }
+ return s.status();
+}
+
+// Writes an ENUM field, including tag, to the stream.
+inline Status WriteEnum(int field_number, const DataPiece& data,
+ const google::protobuf::Enum* enum_type,
+ CodedOutputStream* stream) {
+ StatusOr<int> e = data.ToEnum(enum_type);
+ if (e.ok()) {
+ WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
+ }
+ return e.status();
+}
+
+// Given a google::protobuf::Type, returns the set of all required fields.
+std::set<const google::protobuf::Field*> GetRequiredFields(
+ const google::protobuf::Type& type) {
+ std::set<const google::protobuf::Field*> required;
+ for (int i = 0; i < type.fields_size(); i++) {
+ const google::protobuf::Field& field = type.fields(i);
+ if (field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
+ required.insert(&field);
+ }
+ }
+ return required;
+}
+
+} // namespace
+
+ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo,
+ const google::protobuf::Type& type,
+ ProtoWriter* enclosing)
+ : BaseElement(NULL),
+ ow_(enclosing),
+ parent_field_(NULL),
+ typeinfo_(typeinfo),
+ type_(type),
+ required_fields_(GetRequiredFields(type)),
+ size_index_(-1),
+ array_index_(-1) {}
+
+ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent,
+ const google::protobuf::Field* field,
+ const google::protobuf::Type& type,
+ bool is_list)
+ : BaseElement(parent),
+ ow_(this->parent()->ow_),
+ parent_field_(field),
+ typeinfo_(this->parent()->typeinfo_),
+ type_(type),
+ size_index_(
+ !is_list && field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE
+ ? ow_->size_insert_.size()
+ : -1),
+ array_index_(is_list ? 0 : -1) {
+ if (!is_list) {
+ if (ow_->IsRepeated(*field)) {
+ // Update array_index_ if it is an explicit list.
+ if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
+ } else {
+ this->parent()->RegisterField(field);
+ }
+
+ if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ required_fields_ = GetRequiredFields(type_);
+ int start_pos = ow_->stream_->ByteCount();
+ // length of serialized message is the final buffer position minus
+ // starting buffer position, plus length adjustments for size fields
+ // of any nested messages. We start with -start_pos here, so we only
+ // need to add the final buffer position to it at the end.
+ SizeInfo info = {start_pos, -start_pos};
+ ow_->size_insert_.push_back(info);
+ }
+ }
+}
+
+ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() {
+ // Calls the registered error listener for any required field(s) not yet
+ // seen.
+ for (set<const google::protobuf::Field*>::iterator it =
+ required_fields_.begin();
+ it != required_fields_.end(); ++it) {
+ ow_->MissingField((*it)->name());
+ }
+ // Computes the total number of proto bytes used by a message, also adjusts
+ // the size of all parent messages by the length of this size field.
+ // If size_index_ < 0, this is not a message, so no size field is added.
+ if (size_index_ >= 0) {
+ // Add the final buffer position to compute the total length of this
+ // serialized message. The stored value (before this addition) already
+ // contains the total length of the size fields of all nested messages
+ // minus the initial buffer position.
+ ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
+ // Calculate the length required to serialize the size field of the
+ // message, and propagate this additional size information upward to
+ // all enclosing messages.
+ int size = ow_->size_insert_[size_index_].size;
+ int length = CodedOutputStream::VarintSize32(size);
+ for (ProtoElement* e = parent(); e != NULL; e = e->parent()) {
+ // Only nested messages have size field, lists do not have size field.
+ if (e->size_index_ >= 0) {
+ ow_->size_insert_[e->size_index_].size += length;
+ }
+ }
+ }
+ return BaseElement::pop<ProtoElement>();
+}
+
+void ProtoWriter::ProtoElement::RegisterField(
+ const google::protobuf::Field* field) {
+ if (!required_fields_.empty() &&
+ field->cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
+ required_fields_.erase(field);
+ }
+}
+
+string ProtoWriter::ProtoElement::ToString() const {
+ if (parent() == NULL) return "";
+ string loc = parent()->ToString();
+ if (!ow_->IsRepeated(*parent_field_) ||
+ parent()->parent_field_ != parent_field_) {
+ string name = parent_field_->name();
+ int i = 0;
+ while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
+ if (i > 0 && i == name.size()) { // safe field name
+ if (loc.empty()) {
+ loc = name;
+ } else {
+ StrAppend(&loc, ".", name);
+ }
+ } else {
+ StrAppend(&loc, "[\"", CEscape(name), "\"]");
+ }
+ }
+ if (ow_->IsRepeated(*parent_field_) && array_index_ > 0) {
+ StrAppend(&loc, "[", array_index_ - 1, "]");
+ }
+ return loc.empty() ? "." : loc;
+}
+
+bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32 index) {
+ return ContainsKey(oneof_indices_, index);
+}
+
+void ProtoWriter::ProtoElement::TakeOneofIndex(int32 index) {
+ InsertIfNotPresent(&oneof_indices_, index);
+}
+
+void ProtoWriter::InvalidName(StringPiece unknown_name, StringPiece message) {
+ listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
+}
+
+void ProtoWriter::InvalidValue(StringPiece type_name, StringPiece value) {
+ listener_->InvalidValue(location(), type_name, value);
+}
+
+void ProtoWriter::MissingField(StringPiece missing_name) {
+ listener_->MissingField(location(), missing_name);
+}
+
+ProtoWriter* ProtoWriter::StartObject(StringPiece name) {
+ // Starting the root message. Create the root ProtoElement and return.
+ if (element_ == NULL) {
+ if (!name.empty()) {
+ InvalidName(name, "Root element should not be named.");
+ }
+ element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+ return this;
+ }
+
+ const google::protobuf::Field* field = NULL;
+ field = BeginNamed(name, false);
+ if (field == NULL) return this;
+
+ // Check to see if this field is a oneof and that no oneof in that group has
+ // already been set.
+ if (!ValidOneof(*field, name)) {
+ ++invalid_depth_;
+ return this;
+ }
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ ++invalid_depth_;
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ WriteTag(*field);
+ element_.reset(new ProtoElement(element_.release(), field, *type, false));
+ return this;
+}
+
+ProtoWriter* ProtoWriter::EndObject() {
+ if (invalid_depth_ > 0) {
+ --invalid_depth_;
+ return this;
+ }
+
+ if (element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+
+
+ // If ending the root element,
+ // then serialize the full message with calculated sizes.
+ if (element_ == NULL) {
+ WriteRootMessage();
+ }
+ return this;
+}
+
+ProtoWriter* ProtoWriter::StartList(StringPiece name) {
+ const google::protobuf::Field* field = BeginNamed(name, true);
+ if (field == NULL) return this;
+
+ if (!ValidOneof(*field, name)) {
+ ++invalid_depth_;
+ return this;
+ }
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ ++invalid_depth_;
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ element_.reset(new ProtoElement(element_.release(), field, *type, true));
+ return this;
+}
+
+ProtoWriter* ProtoWriter::EndList() {
+ if (invalid_depth_ > 0) {
+ --invalid_depth_;
+ } else if (element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+ return this;
+}
+
+ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name,
+ const DataPiece& data) {
+ Status status;
+ if (invalid_depth_ > 0) return this;
+
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) return this;
+
+ if (!ValidOneof(*field, name)) return this;
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
+ // error location reporting and required field accounting.
+ element_.reset(new ProtoElement(element_.release(), field, *type, false));
+
+ if (field->kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN ||
+ field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ InvalidValue(field->type_url().empty()
+ ? google::protobuf::Field_Kind_Name(field->kind())
+ : field->type_url(),
+ data.ValueAsStringOrDefault(""));
+ element_.reset(element()->pop());
+ return this;
+ }
+
+ switch (field->kind()) {
+ case google::protobuf::Field_Kind_TYPE_INT32: {
+ status = WriteInt32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: {
+ status = WriteSFixed32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT32: {
+ status = WriteSInt32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED32: {
+ status = WriteFixed32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT32: {
+ status = WriteUInt32(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT64: {
+ status = WriteInt64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: {
+ status = WriteSFixed64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT64: {
+ status = WriteSInt64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED64: {
+ status = WriteFixed64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT64: {
+ status = WriteUInt64(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_DOUBLE: {
+ status = WriteDouble(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FLOAT: {
+ status = WriteFloat(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BOOL: {
+ status = WriteBool(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BYTES: {
+ status = WriteBytes(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_STRING: {
+ status = WriteString(field->number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_ENUM: {
+ status = WriteEnum(field->number(), data,
+ typeinfo_->GetEnumByTypeUrl(field->type_url()),
+ stream_.get());
+ break;
+ }
+ default: // TYPE_GROUP or TYPE_MESSAGE
+ status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie());
+ }
+
+ if (!status.ok()) {
+ InvalidValue(google::protobuf::Field_Kind_Name(field->kind()),
+ status.error_message());
+ }
+
+ element_.reset(element()->pop());
+ return this;
+}
+
+bool ProtoWriter::ValidOneof(const google::protobuf::Field& field,
+ StringPiece unnormalized_name) {
+ if (element_ == NULL) return true;
+
+ if (field.oneof_index() > 0) {
+ if (element_->IsOneofIndexTaken(field.oneof_index())) {
+ InvalidValue(
+ "oneof",
+ StrCat("oneof field '",
+ element_->type().oneofs(field.oneof_index() - 1),
+ "' is already set. Cannot set '", unnormalized_name, "'"));
+ return false;
+ }
+ element_->TakeOneofIndex(field.oneof_index());
+ }
+ return true;
+}
+
+bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
+ return field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED;
+}
+
+const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
+ bool is_list) {
+ if (invalid_depth_ > 0) {
+ ++invalid_depth_;
+ return NULL;
+ }
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) {
+ ++invalid_depth_;
+ // InvalidName() already called in Lookup().
+ return NULL;
+ }
+ if (is_list && !IsRepeated(*field)) {
+ ++invalid_depth_;
+ InvalidName(name, "Proto field is not repeating, cannot start list.");
+ return NULL;
+ }
+ return field;
+}
+
+const google::protobuf::Field* ProtoWriter::Lookup(
+ StringPiece unnormalized_name) {
+ ProtoElement* e = element();
+ if (e == NULL) {
+ InvalidName(unnormalized_name, "Root element must be a message.");
+ return NULL;
+ }
+ if (unnormalized_name.empty()) {
+ // Objects in repeated field inherit the same field descriptor.
+ if (e->parent_field() == NULL) {
+ InvalidName(unnormalized_name, "Proto fields must have a name.");
+ } else if (!IsRepeated(*e->parent_field())) {
+ InvalidName(unnormalized_name, "Proto fields must have a name.");
+ return NULL;
+ }
+ return e->parent_field();
+ }
+ const google::protobuf::Field* field =
+ typeinfo_->FindField(&e->type(), unnormalized_name);
+ if (field == NULL) InvalidName(unnormalized_name, "Cannot find field.");
+ return field;
+}
+
+const google::protobuf::Type* ProtoWriter::LookupType(
+ const google::protobuf::Field* field) {
+ return ((field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE ||
+ field->kind() == google::protobuf::Field_Kind_TYPE_GROUP)
+ ? typeinfo_->GetTypeByTypeUrl(field->type_url())
+ : &element_->type());
+}
+
+void ProtoWriter::WriteRootMessage() {
+ GOOGLE_DCHECK(!done_);
+ int curr_pos = 0;
+ // Calls the destructor of CodedOutputStream to remove any uninitialized
+ // memory from the Cord before we read it.
+ stream_.reset(NULL);
+ const void* data;
+ int length;
+ google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
+ while (input_stream.Next(&data, &length)) {
+ if (length == 0) continue;
+ int num_bytes = length;
+ // Write up to where we need to insert the size field.
+ // The number of bytes we may write is the smaller of:
+ // - the current fragment size
+ // - the distance to the next position where a size field needs to be
+ // inserted.
+ if (!size_insert_.empty() &&
+ size_insert_.front().pos - curr_pos < num_bytes) {
+ num_bytes = size_insert_.front().pos - curr_pos;
+ }
+ output_->Append(static_cast<const char*>(data), num_bytes);
+ if (num_bytes < length) {
+ input_stream.BackUp(length - num_bytes);
+ }
+ curr_pos += num_bytes;
+ // Insert the size field.
+ // size_insert_.front(): the next <index, size> pair to be written.
+ // size_insert_.front().pos: position of the size field.
+ // size_insert_.front().size: the size (integer) to be inserted.
+ if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
+ // Varint32 occupies at most 10 bytes.
+ uint8 insert_buffer[10];
+ uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
+ size_insert_.front().size, insert_buffer);
+ output_->Append(reinterpret_cast<const char*>(insert_buffer),
+ insert_buffer_pos - insert_buffer);
+ size_insert_.pop_front();
+ }
+ }
+ output_->Flush();
+ stream_.reset(new CodedOutputStream(&adapter_));
+ done_ = true;
+}
+
+void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
+ WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
+ static_cast<WireFormatLite::FieldType>(field.kind()));
+ stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
+}
+
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/proto_writer.h b/src/google/protobuf/util/internal/proto_writer.h
new file mode 100644
index 00000000..e631e56f
--- /dev/null
+++ b/src/google/protobuf/util/internal/proto_writer.h
@@ -0,0 +1,315 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__
+
+#include <deque>
+#include <google/protobuf/stubs/hash.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+class CodedOutputStream;
+} // namespace io
+} // namespace protobuf
+
+
+namespace protobuf {
+class Type;
+class Field;
+} // namespace protobuf
+
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectLocationTracker;
+
+// An ObjectWriter that can write protobuf bytes directly from writer events.
+// This class does not support special types like Struct or Map. However, since
+// this class supports raw protobuf, it can be used to provide support for
+// special types by inheriting from it or by wrapping it.
+//
+// It also supports streaming.
+class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter {
+ public:
+// Constructor. Does not take ownership of any parameter passed in.
+ ProtoWriter(TypeResolver* type_resolver, const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener);
+ virtual ~ProtoWriter();
+
+ // ObjectWriter methods.
+ virtual ProtoWriter* StartObject(StringPiece name);
+ virtual ProtoWriter* EndObject();
+ virtual ProtoWriter* StartList(StringPiece name);
+ virtual ProtoWriter* EndList();
+ virtual ProtoWriter* RenderBool(StringPiece name, bool value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderInt32(StringPiece name, int32 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderUint32(StringPiece name, uint32 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderInt64(StringPiece name, int64 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderUint64(StringPiece name, uint64 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderDouble(StringPiece name, double value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderFloat(StringPiece name, float value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderString(StringPiece name, StringPiece value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoWriter* RenderBytes(StringPiece name, StringPiece value) {
+ return RenderDataPiece(name, DataPiece(value, false));
+ }
+ virtual ProtoWriter* RenderNull(StringPiece name) {
+ return RenderDataPiece(name, DataPiece::NullData());
+ }
+
+ // Renders a DataPiece 'value' into a field whose wire type is determined
+ // from the given field 'name'.
+ virtual ProtoWriter* RenderDataPiece(StringPiece name,
+ const DataPiece& value);
+
+ // Returns the location tracker to use for tracking locations for errors.
+ const LocationTrackerInterface& location() {
+ return element_ != NULL ? *element_ : *tracker_;
+ }
+
+ // When true, we finished writing to output a complete message.
+ bool done() const { return done_; }
+
+ // Returns the proto stream object.
+ google::protobuf::io::CodedOutputStream* stream() { return stream_.get(); }
+
+ // Getters and mutators of invalid_depth_.
+ void IncrementInvalidDepth() { ++invalid_depth_; }
+ void DecrementInvalidDepth() { --invalid_depth_; }
+ int invalid_depth() { return invalid_depth_; }
+
+ ErrorListener* listener() { return listener_; }
+
+ const TypeInfo* typeinfo() { return typeinfo_; }
+
+ protected:
+ class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface {
+ public:
+ // Constructor for the root element. No parent nor field.
+ ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type,
+ ProtoWriter* enclosing);
+
+ // Constructor for a field of an element.
+ ProtoElement(ProtoElement* parent, const google::protobuf::Field* field,
+ const google::protobuf::Type& type, bool is_list);
+
+ virtual ~ProtoElement() {}
+
+ // Called just before the destructor for clean up:
+ // - reports any missing required fields
+ // - computes the space needed by the size field, and augment the
+ // length of all parent messages by this additional space.
+ // - releases and returns the parent pointer.
+ ProtoElement* pop();
+
+ // Accessors
+ // parent_field() may be NULL if we are at root.
+ const google::protobuf::Field* parent_field() const {
+ return parent_field_;
+ }
+ const google::protobuf::Type& type() const { return type_; }
+
+ // Registers field for accounting required fields.
+ void RegisterField(const google::protobuf::Field* field);
+
+ // To report location on error messages.
+ virtual string ToString() const;
+
+ virtual ProtoElement* parent() const {
+ return static_cast<ProtoElement*>(BaseElement::parent());
+ }
+
+ // Returns true if the index is already taken by a preceeding oneof input.
+ bool IsOneofIndexTaken(int32 index);
+
+ // Marks the oneof 'index' as taken. Future inputs to this oneof will
+ // generate an error.
+ void TakeOneofIndex(int32 index);
+
+ private:
+ // Used for access to variables of the enclosing instance of
+ // ProtoWriter.
+ ProtoWriter* ow_;
+
+ // Describes the element as a field in the parent message.
+ // parent_field_ is NULL if and only if this element is the root element.
+ const google::protobuf::Field* parent_field_;
+
+ // TypeInfo to lookup types.
+ const TypeInfo* typeinfo_;
+
+ // Additional variables if this element is a message:
+ // (Root element is always a message).
+ // type_ : the type of this element.
+ // required_fields_ : set of required fields.
+ // size_index_ : index into ProtoWriter::size_insert_
+ // for later insertion of serialized message length.
+ const google::protobuf::Type& type_;
+ std::set<const google::protobuf::Field*> required_fields_;
+ const int size_index_;
+
+ // Tracks position in repeated fields, needed for LocationTrackerInterface.
+ int array_index_;
+
+ // Set of oneof indices already seen for the type_. Used to validate
+ // incoming messages so no more than one oneof is set.
+ hash_set<int32> oneof_indices_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement);
+ };
+
+ // Container for inserting 'size' information at the 'pos' position.
+ struct SizeInfo {
+ const int pos;
+ int size;
+ };
+
+ ProtoWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener);
+
+ virtual ProtoElement* element() { return element_.get(); }
+
+ // Helper methods for calling ErrorListener. See error_listener.h.
+ void InvalidName(StringPiece unknown_name, StringPiece message);
+ void InvalidValue(StringPiece type_name, StringPiece value);
+ void MissingField(StringPiece missing_name);
+
+ // Common code for BeginObject() and BeginList() that does invalid_depth_
+ // bookkeeping associated with name lookup.
+ const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list);
+
+ // Lookup the field in the current element. Looks in the base descriptor
+ // and in any extension. This will report an error if the field cannot be
+ // found or if multiple matching extensions are found.
+ const google::protobuf::Field* Lookup(StringPiece name);
+
+ // Lookup the field type in the type descriptor. Returns NULL if the type
+ // is not known.
+ const google::protobuf::Type* LookupType(
+ const google::protobuf::Field* field);
+
+ // Write serialized output to the final output ByteSink, inserting all
+ // the size information for nested messages that are missing from the
+ // intermediate Cord buffer.
+ void WriteRootMessage();
+
+ // Helper method to write proto tags based on the given field.
+ void WriteTag(const google::protobuf::Field& field);
+
+
+ // Returns true if the field for type_ can be set as a oneof. If field is not
+ // a oneof type, this function does nothing and returns true.
+ // If another field for this oneof is already set, this function returns
+ // false. It also calls the appropriate error callback.
+ // unnormalized_name is used for error string.
+ bool ValidOneof(const google::protobuf::Field& field,
+ StringPiece unnormalized_name);
+
+ // Returns true if the field is repeated.
+ bool IsRepeated(const google::protobuf::Field& field);
+
+ private:
+ // Variables for describing the structure of the input tree:
+ // master_type_: descriptor for the whole protobuf message.
+ // typeinfo_ : the TypeInfo object to lookup types.
+ const google::protobuf::Type& master_type_;
+ const TypeInfo* typeinfo_;
+ // Whether we own the typeinfo_ object.
+ bool own_typeinfo_;
+
+ // Indicates whether we finished writing root message completely.
+ bool done_;
+
+ // Variable for internal state processing:
+ // element_ : the current element.
+ // size_insert_: sizes of nested messages.
+ // pos - position to insert the size field.
+ // size - size value to be inserted.
+ google::protobuf::scoped_ptr<ProtoElement> element_;
+ std::deque<SizeInfo> size_insert_;
+
+ // Variables for output generation:
+ // output_ : pointer to an external ByteSink for final user-visible output.
+ // buffer_ : buffer holding partial message before being ready for output_.
+ // adapter_ : internal adapter between CodedOutputStream and buffer_.
+ // stream_ : wrapper for writing tags and other encodings in wire format.
+ strings::ByteSink* output_;
+ string buffer_;
+ google::protobuf::io::StringOutputStream adapter_;
+ google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_;
+
+ // Variables for error tracking and reporting:
+ // listener_ : a place to report any errors found.
+ // invalid_depth_: number of enclosing invalid nested messages.
+ // tracker_ : the root location tracker interface.
+ ErrorListener* listener_;
+ int invalid_depth_;
+ google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc
index 2bbb1597..034d616f 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource.cc
+++ b/src/google/protobuf/util/internal/protostream_objectsource.cc
@@ -140,10 +140,10 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
bool include_start_and_end,
ObjectWriter* ow) const {
- const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
- if (type_renderer != NULL) {
- return (*type_renderer)(this, type, name, ow);
- }
+ const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
+ if (type_renderer != NULL) {
+ return (*type_renderer)(this, type, name, ow);
+ }
const google::protobuf::Field* field = NULL;
string field_name;
@@ -171,7 +171,9 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
if (field->cardinality() ==
google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- if (IsMap(*field)) {
+ bool check_maps = true;
+
+ if (check_maps && IsMap(*field)) {
ow->StartObject(field_name);
ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
ow->EndObject();
@@ -218,48 +220,32 @@ StatusOr<uint32> ProtoStreamObjectSource::RenderMap(
const google::protobuf::Type* field_type =
typeinfo_->GetTypeByTypeUrl(field->type_url());
uint32 tag_to_return = 0;
- if (IsPackable(*field) &&
- list_tag ==
- WireFormatLite::MakeTag(field->number(),
- WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
- RETURN_IF_ERROR(RenderPackedMapEntry(field_type, ow));
- tag_to_return = stream_->ReadTag();
- } else {
- do {
- RETURN_IF_ERROR(RenderMapEntry(field_type, ow));
- } while ((tag_to_return = stream_->ReadTag()) == list_tag);
- }
- return tag_to_return;
-}
-
-Status ProtoStreamObjectSource::RenderMapEntry(
- const google::protobuf::Type* type, ObjectWriter* ow) const {
- uint32 buffer32;
- stream_->ReadVarint32(&buffer32); // message length
- int old_limit = stream_->PushLimit(buffer32);
- string map_key;
- for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
- const google::protobuf::Field* field = FindAndVerifyField(*type, tag);
- if (field == NULL) {
- WireFormat::SkipField(stream_, tag, NULL);
- continue;
- }
- // Map field numbers are key = 1 and value = 2
- if (field->number() == 1) {
- map_key = ReadFieldValueAsString(*field);
- } else if (field->number() == 2) {
- if (map_key.empty()) {
- return Status(util::error::INTERNAL, "Map key must be non-empty");
+ do {
+ // Render map entry message type.
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // message length
+ int old_limit = stream_->PushLimit(buffer32);
+ string map_key;
+ for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
+ const google::protobuf::Field* field =
+ FindAndVerifyField(*field_type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(stream_, tag, NULL);
+ continue;
+ }
+ // Map field numbers are key = 1 and value = 2
+ if (field->number() == 1) {
+ map_key = ReadFieldValueAsString(*field);
+ } else if (field->number() == 2) {
+ if (map_key.empty()) {
+ return Status(util::error::INTERNAL, "Map key must be non-empty");
+ }
+ RETURN_IF_ERROR(RenderField(field, map_key, ow));
}
- // Disable case normalization for map keys as they are just data. We
- // retain them intact.
- ow->DisableCaseNormalizationForNextKey();
- RETURN_IF_ERROR(RenderField(field, map_key, ow));
}
- }
- stream_->PopLimit(old_limit);
-
- return Status::OK;
+ stream_->PopLimit(old_limit);
+ } while ((tag_to_return = stream_->ReadTag()) == list_tag);
+ return tag_to_return;
}
Status ProtoStreamObjectSource::RenderPacked(
@@ -274,18 +260,6 @@ Status ProtoStreamObjectSource::RenderPacked(
return Status::OK;
}
-Status ProtoStreamObjectSource::RenderPackedMapEntry(
- const google::protobuf::Type* type, ObjectWriter* ow) const {
- uint32 length;
- stream_->ReadVarint32(&length);
- int old_limit = stream_->PushLimit(length);
- while (stream_->BytesUntilLimit() > 0) {
- RETURN_IF_ERROR(RenderMapEntry(type, ow));
- }
- stream_->PopLimit(old_limit);
- return Status::OK;
-}
-
Status ProtoStreamObjectSource::RenderTimestamp(
const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
StringPiece field_name, ObjectWriter* ow) {
@@ -601,10 +575,10 @@ Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os,
// nested_type cannot be null at this time.
const google::protobuf::Type* nested_type = resolved_type.ValueOrDie();
- // We know the type so we can render it. Recursively parse the nested stream
- // using a nested ProtoStreamObjectSource using our nested type information.
google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size());
google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream);
+ // We know the type so we can render it. Recursively parse the nested stream
+ // using a nested ProtoStreamObjectSource using our nested type information.
ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type);
// We manually call start and end object here so we can inject the @type.
@@ -676,8 +650,7 @@ void ProtoStreamObjectSource::InitRendererMap() {
&ProtoStreamObjectSource::RenderString;
(*renderers_)["google.protobuf.BytesValue"] =
&ProtoStreamObjectSource::RenderBytes;
- (*renderers_)["google.protobuf.Any"] =
- &ProtoStreamObjectSource::RenderAny;
+ (*renderers_)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny;
(*renderers_)["google.protobuf.Struct"] =
&ProtoStreamObjectSource::RenderStruct;
(*renderers_)["google.protobuf.Value"] =
@@ -704,87 +677,118 @@ ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) {
Status ProtoStreamObjectSource::RenderField(
const google::protobuf::Field* field, StringPiece field_name,
ObjectWriter* ow) const {
+ // Short-circuit message types as it tends to call WriteMessage recursively
+ // and ends up using a lot of stack space. Keep the stack usage of this
+ // message small in order to preserve stack space and not crash.
+ if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // message length
+ int old_limit = stream_->PushLimit(buffer32);
+ // Get the nested message type for this field.
+ const google::protobuf::Type* type =
+ typeinfo_->GetTypeByTypeUrl(field->type_url());
+ if (type == NULL) {
+ return Status(util::error::INTERNAL,
+ StrCat("Invalid configuration. Could not find the type: ",
+ field->type_url()));
+ }
+
+ // Short-circuit any special type rendering to save call-stack space.
+ const TypeRenderer* type_renderer = FindTypeRenderer(type->name());
+
+ bool use_type_renderer = type_renderer != NULL;
+
+ if (use_type_renderer) {
+ RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow));
+ } else {
+ RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
+ }
+ if (!stream_->ConsumedEntireMessage()) {
+ return Status(util::error::INVALID_ARGUMENT,
+ "Nested protocol message not parsed in its entirety.");
+ }
+ stream_->PopLimit(old_limit);
+ } else {
+ // Render all other non-message types.
+ return RenderNonMessageField(field, field_name, ow);
+ }
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderNonMessageField(
+ const google::protobuf::Field* field, StringPiece field_name,
+ ObjectWriter* ow) const {
+ // Temporary buffers of different types.
+ uint32 buffer32;
+ uint64 buffer64;
+ string strbuffer;
switch (field->kind()) {
case google::protobuf::Field_Kind_TYPE_BOOL: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderBool(field_name, buffer64 != 0);
break;
}
case google::protobuf::Field_Kind_TYPE_INT32: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_INT64: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_UINT32: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_UINT64: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_SINT32: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_SINT64: {
- uint64 buffer64;
stream_->ReadVarint64(&buffer64);
ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_SFIXED32: {
- uint32 buffer32;
stream_->ReadLittleEndian32(&buffer32);
ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_SFIXED64: {
- uint64 buffer64;
stream_->ReadLittleEndian64(&buffer64);
ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_FIXED32: {
- uint32 buffer32;
stream_->ReadLittleEndian32(&buffer32);
ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_FIXED64: {
- uint64 buffer64;
stream_->ReadLittleEndian64(&buffer64);
ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_FLOAT: {
- uint32 buffer32;
stream_->ReadLittleEndian32(&buffer32);
ow->RenderFloat(field_name, bit_cast<float>(buffer32));
break;
}
case google::protobuf::Field_Kind_TYPE_DOUBLE: {
- uint64 buffer64;
stream_->ReadLittleEndian64(&buffer64);
ow->RenderDouble(field_name, bit_cast<double>(buffer64));
break;
}
case google::protobuf::Field_Kind_TYPE_ENUM: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32);
// If the field represents an explicit NULL value, render null.
@@ -811,40 +815,15 @@ Status ProtoStreamObjectSource::RenderField(
break;
}
case google::protobuf::Field_Kind_TYPE_STRING: {
- uint32 buffer32;
- string str;
stream_->ReadVarint32(&buffer32); // string size.
- stream_->ReadString(&str, buffer32);
- ow->RenderString(field_name, str);
+ stream_->ReadString(&strbuffer, buffer32);
+ ow->RenderString(field_name, strbuffer);
break;
}
case google::protobuf::Field_Kind_TYPE_BYTES: {
- uint32 buffer32;
stream_->ReadVarint32(&buffer32); // bytes size.
- string value;
- stream_->ReadString(&value, buffer32);
- ow->RenderBytes(field_name, value);
- break;
- }
- case google::protobuf::Field_Kind_TYPE_MESSAGE: {
- uint32 buffer32;
- stream_->ReadVarint32(&buffer32); // message length
- int old_limit = stream_->PushLimit(buffer32);
- // Get the nested message type for this field.
- const google::protobuf::Type* type =
- typeinfo_->GetTypeByTypeUrl(field->type_url());
- if (type == NULL) {
- return Status(util::error::INTERNAL,
- StrCat("Invalid configuration. Could not find the type: ",
- field->type_url()));
- }
-
- RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
- if (!stream_->ConsumedEntireMessage()) {
- return Status(util::error::INVALID_ARGUMENT,
- "Nested protocol message not parsed in its entirety.");
- }
- stream_->PopLimit(old_limit);
+ stream_->ReadString(&strbuffer, buffer32);
+ ow->RenderBytes(field_name, strbuffer);
break;
}
default:
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h
index 3cd37aa1..78defa1d 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource.h
+++ b/src/google/protobuf/util/internal/protostream_objectsource.h
@@ -122,20 +122,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
StringPiece name, uint32 list_tag,
ObjectWriter* ow) const;
- // Renders an entry in a map, advancing stream pointers appropriately.
- util::Status RenderMapEntry(const google::protobuf::Type* type,
- ObjectWriter* ow) const;
-
// Renders a packed repeating field. A packed field is stored as:
// {tag length item1 item2 item3} instead of the less efficient
// {tag item1 tag item2 tag item3}.
util::Status RenderPacked(const google::protobuf::Field* field,
ObjectWriter* ow) const;
- // Equivalent of RenderPacked, but for map entries.
- util::Status RenderPackedMapEntry(const google::protobuf::Type* type,
- ObjectWriter* ow) const;
-
// Renders a google.protobuf.Timestamp value to ObjectWriter
static util::Status RenderTimestamp(const ProtoStreamObjectSource* os,
const google::protobuf::Type& type,
@@ -210,6 +202,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
util::Status RenderField(const google::protobuf::Field* field,
StringPiece field_name, ObjectWriter* ow) const;
+ // Same as above but renders all non-message field types. Callers don't call
+ // this function directly. They just use RenderField.
+ util::Status RenderNonMessageField(const google::protobuf::Field* field,
+ StringPiece field_name,
+ ObjectWriter* ow) const;
+
// Reads field value according to Field spec in 'field' and returns the read
// value as string. This only works for primitive datatypes (no message
@@ -238,6 +236,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
// google::protobuf::Type of the message source.
const google::protobuf::Type& type_;
+
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource);
};
diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
index f6e5ee7a..561f6763 100644
--- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc
+++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
@@ -327,9 +327,16 @@ TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) {
DoTest(primitive, Primitive::descriptor());
}
+TEST_P(ProtostreamObjectSourceTest, CustomJsonName) {
+ Author author;
+ author.set_id(12345);
+
+ ow_.StartObject("")->RenderUint64("@id", 12345)->EndObject();
+ DoTest(author, Author::descriptor());
+}
+
TEST_P(ProtostreamObjectSourceTest, NestedMessage) {
Author* author = new Author();
- author->set_id(101L);
author->set_name("Tolstoy");
Book book;
book.set_title("My Book");
@@ -338,7 +345,6 @@ TEST_P(ProtostreamObjectSourceTest, NestedMessage) {
ow_.StartObject("")
->RenderString("title", "My Book")
->StartObject("author")
- ->RenderUint64("id", bit_cast<uint64>(101LL))
->RenderString("name", "Tolstoy")
->EndObject()
->EndObject();
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc
index 0958997c..786bf0be 100644
--- a/src/google/protobuf/util/internal/protostream_objectwriter.cc
+++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc
@@ -51,7 +51,6 @@ namespace util {
namespace converter {
using google::protobuf::internal::WireFormatLite;
-using google::protobuf::io::CodedOutputStream;
using util::error::INVALID_ARGUMENT;
using util::Status;
using util::StatusOr;
@@ -60,230 +59,31 @@ using util::StatusOr;
ProtoStreamObjectWriter::ProtoStreamObjectWriter(
TypeResolver* type_resolver, const google::protobuf::Type& type,
strings::ByteSink* output, ErrorListener* listener)
- : master_type_(type),
- typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
- own_typeinfo_(true),
- done_(false),
- element_(NULL),
- size_insert_(),
- output_(output),
- buffer_(),
- adapter_(&buffer_),
- stream_(new CodedOutputStream(&adapter_)),
- listener_(listener),
- invalid_depth_(0),
- tracker_(new ObjectLocationTracker()) {}
+ : ProtoWriter(type_resolver, type, output, listener),
+ master_type_(type),
+ current_(NULL) {}
ProtoStreamObjectWriter::ProtoStreamObjectWriter(
const TypeInfo* typeinfo, const google::protobuf::Type& type,
strings::ByteSink* output, ErrorListener* listener)
- : master_type_(type),
- typeinfo_(typeinfo),
- own_typeinfo_(false),
- done_(false),
- element_(NULL),
- size_insert_(),
- output_(output),
- buffer_(),
- adapter_(&buffer_),
- stream_(new CodedOutputStream(&adapter_)),
- listener_(listener),
- invalid_depth_(0),
- tracker_(new ObjectLocationTracker()) {}
+ : ProtoWriter(typeinfo, type, output, listener),
+ master_type_(type),
+ current_(NULL) {}
ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
- if (own_typeinfo_) {
- delete typeinfo_;
- }
- if (element_ == NULL) return;
+ if (current_ == NULL) return;
// Cleanup explicitly in order to avoid destructor stack overflow when input
// is deeply nested.
// Cast to BaseElement to avoid doing additional checks (like missing fields)
// during pop().
google::protobuf::scoped_ptr<BaseElement> element(
- static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
+ static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
while (element != NULL) {
element.reset(element->pop<BaseElement>());
}
}
namespace {
-
-// Writes an INT32 field, including tag to the stream.
-inline Status WriteInt32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int32> i32 = data.ToInt32();
- if (i32.ok()) {
- WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
- }
- return i32.status();
-}
-
-// writes an SFIXED32 field, including tag, to the stream.
-inline Status WriteSFixed32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int32> i32 = data.ToInt32();
- if (i32.ok()) {
- WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
- }
- return i32.status();
-}
-
-// Writes an SINT32 field, including tag, to the stream.
-inline Status WriteSInt32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int32> i32 = data.ToInt32();
- if (i32.ok()) {
- WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
- }
- return i32.status();
-}
-
-// Writes a FIXED32 field, including tag, to the stream.
-inline Status WriteFixed32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint32> u32 = data.ToUint32();
- if (u32.ok()) {
- WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
- }
- return u32.status();
-}
-
-// Writes a UINT32 field, including tag, to the stream.
-inline Status WriteUInt32(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint32> u32 = data.ToUint32();
- if (u32.ok()) {
- WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
- }
- return u32.status();
-}
-
-// Writes an INT64 field, including tag, to the stream.
-inline Status WriteInt64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int64> i64 = data.ToInt64();
- if (i64.ok()) {
- WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
- }
- return i64.status();
-}
-
-// Writes an SFIXED64 field, including tag, to the stream.
-inline Status WriteSFixed64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int64> i64 = data.ToInt64();
- if (i64.ok()) {
- WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
- }
- return i64.status();
-}
-
-// Writes an SINT64 field, including tag, to the stream.
-inline Status WriteSInt64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<int64> i64 = data.ToInt64();
- if (i64.ok()) {
- WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
- }
- return i64.status();
-}
-
-// Writes a FIXED64 field, including tag, to the stream.
-inline Status WriteFixed64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint64> u64 = data.ToUint64();
- if (u64.ok()) {
- WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
- }
- return u64.status();
-}
-
-// Writes a UINT64 field, including tag, to the stream.
-inline Status WriteUInt64(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<uint64> u64 = data.ToUint64();
- if (u64.ok()) {
- WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
- }
- return u64.status();
-}
-
-// Writes a DOUBLE field, including tag, to the stream.
-inline Status WriteDouble(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<double> d = data.ToDouble();
- if (d.ok()) {
- WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
- }
- return d.status();
-}
-
-// Writes a FLOAT field, including tag, to the stream.
-inline Status WriteFloat(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<float> f = data.ToFloat();
- if (f.ok()) {
- WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
- }
- return f.status();
-}
-
-// Writes a BOOL field, including tag, to the stream.
-inline Status WriteBool(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<bool> b = data.ToBool();
- if (b.ok()) {
- WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
- }
- return b.status();
-}
-
-// Writes a BYTES field, including tag, to the stream.
-inline Status WriteBytes(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<string> c = data.ToBytes();
- if (c.ok()) {
- WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
- }
- return c.status();
-}
-
-// Writes a STRING field, including tag, to the stream.
-inline Status WriteString(int field_number, const DataPiece& data,
- CodedOutputStream* stream) {
- StatusOr<string> s = data.ToString();
- if (s.ok()) {
- WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
- }
- return s.status();
-}
-
-// Writes an ENUM field, including tag, to the stream.
-inline Status WriteEnum(int field_number, const DataPiece& data,
- const google::protobuf::Enum* enum_type,
- CodedOutputStream* stream) {
- StatusOr<int> e = data.ToEnum(enum_type);
- if (e.ok()) {
- WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
- }
- return e.status();
-}
-
-// Given a google::protobuf::Type, returns the set of all required fields.
-std::set<const google::protobuf::Field*> GetRequiredFields(
- const google::protobuf::Type& type) {
- std::set<const google::protobuf::Field*> required;
- for (int i = 0; i < type.fields_size(); i++) {
- const google::protobuf::Field& field = type.fields(i);
- if (field.cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
- required.insert(&field);
- }
- }
- return required;
-}
-
// Utility method to split a string representation of Timestamp or Duration and
// return the parts.
void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
@@ -298,6 +98,78 @@ void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
}
}
+Status GetNanosFromStringPiece(StringPiece s_nanos,
+ const char* parse_failure_message,
+ const char* exceeded_limit_message,
+ int32* nanos) {
+ *nanos = 0;
+
+ // Count the number of leading 0s and consume them.
+ int num_leading_zeros = 0;
+ while (s_nanos.Consume("0")) {
+ num_leading_zeros++;
+ }
+ int32 i_nanos = 0;
+ // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
+ // "0." + s_nanos.ToString() seconds. An int32 is used for the
+ // conversion to 'nanos', rather than a double, so that there is no
+ // loss of precision.
+ if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) {
+ return Status(INVALID_ARGUMENT, parse_failure_message);
+ }
+ if (i_nanos > kNanosPerSecond || i_nanos < 0) {
+ return Status(INVALID_ARGUMENT, exceeded_limit_message);
+ }
+ // s_nanos should only have digits. No whitespace.
+ if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
+ return Status(INVALID_ARGUMENT, parse_failure_message);
+ }
+
+ if (i_nanos > 0) {
+ // 'scale' is the number of digits to the right of the decimal
+ // point in "0." + s_nanos.ToString()
+ int32 scale = num_leading_zeros + s_nanos.size();
+ // 'conversion' converts i_nanos into nanoseconds.
+ // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale))
+ // For efficiency, we precompute the conversion factor.
+ int32 conversion = 0;
+ switch (scale) {
+ case 1:
+ conversion = 100000000;
+ break;
+ case 2:
+ conversion = 10000000;
+ break;
+ case 3:
+ conversion = 1000000;
+ break;
+ case 4:
+ conversion = 100000;
+ break;
+ case 5:
+ conversion = 10000;
+ break;
+ case 6:
+ conversion = 1000;
+ break;
+ case 7:
+ conversion = 100;
+ break;
+ case 8:
+ conversion = 10;
+ break;
+ case 9:
+ conversion = 1;
+ break;
+ default:
+ return Status(INVALID_ARGUMENT, exceeded_limit_message);
+ }
+ *nanos = i_nanos * conversion;
+ }
+
+ return Status::OK;
+}
+
} // namespace
ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
@@ -421,7 +293,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
}
// Resolve the type url, and report an error if we failed to resolve it.
StatusOr<const google::protobuf::Type*> resolved_type =
- parent_->typeinfo_->ResolveTypeUrl(type_url_);
+ parent_->typeinfo()->ResolveTypeUrl(type_url_);
if (!resolved_type.ok()) {
parent_->InvalidValue("Any", resolved_type.status().error_message());
invalid_ = true;
@@ -440,8 +312,8 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
// Create our object writer and initialize it with the first StartObject
// call.
- ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo_, *type, &output_,
- parent_->listener_));
+ ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
+ parent_->listener()));
ow_->StartObject("");
}
@@ -453,604 +325,431 @@ void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
}
// Render the type_url and value fields directly to the stream.
// type_url has tag 1 and value has tag 2.
- WireFormatLite::WriteString(1, type_url_, parent_->stream_.get());
+ WireFormatLite::WriteString(1, type_url_, parent_->stream());
if (!data_.empty()) {
- WireFormatLite::WriteBytes(2, data_, parent_->stream_.get());
+ WireFormatLite::WriteBytes(2, data_, parent_->stream());
}
}
-ProtoStreamObjectWriter::ProtoElement::ProtoElement(
- const TypeInfo* typeinfo, const google::protobuf::Type& type,
- ProtoStreamObjectWriter* enclosing)
+ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
+ ItemType item_type, bool is_placeholder,
+ bool is_list)
: BaseElement(NULL),
ow_(enclosing),
any_(),
- field_(NULL),
- typeinfo_(typeinfo),
- type_(type),
- required_fields_(GetRequiredFields(type)),
- is_repeated_type_(false),
- size_index_(-1),
- array_index_(-1),
- element_type_(GetElementType(type_)) {
- if (element_type_ == ANY) {
+ item_type_(item_type),
+ is_placeholder_(is_placeholder),
+ is_list_(is_list) {
+ if (item_type_ == ANY) {
any_.reset(new AnyWriter(ow_));
}
}
-ProtoStreamObjectWriter::ProtoElement::ProtoElement(
- ProtoStreamObjectWriter::ProtoElement* parent,
- const google::protobuf::Field* field, const google::protobuf::Type& type,
- ElementType element_type)
+ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
+ ItemType item_type, bool is_placeholder,
+ bool is_list)
: BaseElement(parent),
ow_(this->parent()->ow_),
any_(),
- field_(field),
- typeinfo_(this->parent()->typeinfo_),
- type_(type),
- is_repeated_type_(element_type == ProtoElement::LIST ||
- element_type == ProtoElement::STRUCT_LIST ||
- element_type == ProtoElement::MAP ||
- element_type == ProtoElement::STRUCT_MAP),
- size_index_(!is_repeated_type_ &&
- field->kind() ==
- google::protobuf::Field_Kind_TYPE_MESSAGE
- ? ow_->size_insert_.size()
- : -1),
- array_index_(is_repeated_type_ ? 0 : -1),
- element_type_(element_type) {
- if (!is_repeated_type_) {
- if (field->cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- // Update array_index_ if it is an explicit list.
- if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
- } else {
- this->parent()->RegisterField(field);
- }
- if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
- required_fields_ = GetRequiredFields(type_);
- int start_pos = ow_->stream_->ByteCount();
- // length of serialized message is the final buffer position minus
- // starting buffer position, plus length adjustments for size fields
- // of any nested messages. We start with -start_pos here, so we only
- // need to add the final buffer position to it at the end.
- SizeInfo info = {start_pos, -start_pos};
- ow_->size_insert_.push_back(info);
- }
- }
- if (element_type == ANY) {
+ item_type_(item_type),
+ is_placeholder_(is_placeholder),
+ is_list_(is_list) {
+ if (item_type == ANY) {
any_.reset(new AnyWriter(ow_));
}
}
-ProtoStreamObjectWriter::ProtoElement*
-ProtoStreamObjectWriter::ProtoElement::pop() {
- // Calls the registered error listener for any required field(s) not yet
- // seen.
- for (set<const google::protobuf::Field*>::iterator it =
- required_fields_.begin();
- it != required_fields_.end(); ++it) {
- ow_->MissingField((*it)->name());
- }
- // Computes the total number of proto bytes used by a message, also adjusts
- // the size of all parent messages by the length of this size field.
- // If size_index_ < 0, this is not a message, so no size field is added.
- if (size_index_ >= 0) {
- // Add the final buffer position to compute the total length of this
- // serialized message. The stored value (before this addition) already
- // contains the total length of the size fields of all nested messages
- // minus the initial buffer position.
- ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
- // Calculate the length required to serialize the size field of the
- // message, and propagate this additional size information upward to
- // all enclosing messages.
- int size = ow_->size_insert_[size_index_].size;
- int length = CodedOutputStream::VarintSize32(size);
- for (ProtoElement* e = parent(); e != NULL; e = e->parent()) {
- // Only nested messages have size field, lists do not have size field.
- if (e->size_index_ >= 0) {
- ow_->size_insert_[e->size_index_].size += length;
- }
- }
- }
- return BaseElement::pop<ProtoElement>();
-}
-
-void ProtoStreamObjectWriter::ProtoElement::RegisterField(
- const google::protobuf::Field* field) {
- if (!required_fields_.empty() &&
- field->cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
- required_fields_.erase(field);
- }
-}
-
-string ProtoStreamObjectWriter::ProtoElement::ToString() const {
- if (parent() == NULL) return "";
- string loc = parent()->ToString();
- if (field_->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED ||
- parent()->field_ != field_) {
- string name = field_->name();
- int i = 0;
- while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
- if (i > 0 && i == name.size()) { // safe field name
- if (loc.empty()) {
- loc = name;
- } else {
- StrAppend(&loc, ".", name);
- }
- } else {
- StrAppend(&loc, "[\"", CEscape(name), "\"]");
- }
- }
- if (field_->cardinality() ==
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
- array_index_ > 0) {
- StrAppend(&loc, "[", array_index_ - 1, "]");
- }
- return loc.empty() ? "." : loc;
-}
-
-bool ProtoStreamObjectWriter::ProtoElement::OneofIndexTaken(int32 index) {
- return ContainsKey(oneof_indices_, index);
-}
-
-void ProtoStreamObjectWriter::ProtoElement::TakeOneofIndex(int32 index) {
- InsertIfNotPresent(&oneof_indices_, index);
-}
-
-bool ProtoStreamObjectWriter::ProtoElement::InsertMapKeyIfNotPresent(
+bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
StringPiece map_key) {
return InsertIfNotPresent(&map_keys_, map_key.ToString());
}
-inline void ProtoStreamObjectWriter::InvalidName(StringPiece unknown_name,
- StringPiece message) {
- listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
-}
-
-inline void ProtoStreamObjectWriter::InvalidValue(StringPiece type_name,
- StringPiece value) {
- listener_->InvalidValue(location(), type_name, value);
-}
-
-inline void ProtoStreamObjectWriter::MissingField(StringPiece missing_name) {
- listener_->MissingField(location(), missing_name);
-}
-
ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
StringPiece name) {
- // Starting the root message. Create the root ProtoElement and return.
- if (element_ == NULL) {
- if (!name.empty()) {
- InvalidName(name, "Root element should not be named.");
- }
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+ if (invalid_depth() > 0) {
+ IncrementInvalidDepth();
+ return this;
+ }
+
+ // Starting the root message. Create the root Item and return.
+ // ANY message type does not need special handling, just set the ItemType
+ // to ANY.
+ if (current_ == NULL) {
+ ProtoWriter::StartObject(name);
+ current_.reset(new Item(
+ this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
+ false, false));
// If master type is a special type that needs extra values to be written to
// stream, we write those values.
if (master_type_.name() == kStructType) {
- StartStruct(NULL);
- } else if (master_type_.name() == kStructValueType) {
- // We got a StartObject call with google.protobuf.Value field. This means
- // we are starting an object within google.protobuf.Value type. The only
- // object within that type is a struct type. So start a struct.
- const google::protobuf::Field* field = StartStructValueInStruct(NULL);
- StartStruct(field);
+ // Struct has a map<string, Value> field called "fields".
+ // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
+ // "fields": [
+ Push("fields", Item::MAP, true, true);
+ return this;
}
- return this;
- }
- const google::protobuf::Field* field = NULL;
- if (element_ != NULL && element_->IsAny()) {
- element_->any()->StartObject(name);
- return this;
- } else if (element_ != NULL &&
- (element_->IsMap() || element_->IsStructMap())) {
- if (!ValidMapKey(name)) {
- ++invalid_depth_;
+ if (master_type_.name() == kStructValueType) {
+ // We got a StartObject call with google.protobuf.Value field. The only
+ // object within that type is a struct type. So start a struct.
+ //
+ // The struct field in Value type is named "struct_value"
+ // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
+ // Also start the map field "fields" within the struct.
+ // "struct_value": {
+ // "fields": [
+ Push("struct_value", Item::MESSAGE, true, false);
+ Push("fields", Item::MAP, true, true);
return this;
}
- field = StartMapEntry(name);
- if (element_->IsStructMapEntry()) {
- // If the top element is a map entry, this means we are starting another
- // struct within a struct.
- field = StartStructValueInStruct(field);
+ if (master_type_.name() == kStructListValueType) {
+ InvalidValue(kStructListValueType,
+ "Cannot start root message with ListValue.");
}
- } else if (element_ != NULL && element_->IsStructList()) {
- // If the top element is a list, then we are starting a list field within a
- // struct.
- field = Lookup(name);
- field = StartStructValueInStruct(field);
- } else {
- field = BeginNamed(name, false);
- }
- if (field == NULL) {
- return this;
- }
- const google::protobuf::Type* type = LookupType(field);
- if (type == NULL) {
- ++invalid_depth_;
- InvalidName(name,
- StrCat("Missing descriptor for field: ", field->type_url()));
return this;
}
- // Check to see if this field is a oneof and that no oneof in that group has
- // already been set.
- if (!ValidOneof(*field, name)) {
- ++invalid_depth_;
+ // Send all ANY events to AnyWriter.
+ if (current_->IsAny()) {
+ current_->any()->StartObject(name);
return this;
}
- if (field->type_url() == GetFullTypeWithUrl(kStructType)) {
- // Start a struct object.
- StartStruct(field);
- } else if (field->type_url() == GetFullTypeWithUrl(kStructValueType)) {
- // We got a StartObject call with google.protobuf.Value field. This means we
- // are starting an object within google.protobuf.Value type. The only object
- // within that type is a struct type. So start a struct.
- field = StartStructValueInStruct(field);
- StartStruct(field);
- } else if (field->type_url() == GetFullTypeWithUrl(kAnyType)) {
- // Begin an Any. We can't do the real work till we get the @type field.
- WriteTag(*field);
- element_.reset(
- new ProtoElement(element_.release(), field, *type, ProtoElement::ANY));
- } else if (IsMap(*field)) {
- // Begin a map.
- // A map is triggered by a StartObject() call if the current field has a map
- // type. Map values are written to proto in a manner detailed in comments
- // above StartMapEntry() function.
- element_.reset(
- new ProtoElement(element_.release(), field, *type, ProtoElement::MAP));
- } else {
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::MESSAGE));
- }
- return this;
-}
-
-// Proto3 maps are represented on the wire as a message with
-// "key" and a "value".
-//
-// For example, the syntax:
-// map<key_type, value_type> map_field = N;
-//
-// is represented as:
-// message MapFieldEntry {
-// option map_entry = true; // marks the map construct in the descriptor
-//
-// key_type key = 1;
-// value_type value = 2;
-// }
-// repeated MapFieldEntry map_field = N;
-//
-// See go/proto3-maps for more information.
-const google::protobuf::Field* ProtoStreamObjectWriter::StartMapEntry(
- StringPiece name) {
- // top of stack is already a map field
- const google::protobuf::Field* field = element_->field();
- const google::protobuf::Type& type = element_->type();
- // If we come from a regular map, use MAP_ENTRY or if we come from a struct,
- // use STRUCT_MAP_ENTRY. These values are used later in StartObject/StartList
- // or RenderDataPiece for making appropriate decisions.
- ProtoElement::ElementType element_type = element_->IsStructMap()
- ? ProtoElement::STRUCT_MAP_ENTRY
- : ProtoElement::MAP_ENTRY;
- WriteTag(*field);
- element_.reset(
- new ProtoElement(element_.release(), field, type, element_type));
- RenderDataPiece("key", DataPiece(name));
- return BeginNamed("value", false);
-}
+ // If we are within a map, we render name as keys and send StartObject to the
+ // value field.
+ if (current_->IsMap()) {
+ if (!ValidMapKey(name)) {
+ IncrementInvalidDepth();
+ return this;
+ }
-// Starts a google.protobuf.Struct.
-// 'field' represents a field in a message of type google.protobuf.Struct. A
-// struct contains a map with name 'fields'. This function starts this map as
-// well.
-// When 'field' is NULL, it means that the top level message is of struct
-// type.
-void ProtoStreamObjectWriter::StartStruct(
- const google::protobuf::Field* field) {
- const google::protobuf::Type* type = NULL;
- if (field) {
- type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT));
- }
- const google::protobuf::Field* struct_field = BeginNamed("fields", false);
+ // Map is a repeated field of message type with a "key" and a "value" field.
+ // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
+ // message MapFieldEntry {
+ // key_type key = 1;
+ // value_type value = 2;
+ // }
+ //
+ // repeated MapFieldEntry map_field = N;
+ //
+ // That means, we render the following element within a list (hence no
+ // name):
+ // { "key": "<name>", "value": {
+ Push("", Item::MESSAGE, false, false);
+ ProtoWriter::RenderDataPiece("key", DataPiece(name));
+ Push("value", Item::MESSAGE, true, false);
+
+ // Make sure we are valid so far after starting map fields.
+ if (invalid_depth() > 0) return this;
+
+ // If top of stack is g.p.Struct type, start the struct the map field within
+ // it.
+ if (element() != NULL && IsStruct(*element()->parent_field())) {
+ // Render "fields": [
+ Push("fields", Item::MAP, true, true);
+ return this;
+ }
- if (!struct_field) {
- // It is a programmatic error if this happens. Log an error.
- GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'fields' within "
- << (field ? field->type_url() : "google.protobuf.Struct");
- return;
+ // If top of stack is g.p.Value type, start the Struct within it.
+ if (element() != NULL && IsStructValue(*element()->parent_field())) {
+ // Render
+ // "struct_value": {
+ // "fields": [
+ Push("struct_value", Item::MESSAGE, true, false);
+ Push("fields", Item::MAP, true, true);
+ }
+ return this;
}
- type = LookupType(struct_field);
- element_.reset(new ProtoElement(element_.release(), struct_field, *type,
- ProtoElement::STRUCT_MAP));
-}
+ const google::protobuf::Field* field = BeginNamed(name, false);
+ if (field == NULL) return this;
-// Starts a "struct_value" within struct.proto's google.protobuf.Value type.
-// 'field' should be of the type google.protobuf.Value.
-// Returns the field identifying "struct_value" within the given field.
-//
-// If field is NULL, then we are starting struct_value at the top-level, in
-// this case skip writing any tag information for the passed field.
-const google::protobuf::Field*
-ProtoStreamObjectWriter::StartStructValueInStruct(
- const google::protobuf::Field* field) {
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_VALUE));
- }
- return BeginNamed("struct_value", false);
-}
-
-// Starts a "list_value" within struct.proto's google.protobuf.Value type.
-// 'field' should be of the type google.protobuf.Value.
-// Returns the field identifying "list_value" within the given field.
-//
-// If field is NULL, then we are starting list_value at the top-level, in
-// this case skip writing any tag information for the passed field.
-const google::protobuf::Field* ProtoStreamObjectWriter::StartListValueInStruct(
- const google::protobuf::Field* field) {
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_VALUE));
+ if (IsStruct(*field)) {
+ // Start a struct object.
+ // Render
+ // "<name>": {
+ // "fields": {
+ Push(name, Item::MESSAGE, false, false);
+ Push("fields", Item::MAP, true, true);
+ return this;
}
- const google::protobuf::Field* list_value = BeginNamed("list_value", false);
- if (!list_value) {
- // It is a programmatic error if this happens. Log an error.
- GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'list_value' within "
- << (field ? field->type_url() : "google.protobuf.Value");
- return field;
+ if (IsStructValue(*field)) {
+ // We got a StartObject call with google.protobuf.Value field. The only
+ // object within that type is a struct type. So start a struct.
+ // Render
+ // "<name>": {
+ // "struct_value": {
+ // "fields": {
+ Push(name, Item::MESSAGE, false, false);
+ Push("struct_value", Item::MESSAGE, true, false);
+ Push("fields", Item::MAP, true, true);
+ return this;
}
- return StartRepeatedValuesInListValue(list_value);
-}
-
-// Starts the repeated "values" field in struct.proto's
-// google.protobuf.ListValue type. 'field' should be of type
-// google.protobuf.ListValue.
-//
-// If field is NULL, then we are starting ListValue at the top-level, in
-// this case skip writing any tag information for the passed field.
-const google::protobuf::Field*
-ProtoStreamObjectWriter::StartRepeatedValuesInListValue(
- const google::protobuf::Field* field) {
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- WriteTag(*field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_LIST_VALUE));
+ if (IsMap(*field)) {
+ // Begin a map. A map is triggered by a StartObject() call if the current
+ // field has a map type.
+ // A map type is always repeated, hence set is_list to true.
+ // Render
+ // "<name>": [
+ Push(name, Item::MAP, false, true);
+ return this;
}
- return BeginNamed("values", true);
-}
-void ProtoStreamObjectWriter::SkipElements() {
- if (element_ == NULL) return;
-
- ProtoElement::ElementType element_type = element_->element_type();
- while (element_type == ProtoElement::STRUCT ||
- element_type == ProtoElement::STRUCT_LIST_VALUE ||
- element_type == ProtoElement::STRUCT_VALUE ||
- element_type == ProtoElement::STRUCT_MAP_ENTRY ||
- element_type == ProtoElement::MAP_ENTRY) {
- element_.reset(element_->pop());
- element_type =
- element_ != NULL ? element_->element_type() : ProtoElement::MESSAGE;
- }
+ // A regular message type. Pass it directly to ProtoWriter.
+ // Render
+ // "<name>": {
+ Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
+ return this;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
- if (invalid_depth_ > 0) {
- --invalid_depth_;
+ if (invalid_depth() > 0) {
+ DecrementInvalidDepth();
return this;
}
- if (element_ != NULL && element_->IsAny()) {
- if (element_->any()->EndObject()) {
- return this;
- }
- }
- if (element_ != NULL) {
- element_.reset(element_->pop());
- }
- // Skip sentinel elements added to keep track of new proto3 types - map,
- // struct.
- SkipElements();
+ if (current_ == NULL) return this;
-
- // If ending the root element,
- // then serialize the full message with calculated sizes.
- if (element_ == NULL) {
- WriteRootMessage();
+ if (current_->IsAny()) {
+ if (current_->any()->EndObject()) return this;
}
+
+ Pop();
+
return this;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) {
- const google::protobuf::Field* field = NULL;
+ if (invalid_depth() > 0) {
+ IncrementInvalidDepth();
+ return this;
+ }
+
// Since we cannot have a top-level repeated item in protobuf, the only way
- // element_ can be null when here is when we start a top-level list
- // google.protobuf.ListValue.
- if (element_ == NULL) {
+ // this is valid is if we start a special type google.protobuf.ListValue or
+ // google.protobuf.Value.
+ if (current_ == NULL) {
if (!name.empty()) {
InvalidName(name, "Root element should not be named.");
+ IncrementInvalidDepth();
+ return this;
}
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
// If master type is a special type that needs extra values to be written to
// stream, we write those values.
if (master_type_.name() == kStructValueType) {
// We got a StartList with google.protobuf.Value master type. This means
// we have to start the "list_value" within google.protobuf.Value.
- field = StartListValueInStruct(NULL);
- } else if (master_type_.name() == kStructListValueType) {
+ //
+ // See
+ // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
+ //
+ // Render
+ // "<name>": {
+ // "list_value": {
+ // "values": [ // Start this list.
+ ProtoWriter::StartObject(name);
+ current_.reset(new Item(this, Item::MESSAGE, false, false));
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
+
+ if (master_type_.name() == kStructListValueType) {
// We got a StartList with google.protobuf.ListValue master type. This
// means we have to start the "values" within google.protobuf.ListValue.
- field = StartRepeatedValuesInListValue(NULL);
+ //
+ // Render
+ // "<name>": {
+ // "values": [ // Start this list.
+ ProtoWriter::StartObject(name);
+ current_.reset(new Item(this, Item::MESSAGE, false, false));
+ Push("values", Item::MESSAGE, true, true);
+ return this;
}
- // field is NULL when master_type_ is anything other than
- // google.protobuf.Value or google.protobuf.ListValue.
- if (field) {
- const google::protobuf::Type* type = LookupType(field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::STRUCT_LIST));
- }
+ // Send the event to ProtoWriter so proper errors can be reported.
+ //
+ // Render a regular list:
+ // "<name>": [
+ ProtoWriter::StartList(name);
+ current_.reset(new Item(this, Item::MESSAGE, false, true));
return this;
}
- if (element_->IsAny()) {
- element_->any()->StartList(name);
+ if (current_->IsAny()) {
+ current_->any()->StartList(name);
return this;
}
- // The type of element we push to stack.
- ProtoElement::ElementType element_type = ProtoElement::LIST;
- // Check if we need to start a map. This can heppen when there is either a map
- // or a struct type within a list.
- if (element_->IsMap() || element_->IsStructMap()) {
+ // If the top of stack is a map, we are starting a list value within a map.
+ // Since map does not allow repeated values, this can only happen when the map
+ // value is of a special type that renders a list in JSON. These can be one
+ // of 3 cases:
+ // i. We are rendering a list value within google.protobuf.Struct
+ // ii. We are rendering a list value within google.protobuf.Value
+ // iii. We are rendering a list value with type google.protobuf.ListValue.
+ if (current_->IsMap()) {
if (!ValidMapKey(name)) {
- ++invalid_depth_;
+ IncrementInvalidDepth();
return this;
}
- field = StartMapEntry(name);
- if (field == NULL) return this;
+ // Start the repeated map entry object.
+ // Render
+ // { "key": "<name>", "value": {
+ Push("", Item::MESSAGE, false, false);
+ ProtoWriter::RenderDataPiece("key", DataPiece(name));
+ Push("value", Item::MESSAGE, true, false);
+
+ // Make sure we are valid after pushing all above items.
+ if (invalid_depth() > 0) return this;
+
+ // case i and ii above. Start "list_value" field within g.p.Value
+ if (element() != NULL && element()->parent_field() != NULL) {
+ // Render
+ // "list_value": {
+ // "values": [ // Start this list
+ if (IsStructValue(*element()->parent_field())) {
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
- if (element_->IsStructMapEntry()) {
- // If the top element is a map entry, this means we are starting a list
- // within a struct or a map.
- // An example sequence of calls would be
- // StartObject -> StartList
- field = StartListValueInStruct(field);
- if (field == NULL) return this;
+ // Render
+ // "values": [
+ if (IsStructListValue(*element()->parent_field())) {
+ // case iii above. Bind directly to g.p.ListValue
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
}
- element_type = ProtoElement::STRUCT_LIST;
- } else if (element_->IsStructList()) {
- // If the top element is a STRUCT_LIST, this means we are starting a list
- // within the current list (inside a struct).
- // An example call sequence would be
- // StartObject -> StartList -> StartList
- // with StartObject starting a struct.
+ // Report an error.
+ InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
+ "') within a map."));
+ return this;
+ }
- // Lookup the last list type in element stack as we are adding an element of
- // the same type.
- field = Lookup(name);
- if (field == NULL) return this;
+ // When name is empty and stack is not empty, we are rendering an item within
+ // a list.
+ if (name.empty()) {
+ if (element() != NULL && element()->parent_field() != NULL) {
+ if (IsStructValue(*element()->parent_field())) {
+ // Since it is g.p.Value, we bind directly to the list_value.
+ // Render
+ // { // g.p.Value item within the list
+ // "list_value": {
+ // "values": [
+ Push("", Item::MESSAGE, false, false);
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
- field = StartListValueInStruct(field);
- if (field == NULL) return this;
+ if (IsStructListValue(*element()->parent_field())) {
+ // Since it is g.p.ListValue, we bind to it directly.
+ // Render
+ // { // g.p.ListValue item within the list
+ // "values": [
+ Push("", Item::MESSAGE, false, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
+ }
- element_type = ProtoElement::STRUCT_LIST;
- } else {
- // Lookup field corresponding to 'name'. If it is a google.protobuf.Value
- // or google.protobuf.ListValue type, then StartList is a valid call, start
- // this list.
- // We cannot use Lookup() here as it will produce InvalidName() error if the
- // field is not found. We do not want to error here as it would cause us to
- // report errors twice, once here and again later in BeginNamed() call.
- // Also we ignore if the field is not found here as it is caught later.
- field = typeinfo_->FindField(&element_->type(), name);
-
- // Only check for oneof collisions on the first StartList call. We identify
- // the first call with !name.empty() check. Subsequent list element calls
- // will not have the name filled.
- if (!name.empty() && field && !ValidOneof(*field, name)) {
- ++invalid_depth_;
+ // Pass the event to underlying ProtoWriter.
+ Push(name, Item::MESSAGE, false, true);
+ return this;
+ }
+
+ // name is not empty
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) {
+ IncrementInvalidDepth();
+ return this;
+ }
+
+ if (IsStructValue(*field)) {
+ // If g.p.Value is repeated, start that list. Otherwise, start the
+ // "list_value" within it.
+ if (IsRepeated(*field)) {
+ // Render it just like a regular repeated field.
+ // "<name>": [
+ Push(name, Item::MESSAGE, false, true);
return this;
}
- // It is an error to try to bind to map, which behind the scenes is a list.
- if (field && IsMap(*field)) {
- // Push field to stack for error location tracking & reporting.
- element_.reset(new ProtoElement(element_.release(), field,
- *LookupType(field),
- ProtoElement::MESSAGE));
- InvalidValue("Map", "Cannot bind a list to map.");
- ++invalid_depth_;
- element_.reset(element_->pop());
+ // Start the "list_value" field.
+ // Render
+ // "<name>": {
+ // "list_value": {
+ // "values": [
+ Push(name, Item::MESSAGE, false, false);
+ Push("list_value", Item::MESSAGE, true, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
+ }
+
+ if (IsStructListValue(*field)) {
+ // If g.p.ListValue is repeated, start that list. Otherwise, start the
+ // "values" within it.
+ if (IsRepeated(*field)) {
+ // Render it just like a regular repeated field.
+ // "<name>": [
+ Push(name, Item::MESSAGE, false, true);
return this;
}
- if (field && field->type_url() == GetFullTypeWithUrl(kStructValueType)) {
- // There are 2 cases possible:
- // a. g.p.Value is repeated
- // b. g.p.Value is not repeated
- //
- // For case (a), the StartList should bind to the repeated g.p.Value.
- // For case (b), the StartList should bind to g.p.ListValue within the
- // g.p.Value.
- //
- // This means, for case (a), we treat it just like any other repeated
- // message, except we would apply an appropriate element_type so future
- // Start or Render calls are routed appropriately.
- if (field->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- field = StartListValueInStruct(field);
- }
- element_type = ProtoElement::STRUCT_LIST;
- } else if (field &&
- field->type_url() == GetFullTypeWithUrl(kStructListValueType)) {
- // We got a StartList with google.protobuf.ListValue master type. This
- // means we have to start the "values" within google.protobuf.ListValue.
- field = StartRepeatedValuesInListValue(field);
- } else {
- // If no special types are to be bound, fall back to normal processing of
- // StartList.
- field = BeginNamed(name, true);
- }
- if (field == NULL) return this;
+ // Start the "values" field within g.p.ListValue.
+ // Render
+ // "<name>": {
+ // "values": [
+ Push(name, Item::MESSAGE, false, false);
+ Push("values", Item::MESSAGE, true, true);
+ return this;
}
- const google::protobuf::Type* type = LookupType(field);
- if (type == NULL) {
- ++invalid_depth_;
- InvalidName(name,
- StrCat("Missing descriptor for field: ", field->type_url()));
+ // If we are here, the field should be repeated. Report an error otherwise.
+ if (!IsRepeated(*field)) {
+ IncrementInvalidDepth();
+ InvalidName(name, "Proto field is not repeating, cannot start list.");
return this;
}
- element_.reset(
- new ProtoElement(element_.release(), field, *type, element_type));
+ if (IsMap(*field)) {
+ InvalidValue("Map",
+ StrCat("Cannot bind a list to map for field '", name, "'."));
+ IncrementInvalidDepth();
+ return this;
+ }
+
+ // Pass the event to ProtoWriter.
+ // Render
+ // "<name>": [
+ Push(name, Item::MESSAGE, false, true);
return this;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
- if (invalid_depth_ > 0) {
- --invalid_depth_;
- } else if (element_ != NULL) {
- if (element_->IsAny()) {
- element_->any()->EndList();
- } else {
- element_.reset(element_->pop());
- // Skip sentinel elements added to keep track of new proto3 types - map,
- // struct.
- SkipElements();
- }
+ if (invalid_depth() > 0) {
+ DecrementInvalidDepth();
+ return this;
}
- // When element_ is NULL, we have reached the root message type. Write out
- // the bytes.
- if (element_ == NULL) {
- WriteRootMessage();
+ if (current_ == NULL) return this;
+
+ if (current_->IsAny()) {
+ current_->any()->EndList();
+ return this;
}
+
+ Pop();
return this;
}
@@ -1083,7 +782,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
"null values are supported.");
}
}
- ow->RenderDataPiece(struct_field_name, data);
+ ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
return Status::OK;
}
@@ -1105,15 +804,15 @@ Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
}
- ow->RenderDataPiece("seconds", DataPiece(seconds));
- ow->RenderDataPiece("nanos", DataPiece(nanos));
+ ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
+ ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
return Status::OK;
}
static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
StringPiece path) {
- ow->RenderDataPiece("paths",
- DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase)));
+ ow->ProtoWriter::RenderDataPiece(
+ "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase)));
return Status::OK;
}
@@ -1162,224 +861,133 @@ Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
"Invalid duration format, failed to parse seconds");
}
- double d_nanos = 0;
- if (!safe_strtod("0." + s_nanos.ToString(), &d_nanos)) {
- return Status(INVALID_ARGUMENT,
- "Invalid duration format, failed to parse nanos seconds");
+ int32 nanos = 0;
+ Status nanos_status = GetNanosFromStringPiece(
+ s_nanos, "Invalid duration format, failed to parse nano seconds",
+ "Duration value exceeds limits", &nanos);
+ if (!nanos_status.ok()) {
+ return nanos_status;
}
+ nanos = sign * nanos;
- int32 nanos = sign * static_cast<int32>(d_nanos * kNanosPerSecond);
int64 seconds = sign * unsigned_seconds;
-
if (seconds > kMaxSeconds || seconds < kMinSeconds ||
nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
return Status(INVALID_ARGUMENT, "Duration value exceeds limits");
}
- ow->RenderDataPiece("seconds", DataPiece(seconds));
- ow->RenderDataPiece("nanos", DataPiece(nanos));
+ ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
+ ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
return Status::OK;
}
Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
const DataPiece& data) {
- ow->RenderDataPiece("value", data);
+ ow->ProtoWriter::RenderDataPiece("value", data);
return Status::OK;
}
ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
StringPiece name, const DataPiece& data) {
Status status;
- if (invalid_depth_ > 0) return this;
- if (element_ != NULL && element_->IsAny()) {
- element_->any()->RenderDataPiece(name, data);
+ if (invalid_depth() > 0) return this;
+
+ if (current_ == NULL) {
+ const TypeRenderer* type_renderer =
+ FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
+ if (type_renderer == NULL) {
+ InvalidName(name, "Root element must be a message.");
+ return this;
+ }
+ // Render the special type.
+ // "<name>": {
+ // ... Render special type ...
+ // }
+ ProtoWriter::StartObject(name);
+ status = (*type_renderer)(this, data);
+ if (!status.ok()) {
+ InvalidValue(master_type_.name(),
+ StrCat("Field '", name, "', ", status.error_message()));
+ }
+ ProtoWriter::EndObject();
+ return this;
+ }
+
+ if (current_->IsAny()) {
+ current_->any()->RenderDataPiece(name, data);
return this;
}
const google::protobuf::Field* field = NULL;
- string type_url;
- bool is_map_entry = false;
- // We are at the root when element_ == NULL.
- if (element_ == NULL) {
- type_url = GetFullTypeWithUrl(master_type_.name());
- } else {
- if (element_->IsMap() || element_->IsStructMap()) {
- if (!ValidMapKey(name)) return this;
- is_map_entry = true;
- field = StartMapEntry(name);
- } else {
- field = Lookup(name);
- }
+ if (current_->IsMap()) {
+ if (!ValidMapKey(name)) return this;
+
+ // Render an item in repeated map list.
+ // { "key": "<name>", "value":
+ Push("", Item::MESSAGE, false, false);
+ ProtoWriter::RenderDataPiece("key", DataPiece(name));
+ field = Lookup("value");
if (field == NULL) {
+ GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
+ return this;
+ }
+
+ const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
+ if (type_renderer != NULL) {
+ // Map's value type is a special type. Render it like a message:
+ // "value": {
+ // ... Render special type ...
+ // }
+ Push("value", Item::MESSAGE, true, false);
+ status = (*type_renderer)(this, data);
+ if (!status.ok()) {
+ InvalidValue(field->type_url(),
+ StrCat("Field '", name, "', ", status.error_message()));
+ }
+ Pop();
return this;
}
- // Check to see if this field is a oneof and that no oneof in that group has
- // already been set.
- if (!ValidOneof(*field, name)) return this;
+ // If we are rendering explicit null values and the backend proto field is
+ // not of the google.protobuf.NullType type, we do nothing.
+ if (data.type() == DataPiece::TYPE_NULL &&
+ field->type_url() != kStructNullValueTypeUrl) {
+ return this;
+ }
- type_url = field->type_url();
+ // Render the map value as a primitive type.
+ ProtoWriter::RenderDataPiece("value", data);
+ Pop();
+ return this;
}
- // Check if there are any well known type renderers available for type_url.
- const TypeRenderer* type_renderer = FindTypeRenderer(type_url);
+ field = Lookup(name);
+ if (field == NULL) return this;
+
+ // Check if the field is of special type. Render it accordingly if so.
+ const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
if (type_renderer != NULL) {
- // Push the current element to stack so lookups in type_renderer will
- // find the fields. We do an EndObject soon after, which pops this. This is
- // safe because all well-known types are messages.
- if (element_ == NULL) {
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
- } else {
- if (field) {
- WriteTag(*field);
- const google::protobuf::Type* type = LookupType(field);
- element_.reset(new ProtoElement(element_.release(), field, *type,
- ProtoElement::MESSAGE));
- }
- }
+ Push(name, Item::MESSAGE, false, false);
status = (*type_renderer)(this, data);
if (!status.ok()) {
- InvalidValue(type_url,
+ InvalidValue(field->type_url(),
StrCat("Field '", name, "', ", status.error_message()));
}
- EndObject();
- return this;
- } else if (element_ == NULL) { // no message type found at root
- element_.reset(new ProtoElement(typeinfo_, master_type_, this));
- InvalidName(name, "Root element must be a message.");
+ Pop();
return this;
}
- if (field == NULL) {
- return this;
- }
- const google::protobuf::Type* type = LookupType(field);
- if (type == NULL) {
- InvalidName(name,
- StrCat("Missing descriptor for field: ", field->type_url()));
+ // If we are rendering explicit null values and the backend proto field is
+ // not of the google.protobuf.NullType type, we do nothing.
+ if (data.type() == DataPiece::TYPE_NULL &&
+ field->type_url() != kStructNullValueTypeUrl) {
return this;
}
- // Whether we should pop at the end. Set to true if the data field is a
- // message type, which can happen in case of struct values.
- bool should_pop = false;
-
- RenderSimpleDataPiece(*field, *type, data);
-
- if (should_pop && element_ != NULL) {
- element_.reset(element_->pop());
- }
-
- if (is_map_entry) {
- // Ending map is the same as ending an object.
- EndObject();
- }
+ ProtoWriter::RenderDataPiece(name, data);
return this;
}
-void ProtoStreamObjectWriter::RenderSimpleDataPiece(
- const google::protobuf::Field& field, const google::protobuf::Type& type,
- const DataPiece& data) {
- // If we are rendering explicit null values and the backend proto field is not
- // of the google.protobuf.NullType type, we do nothing.
- if (data.type() == DataPiece::TYPE_NULL &&
- field.type_url() != kStructNullValueTypeUrl) {
- return;
- }
-
- // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
- // error location reporting and required field accounting.
- element_.reset(new ProtoElement(element_.release(), &field, type,
- ProtoElement::MESSAGE));
-
- // Make sure that field represents a simple data type.
- if (field.kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN ||
- field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
- InvalidValue(field.type_url().empty()
- ? google::protobuf::Field_Kind_Name(field.kind())
- : field.type_url(),
- data.ValueAsStringOrDefault(""));
- return;
- }
-
- Status status;
- switch (field.kind()) {
- case google::protobuf::Field_Kind_TYPE_INT32: {
- status = WriteInt32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SFIXED32: {
- status = WriteSFixed32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SINT32: {
- status = WriteSInt32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_FIXED32: {
- status = WriteFixed32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_UINT32: {
- status = WriteUInt32(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_INT64: {
- status = WriteInt64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SFIXED64: {
- status = WriteSFixed64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_SINT64: {
- status = WriteSInt64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_FIXED64: {
- status = WriteFixed64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_UINT64: {
- status = WriteUInt64(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_DOUBLE: {
- status = WriteDouble(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_FLOAT: {
- status = WriteFloat(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_BOOL: {
- status = WriteBool(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_BYTES: {
- status = WriteBytes(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_STRING: {
- status = WriteString(field.number(), data, stream_.get());
- break;
- }
- case google::protobuf::Field_Kind_TYPE_ENUM: {
- status = WriteEnum(field.number(), data,
- typeinfo_->GetEnumByTypeUrl(field.type_url()),
- stream_.get());
- break;
- }
- default: // TYPE_GROUP or TYPE_MESSAGE
- status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie());
- }
- if (!status.ok()) {
- InvalidValue(google::protobuf::Field_Kind_Name(field.kind()),
- status.error_message());
- }
- element_.reset(element_->pop());
-}
-
// Map of functions that are responsible for rendering well known type
// represented by the key.
hash_map<string, ProtoStreamObjectWriter::TypeRenderer>*
@@ -1446,45 +1054,12 @@ ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) {
return FindOrNull(*renderers_, type_url);
}
-ProtoStreamObjectWriter::ProtoElement::ElementType
-ProtoStreamObjectWriter::GetElementType(const google::protobuf::Type& type) {
- if (type.name() == kAnyType) {
- return ProtoElement::ANY;
- } else if (type.name() == kStructType) {
- return ProtoElement::STRUCT;
- } else if (type.name() == kStructValueType) {
- return ProtoElement::STRUCT_VALUE;
- } else if (type.name() == kStructListValueType) {
- return ProtoElement::STRUCT_LIST_VALUE;
- } else {
- return ProtoElement::MESSAGE;
- }
-}
-
-bool ProtoStreamObjectWriter::ValidOneof(const google::protobuf::Field& field,
- StringPiece unnormalized_name) {
- if (element_ == NULL) return true;
-
- if (field.oneof_index() > 0) {
- if (element_->OneofIndexTaken(field.oneof_index())) {
- InvalidValue(
- "oneof",
- StrCat("oneof field '",
- element_->type().oneofs(field.oneof_index() - 1),
- "' is already set. Cannot set '", unnormalized_name, "'"));
- return false;
- }
- element_->TakeOneofIndex(field.oneof_index());
- }
- return true;
-}
-
bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
- if (element_ == NULL) return true;
+ if (current_ == NULL) return true;
- if (!element_->InsertMapKeyIfNotPresent(unnormalized_name)) {
- InvalidName(
- unnormalized_name,
+ if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
+ listener()->InvalidName(
+ location(), unnormalized_name,
StrCat("Repeated map key: '", unnormalized_name, "' is already set."));
return false;
}
@@ -1492,134 +1067,30 @@ bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
return true;
}
-const google::protobuf::Field* ProtoStreamObjectWriter::BeginNamed(
- StringPiece name, bool is_list) {
- if (invalid_depth_ > 0) {
- ++invalid_depth_;
- return NULL;
- }
- const google::protobuf::Field* field = Lookup(name);
- if (field == NULL) {
- ++invalid_depth_;
- // InvalidName() already called in Lookup().
- return NULL;
- }
- if (is_list &&
- field->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- ++invalid_depth_;
- InvalidName(name, "Proto field is not repeating, cannot start list.");
- return NULL;
- }
- return field;
-}
+void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type,
+ bool is_placeholder, bool is_list) {
+ is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
-const google::protobuf::Field* ProtoStreamObjectWriter::Lookup(
- StringPiece unnormalized_name) {
- ProtoElement* e = element();
- if (e == NULL) {
- InvalidName(unnormalized_name, "Root element must be a message.");
- return NULL;
- }
- if (unnormalized_name.empty()) {
- // Objects in repeated field inherit the same field descriptor.
- if (e->field() == NULL) {
- InvalidName(unnormalized_name, "Proto fields must have a name.");
- } else if (e->field()->cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- InvalidName(unnormalized_name, "Proto fields must have a name.");
- return NULL;
- }
- return e->field();
- }
- const google::protobuf::Field* field =
- typeinfo_->FindField(&e->type(), unnormalized_name);
- if (field == NULL) InvalidName(unnormalized_name, "Cannot find field.");
- return field;
-}
-
-const google::protobuf::Type* ProtoStreamObjectWriter::LookupType(
- const google::protobuf::Field* field) {
- return (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE
- ? typeinfo_->GetTypeByTypeUrl(field->type_url())
- : &element_->type());
+ // invalid_depth == 0 means it is a successful StartObject or StartList.
+ if (invalid_depth() == 0)
+ current_.reset(
+ new Item(current_.release(), item_type, is_placeholder, is_list));
}
-// Looks up the oneof struct field based on the data type.
-StatusOr<const google::protobuf::Field*>
-ProtoStreamObjectWriter::LookupStructField(DataPiece::Type type) {
- const google::protobuf::Field* field = NULL;
- switch (type) {
- // Our JSON parser parses numbers as either int64, uint64, or double.
- case DataPiece::TYPE_INT64:
- case DataPiece::TYPE_UINT64:
- case DataPiece::TYPE_DOUBLE: {
- field = Lookup("number_value");
- break;
- }
- case DataPiece::TYPE_STRING: {
- field = Lookup("string_value");
- break;
- }
- case DataPiece::TYPE_BOOL: {
- field = Lookup("bool_value");
- break;
- }
- case DataPiece::TYPE_NULL: {
- field = Lookup("null_value");
- break;
- }
- default: { return Status(INVALID_ARGUMENT, "Invalid struct data type"); }
+void ProtoStreamObjectWriter::Pop() {
+ // Pop all placeholder items sending StartObject or StartList events to
+ // ProtoWriter according to is_list value.
+ while (current_ != NULL && current_->is_placeholder()) {
+ PopOneElement();
}
- if (field == NULL) {
- return Status(INVALID_ARGUMENT, "Could not lookup struct field");
+ if (current_ != NULL) {
+ PopOneElement();
}
- return field;
}
-void ProtoStreamObjectWriter::WriteRootMessage() {
- GOOGLE_DCHECK(!done_);
- int curr_pos = 0;
- // Calls the destructor of CodedOutputStream to remove any uninitialized
- // memory from the Cord before we read it.
- stream_.reset(NULL);
- const void* data;
- int length;
- google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
- while (input_stream.Next(&data, &length)) {
- if (length == 0) continue;
- int num_bytes = length;
- // Write up to where we need to insert the size field.
- // The number of bytes we may write is the smaller of:
- // - the current fragment size
- // - the distance to the next position where a size field needs to be
- // inserted.
- if (!size_insert_.empty() &&
- size_insert_.front().pos - curr_pos < num_bytes) {
- num_bytes = size_insert_.front().pos - curr_pos;
- }
- output_->Append(static_cast<const char*>(data), num_bytes);
- if (num_bytes < length) {
- input_stream.BackUp(length - num_bytes);
- }
- curr_pos += num_bytes;
- // Insert the size field.
- // size_insert_.front(): the next <index, size> pair to be written.
- // size_insert_.front().pos: position of the size field.
- // size_insert_.front().size: the size (integer) to be inserted.
- if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
- // Varint32 occupies at most 10 bytes.
- uint8 insert_buffer[10];
- uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
- size_insert_.front().size, insert_buffer);
- output_->Append(reinterpret_cast<const char*>(insert_buffer),
- insert_buffer_pos - insert_buffer);
- size_insert_.pop_front();
- }
- }
- output_->Flush();
- stream_.reset(new CodedOutputStream(&adapter_));
- done_ = true;
+void ProtoStreamObjectWriter::PopOneElement() {
+ current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
+ current_.reset(current_->pop<Item>());
}
bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
@@ -1630,7 +1101,7 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
return false;
}
const google::protobuf::Type* field_type =
- typeinfo_->GetTypeByTypeUrl(field.type_url());
+ typeinfo()->GetTypeByTypeUrl(field.type_url());
// TODO(xiaofeng): Unify option names.
return GetBoolOptionOrDefault(field_type->options(),
@@ -1638,12 +1109,23 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
GetBoolOptionOrDefault(field_type->options(), "map_entry", false);
}
-void ProtoStreamObjectWriter::WriteTag(const google::protobuf::Field& field) {
- WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
- static_cast<WireFormatLite::FieldType>(field.kind()));
- stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
+bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kAnyType;
}
+bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kStructType;
+}
+
+bool ProtoStreamObjectWriter::IsStructValue(
+ const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
+}
+
+bool ProtoStreamObjectWriter::IsStructListValue(
+ const google::protobuf::Field& field) {
+ return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
+}
} // namespace converter
} // namespace util
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h
index 6e133679..08ac6e33 100644
--- a/src/google/protobuf/util/internal/protostream_objectwriter.h
+++ b/src/google/protobuf/util/internal/protostream_objectwriter.h
@@ -42,6 +42,7 @@
#include <google/protobuf/util/internal/type_info.h>
#include <google/protobuf/util/internal/datapiece.h>
#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/proto_writer.h>
#include <google/protobuf/util/internal/structured_objectwriter.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/stubs/bytestream.h>
@@ -67,9 +68,11 @@ namespace converter {
class ObjectLocationTracker;
// An ObjectWriter that can write protobuf bytes directly from writer events.
+// This class supports all special types like Struct and Map. It uses
+// the ProtoWriter class to write raw proto bytes.
//
// It also supports streaming.
-class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter {
+class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter {
public:
// Constructor. Does not take ownership of any parameter passed in.
ProtoStreamObjectWriter(TypeResolver* type_resolver,
@@ -82,56 +85,13 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
virtual ProtoStreamObjectWriter* EndObject();
virtual ProtoStreamObjectWriter* StartList(StringPiece name);
virtual ProtoStreamObjectWriter* EndList();
- virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, bool value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, int32 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name,
- uint32 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, int64 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name,
- uint64 value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderDouble(StringPiece name,
- double value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, float value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderString(StringPiece name,
- StringPiece value) {
- return RenderDataPiece(name, DataPiece(value));
- }
- virtual ProtoStreamObjectWriter* RenderBytes(StringPiece name,
- StringPiece value) {
- return RenderDataPiece(name, DataPiece(value, false));
- }
- virtual ProtoStreamObjectWriter* RenderNull(StringPiece name) {
- return RenderDataPiece(name, DataPiece::NullData());
- }
// Renders a DataPiece 'value' into a field whose wire type is determined
// from the given field 'name'.
- ProtoStreamObjectWriter* RenderDataPiece(StringPiece name,
- const DataPiece& value);
+ virtual ProtoStreamObjectWriter* RenderDataPiece(StringPiece name,
+ const DataPiece& value);
- // Returns the location tracker to use for tracking locations for errors.
- const LocationTrackerInterface& location() {
- return element_ != NULL ? *element_ : *tracker_;
- }
-
- // When true, we finished writing to output a complete message.
- bool done() const { return done_; }
-
- private:
+ protected:
// Function that renders a well known type with modified behavior.
typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*,
const DataPiece&);
@@ -192,73 +152,37 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
bool has_injected_value_message_;
};
- class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface {
+ // Represents an item in a stack of items used to keep state between
+ // ObjectWrier events.
+ class LIBPROTOBUF_EXPORT Item : public BaseElement {
public:
- // Indicates the type of element. Special types like LIST, MAP, MAP_ENTRY,
- // STRUCT etc. are used to deduce other information based on their position
- // on the stack of elements.
- enum ElementType {
- MESSAGE, // Simple message
- LIST, // List/repeated element
- MAP, // Proto3 map type
- MAP_ENTRY, // Proto3 map message type, with 'key' and 'value' fields
- ANY, // Proto3 Any type
- STRUCT, // Proto3 struct type
- STRUCT_VALUE, // Struct's Value message type
- STRUCT_LIST, // List type indicator within a struct
- STRUCT_LIST_VALUE, // Struct Value's ListValue message type
- STRUCT_MAP, // Struct within a struct type
- STRUCT_MAP_ENTRY // Struct map's entry type with 'key' and 'value'
- // fields
+ // Indicates the type of item.
+ enum ItemType {
+ MESSAGE, // Simple message
+ MAP, // Proto3 map type
+ ANY, // Proto3 Any type
};
- // Constructor for the root element. No parent nor field.
- ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type,
- ProtoStreamObjectWriter* enclosing);
-
- // Constructor for a field of an element.
- ProtoElement(ProtoElement* parent, const google::protobuf::Field* field,
- const google::protobuf::Type& type, ElementType element_type);
+ // Constructor for the root item.
+ Item(ProtoStreamObjectWriter* enclosing, ItemType item_type,
+ bool is_placeholder, bool is_list);
- virtual ~ProtoElement() {}
+ // Constructor for a field of a message.
+ Item(Item* parent, ItemType item_type, bool is_placeholder, bool is_list);
- // Called just before the destructor for clean up:
- // - reports any missing required fields
- // - computes the space needed by the size field, and augment the
- // length of all parent messages by this additional space.
- // - releases and returns the parent pointer.
- ProtoElement* pop();
-
- // Accessors
- const google::protobuf::Field* field() const { return field_; }
- const google::protobuf::Type& type() const { return type_; }
+ virtual ~Item() {}
// These functions return true if the element type is corresponding to the
// type in function name.
- bool IsMap() { return element_type_ == MAP; }
- bool IsStructMap() { return element_type_ == STRUCT_MAP; }
- bool IsStructMapEntry() { return element_type_ == STRUCT_MAP_ENTRY; }
- bool IsStructList() { return element_type_ == STRUCT_LIST; }
- bool IsAny() { return element_type_ == ANY; }
-
- ElementType element_type() { return element_type_; }
-
- void RegisterField(const google::protobuf::Field* field);
- virtual string ToString() const;
+ bool IsMap() { return item_type_ == MAP; }
+ bool IsAny() { return item_type_ == ANY; }
AnyWriter* any() const { return any_.get(); }
- virtual ProtoElement* parent() const {
- return static_cast<ProtoElement*>(BaseElement::parent());
+ virtual Item* parent() const {
+ return static_cast<Item*>(BaseElement::parent());
}
- // Returns true if the index is already taken by a preceeding oneof input.
- bool OneofIndexTaken(int32 index);
-
- // Marks the oneof 'index' as taken. Future inputs to this oneof will
- // generate an error.
- void TakeOneofIndex(int32 index);
-
// Inserts map key into hash set if and only if the key did NOT already
// exist in hash set.
// The hash set (map_keys_) is ONLY used to keep track of map keys.
@@ -266,6 +190,9 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
// already present.
bool InsertMapKeyIfNotPresent(StringPiece map_key);
+ bool is_placeholder() const { return is_placeholder_; }
+ bool is_list() const { return is_list_; }
+
private:
// Used for access to variables of the enclosing instance of
// ProtoStreamObjectWriter.
@@ -274,126 +201,42 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
// A writer for Any objects, handles all Any-related nonsense.
google::protobuf::scoped_ptr<AnyWriter> any_;
- // Describes the element as a field in the parent message.
- // field_ is NULL if and only if this element is the root element.
- const google::protobuf::Field* field_;
-
- // TypeInfo to lookup types.
- const TypeInfo* typeinfo_;
-
- // Additional variables if this element is a message:
- // (Root element is always a message).
- // descriptor_ : describes allowed fields in the message.
- // required_fields_: set of required fields.
- // is_repeated_type_ : true if the element is of type list or map.
- // size_index_ : index into ProtoStreamObjectWriter::size_insert_
- // for later insertion of serialized message length.
- const google::protobuf::Type& type_;
- std::set<const google::protobuf::Field*> required_fields_;
- const bool is_repeated_type_;
- const int size_index_;
-
- // Tracks position in repeated fields, needed for LocationTrackerInterface.
- int array_index_;
-
// The type of this element, see enum for permissible types.
- ElementType element_type_;
-
- // Set of oneof indices already seen for the type_. Used to validate
- // incoming messages so no more than one oneof is set.
- hash_set<int32> oneof_indices_;
+ ItemType item_type_;
// Set of map keys already seen for the type_. Used to validate incoming
// messages so no map key appears more than once.
hash_set<string> map_keys_;
- GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement);
- };
+ // Conveys whether this Item is a placeholder or not. Placeholder items are
+ // pushed to stack to account for special types.
+ bool is_placeholder_;
- // Container for inserting 'size' information at the 'pos' position.
- struct SizeInfo {
- const int pos;
- int size;
+ // Conveys whether this Item is a list or not. This is used to send
+ // StartList or EndList calls to underlying ObjectWriter.
+ bool is_list_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Item);
};
ProtoStreamObjectWriter(const TypeInfo* typeinfo,
const google::protobuf::Type& type,
strings::ByteSink* output, ErrorListener* listener);
- ProtoElement* element() { return element_.get(); }
-
- // Helper methods for calling ErrorListener. See error_listener.h.
- void InvalidName(StringPiece unknown_name, StringPiece message);
- void InvalidValue(StringPiece type_name, StringPiece value);
- void MissingField(StringPiece missing_name);
-
- // Common code for BeginObject() and BeginList() that does invalid_depth_
- // bookkeeping associated with name lookup.
- const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list);
-
- // Lookup the field in the current element. Looks in the base descriptor
- // and in any extension. This will report an error if the field cannot be
- // found or if multiple matching extensions are found.
- const google::protobuf::Field* Lookup(StringPiece name);
-
- // Lookup the field type in the type descriptor. Returns NULL if the type
- // is not known.
- const google::protobuf::Type* LookupType(
- const google::protobuf::Field* field);
-
- // Looks up the oneof struct Value field depending on the type.
- // On failure to find, it returns an appropriate error.
- util::StatusOr<const google::protobuf::Field*> LookupStructField(
- DataPiece::Type type);
-
- // Starts an entry in map. This will be called after placing map element at
- // the top of the stack. Uses this information to write map entries.
- const google::protobuf::Field* StartMapEntry(StringPiece name);
-
- // Starts a google.protobuf.Struct.
- // 'field' is of type google.protobuf.Struct.
- // If field is NULL, it indicates that the top-level message is a struct
- // type.
- void StartStruct(const google::protobuf::Field* field);
-
- // Starts another struct within a struct.
- // 'field' is of type google.protobuf.Value (see struct.proto).
- const google::protobuf::Field* StartStructValueInStruct(
- const google::protobuf::Field* field);
-
- // Starts a list within a struct.
- // 'field' is of type google.protobuf.ListValue (see struct.proto).
- const google::protobuf::Field* StartListValueInStruct(
- const google::protobuf::Field* field);
-
- // Starts the repeated "values" field in struct.proto's
- // google.protobuf.ListValue type. 'field' should be of type
- // google.protobuf.ListValue.
- const google::protobuf::Field* StartRepeatedValuesInListValue(
- const google::protobuf::Field* field);
-
- // Pops sentinel elements off the stack.
- void SkipElements();
-
- // Write serialized output to the final output ByteSink, inserting all
- // the size information for nested messages that are missing from the
- // intermediate Cord buffer.
- void WriteRootMessage();
-
// Returns true if the field is a map.
bool IsMap(const google::protobuf::Field& field);
// Returns true if the field is an any.
bool IsAny(const google::protobuf::Field& field);
- // Helper method to write proto tags based on the given field.
- void WriteTag(const google::protobuf::Field& field);
+ // Returns true if the field is google.protobuf.Struct.
+ bool IsStruct(const google::protobuf::Field& field);
+ // Returns true if the field is google.protobuf.Value.
+ bool IsStructValue(const google::protobuf::Field& field);
- // Helper function to render primitive data types in DataPiece.
- void RenderSimpleDataPiece(const google::protobuf::Field& field,
- const google::protobuf::Type& type,
- const DataPiece& data);
+ // Returns true if the field is google.protobuf.ListValue.
+ bool IsStructListValue(const google::protobuf::Field& field);
// Renders google.protobuf.Value in struct.proto. It picks the right oneof
// type based on value's type.
@@ -417,69 +260,46 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter
static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow,
const DataPiece& value);
- // Helper functions to create the map and find functions responsible for
- // rendering well known types, keyed by type URL.
- static hash_map<string, TypeRenderer>* renderers_;
static void InitRendererMap();
static void DeleteRendererMap();
static TypeRenderer* FindTypeRenderer(const string& type_url);
- // Returns the ProtoElement::ElementType for the given Type.
- static ProtoElement::ElementType GetElementType(
- const google::protobuf::Type& type);
-
- // Returns true if the field for type_ can be set as a oneof. If field is not
- // a oneof type, this function does nothing and returns true.
- // If another field for this oneof is already set, this function returns
- // false. It also calls the appropriate error callback.
- // unnormalized_name is used for error string.
- bool ValidOneof(const google::protobuf::Field& field,
- StringPiece unnormalized_name);
-
// Returns true if the map key for type_ is not duplicated key.
// If map key is duplicated key, this function returns false.
- // Note that caller should make sure that the current proto element (element_)
+ // Note that caller should make sure that the current proto element (current_)
// is of element type MAP or STRUCT_MAP.
// It also calls the appropriate error callback and unnormalzied_name is used
// for error string.
bool ValidMapKey(StringPiece unnormalized_name);
+ // Pushes an item on to the stack. Also calls either StartObject or StartList
+ // on the underlying ObjectWriter depending on whether is_list is false or
+ // not.
+ // is_placeholder conveys whether the item is a placeholder item or not.
+ // Placeholder items are pushed when adding auxillary types' StartObject or
+ // StartList calls.
+ void Push(StringPiece name, Item::ItemType item_type, bool is_placeholder,
+ bool is_list);
+
+ // Pops items from the stack. All placeholder items are popped until a
+ // non-placeholder item is found.
+ void Pop();
+
+ // Pops one element from the stack. Calls EndObject() or EndList() on the
+ // underlying ObjectWriter depending on the value of is_list_.
+ void PopOneElement();
+
+ private:
+ // Helper functions to create the map and find functions responsible for
+ // rendering well known types, keyed by type URL.
+ static hash_map<string, TypeRenderer>* renderers_;
+
// Variables for describing the structure of the input tree:
// master_type_: descriptor for the whole protobuf message.
- // typeinfo_ : the TypeInfo object to lookup types.
const google::protobuf::Type& master_type_;
- const TypeInfo* typeinfo_;
- // Whether we own the typeinfo_ object.
- bool own_typeinfo_;
-
- // Indicates whether we finished writing root message completely.
- bool done_;
-
- // Variable for internal state processing:
- // element_ : the current element.
- // size_insert_: sizes of nested messages.
- // pos - position to insert the size field.
- // size - size value to be inserted.
- google::protobuf::scoped_ptr<ProtoElement> element_;
- std::deque<SizeInfo> size_insert_;
-
- // Variables for output generation:
- // output_ : pointer to an external ByteSink for final user-visible output.
- // buffer_ : buffer holding partial message before being ready for output_.
- // adapter_ : internal adapter between CodedOutputStream and Cord buffer_.
- // stream_ : wrapper for writing tags and other encodings in wire format.
- strings::ByteSink* output_;
- string buffer_;
- google::protobuf::io::StringOutputStream adapter_;
- google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_;
-
- // Variables for error tracking and reporting:
- // listener_ : a place to report any errors found.
- // invalid_depth_: number of enclosing invalid nested messages.
- // tracker_ : the root location tracker interface.
- ErrorListener* listener_;
- int invalid_depth_;
- google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_;
+
+ // The current element, variable for internal state processing.
+ google::protobuf::scoped_ptr<Item> current_;
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter);
};
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
index cb9f97f7..5f9ffb95 100644
--- a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
+++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
@@ -241,6 +241,21 @@ TEST_P(ProtoStreamObjectWriterTest, SimpleMessage) {
CheckOutput(book);
}
+TEST_P(ProtoStreamObjectWriterTest, CustomJsonName) {
+ Book book;
+ Author* robert = book.mutable_author();
+ robert->set_id(12345);
+ robert->set_name("robert");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderUint64("@id", 12345)
+ ->RenderString("name", "robert")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(book);
+}
+
TEST_P(ProtoStreamObjectWriterTest, PrimitiveFromStringConversion) {
Primitive full;
full.set_fix32(101);
@@ -796,10 +811,6 @@ TEST_P(ProtoStreamObjectWriterTest, RootNamedList) {
InvalidName(_, StringPiece("oops"),
StringPiece("Root element should not be named.")))
.With(Args<0>(HasObjectLocation("")));
- EXPECT_CALL(listener_,
- InvalidName(_, StringPiece(""),
- StringPiece("Proto fields must have a name.")))
- .With(Args<0>(HasObjectLocation("")));
ow_->StartList("oops")->RenderString("", "item")->EndList();
CheckOutput(empty, 0);
}
@@ -864,6 +875,18 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
::testing::Values(
testing::USE_TYPE_RESOLVER));
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseTimestamp) {
+ TimestampDuration timestamp;
+ google::protobuf::Timestamp* ts = timestamp.mutable_ts();
+ ts->set_seconds(1448249855);
+ ts->set_nanos(33155000);
+
+ ow_->StartObject("")
+ ->RenderString("ts", "2015-11-23T03:37:35.033155Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) {
TimestampDuration timestamp;
@@ -922,9 +945,66 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError4) {
CheckOutput(timestamp);
}
-// TODO(skarvaje): Write a test for nanos that exceed limit. Currently, it is
-// not possible to construct a test case where nanos exceed limit because of
-// floating point arithmetic.
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError5) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Invalid time format: "
+ "2015-11-23T03:37:35.033155 Z")));
+
+ ow_->StartObject("")
+ // Whitespace in the Timestamp nanos is not allowed.
+ ->RenderString("ts", "2015-11-23T03:37:35.033155 Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError6) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Invalid time format: "
+ "2015-11-23T03:37:35.033155 1234Z")));
+
+ ow_->StartObject("")
+ // Whitespace in the Timestamp nanos is not allowed.
+ ->RenderString("ts", "2015-11-23T03:37:35.033155 1234Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError7) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Invalid time format: "
+ "2015-11-23T03:37:35.033abc155Z")));
+
+ ow_->StartObject("")
+ // Non-numeric characters in the Timestamp nanos is not allowed.
+ ->RenderString("ts", "2015-11-23T03:37:35.033abc155Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseDuration) {
+ TimestampDuration duration;
+ google::protobuf::Duration* dur = duration.mutable_dur();
+ dur->set_seconds(1448216930);
+ dur->set_nanos(132262000);
+
+ ow_->StartObject("")->RenderString("dur", "1448216930.132262s")->EndObject();
+ CheckOutput(duration);
+}
TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) {
TimestampDuration duration;
@@ -962,7 +1042,7 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) {
InvalidValue(
_, StringPiece("type.googleapis.com/google.protobuf.Duration"),
StringPiece("Field 'dur', Invalid duration format, failed to "
- "parse nanos seconds")));
+ "parse nano seconds")));
ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject();
CheckOutput(duration);
@@ -981,6 +1061,19 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError4) {
CheckOutput(duration);
}
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError5) {
+ TimestampDuration duration;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece("Field 'dur', Duration value exceeds limits")));
+
+ ow_->StartObject("")->RenderString("dur", "0.1000000001s")->EndObject();
+ CheckOutput(duration);
+}
+
TEST_P(ProtoStreamObjectWriterTimestampDurationTest,
MismatchedTimestampTypeInput) {
TimestampDuration timestamp;
@@ -1066,12 +1159,12 @@ TEST_P(ProtoStreamObjectWriterStructTest, StructInvalidInputFailure) {
TEST_P(ProtoStreamObjectWriterStructTest, SimpleRepeatedStructMapKeyTest) {
EXPECT_CALL(
listener_,
- InvalidName(_, StringPiece("k1"),
- StringPiece("Repeated map key: 'k1' is already set.")));
+ InvalidName(_, StringPiece("gBike"),
+ StringPiece("Repeated map key: 'gBike' is already set.")));
ow_->StartObject("")
->StartObject("object")
- ->RenderString("k1", "v1")
- ->RenderString("k1", "v2")
+ ->RenderString("gBike", "v1")
+ ->RenderString("gBike", "v2")
->EndObject()
->EndObject();
}
@@ -1121,10 +1214,11 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
TEST_P(ProtoStreamObjectWriterMapTest, MapShouldNotAcceptList) {
MapIn mm;
- EXPECT_CALL(listener_,
- InvalidValue(_, StringPiece("Map"),
- StringPiece("Cannot bind a list to map.")))
- .With(Args<0>(HasObjectLocation("map_input")));
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(
+ _, StringPiece("Map"),
+ StringPiece("Cannot bind a list to map for field 'map_input'.")));
ow_->StartObject("")
->StartList("map_input")
->RenderString("a", "b")
@@ -1591,13 +1685,17 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeEscapedCorrectly) {
TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyCanContainAnyChars) {
FieldMaskTest expected;
expected.mutable_single_mask()->add_paths(
- "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War孙天涌,./?><\\\\\"]");
+ // \xE5\xAD\x99 is the UTF-8 byte sequence for chinese character 孙.
+ // We cannot embed non-ASCII characters in the code directly because
+ // some windows compilers will try to interpret them using the system's
+ // current encoding and end up with invalid UTF-8 byte sequence.
+ "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War\xE5\xAD\x99,./?><\\\\\"]");
expected.mutable_single_mask()->add_paths("path.to.map[\"key2\"]");
ow_->StartObject("");
ow_->RenderString(
"single_mask",
- "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War孙天涌,./?><\\\\\"],"
+ "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War\xE5\xAD\x99,./?><\\\\\"],"
"path.to.map[\"key2\"]");
ow_->EndObject();
diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter.h b/src/google/protobuf/util/internal/snake2camel_objectwriter.h
deleted file mode 100644
index 9b4ab8a3..00000000
--- a/src/google/protobuf/util/internal/snake2camel_objectwriter.h
+++ /dev/null
@@ -1,165 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
-#define GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
-
-#include <string>
-
-#include <google/protobuf/stubs/common.h>
-#include <google/protobuf/stubs/strutil.h>
-#include <google/protobuf/stubs/stringpiece.h>
-#include <google/protobuf/util/internal/object_writer.h>
-#include <google/protobuf/util/internal/utility.h>
-
-namespace google {
-namespace protobuf {
-namespace util {
-namespace converter {
-
-// Snake2CamelObjectWriter is an ObjectWriter than translates each field name
-// from snake_case to camelCase. Typical usage is:
-// ProtoStreamObjectSource psos(...);
-// JsonObjectWriter jow(...);
-// Snake2CamelObjectWriter snake_to_camel(&jow);
-// psos.writeTo(&snake_to_camel);
-class Snake2CamelObjectWriter : public ObjectWriter {
- public:
- explicit Snake2CamelObjectWriter(ObjectWriter* ow)
- : ow_(ow), normalize_case_(true) {}
- virtual ~Snake2CamelObjectWriter() {}
-
- // ObjectWriter methods.
- virtual Snake2CamelObjectWriter* StartObject(StringPiece name) {
- ow_->StartObject(name);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* EndObject() {
- ow_->EndObject();
- return this;
- }
-
- virtual Snake2CamelObjectWriter* StartList(StringPiece name) {
- ow_->StartList(name);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* EndList() {
- ow_->EndList();
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderBool(StringPiece name, bool value) {
- ow_->RenderBool(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderInt32(StringPiece name, int32 value) {
- ow_->RenderInt32(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderUint32(StringPiece name,
- uint32 value) {
- ow_->RenderUint32(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderInt64(StringPiece name, int64 value) {
- ow_->RenderInt64(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderUint64(StringPiece name,
- uint64 value) {
- ow_->RenderUint64(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderDouble(StringPiece name,
- double value) {
- ow_->RenderDouble(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderFloat(StringPiece name, float value) {
- ow_->RenderFloat(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderString(StringPiece name,
- StringPiece value) {
- ow_->RenderString(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderBytes(StringPiece name,
- StringPiece value) {
- ow_->RenderBytes(name, value);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* RenderNull(StringPiece name) {
- ow_->RenderNull(name);
- return this;
- }
-
- virtual Snake2CamelObjectWriter* DisableCaseNormalizationForNextKey() {
- normalize_case_ = false;
- return this;
- }
-
- private:
- ObjectWriter* ow_;
- bool normalize_case_;
-
- bool ShouldNormalizeCase(StringPiece name) {
- if (normalize_case_) {
- return !IsCamel(name);
- } else {
- normalize_case_ = true;
- return false;
- }
- }
-
- bool IsCamel(StringPiece name) {
- return name.empty() || (ascii_islower(name[0]) && !name.contains("_"));
- }
-
- GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Snake2CamelObjectWriter);
-};
-
-} // namespace converter
-} // namespace util
-} // namespace protobuf
-
-} // namespace google
-#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/testdata/books.proto b/src/google/protobuf/util/internal/testdata/books.proto
index 6e2f109b..82b81760 100644
--- a/src/google/protobuf/util/internal/testdata/books.proto
+++ b/src/google/protobuf/util/internal/testdata/books.proto
@@ -66,7 +66,7 @@ message Publisher {
// An author of a book
message Author {
- optional uint64 id = 1;
+ optional uint64 id = 1 [json_name = "@id"];
optional string name = 2;
repeated string pseudonym = 3;
optional bool alive = 4;
diff --git a/src/google/protobuf/util/internal/testdata/default_value.proto b/src/google/protobuf/util/internal/testdata/default_value.proto
index ebbdf6ab..cccc741c 100644
--- a/src/google/protobuf/util/internal/testdata/default_value.proto
+++ b/src/google/protobuf/util/internal/testdata/default_value.proto
@@ -75,9 +75,10 @@ message DefaultValueTestCases {
IntToStringMap int_to_string = 403;
MixedMap mixed1 = 404;
MixedMap2 mixed2 = 405;
- MessageMap map_of_objects = 406;
- MixedMap mixed_empty = 407;
- MessageMap message_map_empty = 408;
+ MixedMap2 empty_mixed2 = 406;
+ MessageMap map_of_objects = 407;
+ MixedMap mixed_empty = 408;
+ MessageMap message_map_empty = 409;
DoubleValueMessage double_value = 501;
DoubleValueMessage double_value_default = 502;
}
diff --git a/src/google/protobuf/util/internal/testdata/default_value_test.proto b/src/google/protobuf/util/internal/testdata/default_value_test.proto
index 21b85e6d..93288341 100644
--- a/src/google/protobuf/util/internal/testdata/default_value_test.proto
+++ b/src/google/protobuf/util/internal/testdata/default_value_test.proto
@@ -43,4 +43,11 @@ message DefaultValueTest {
bool bool_value = 13;
string string_value = 15;
bytes bytes_value = 17 [ctype = CORD];
+
+ enum EnumDefault {
+ ENUM_FIRST = 0;
+ ENUM_SECOND = 1;
+ ENUM_THIRD = 2;
+ }
+ EnumDefault enum_value = 18;
}
diff --git a/src/google/protobuf/util/internal/testdata/maps.proto b/src/google/protobuf/util/internal/testdata/maps.proto
index 7fb42a26..6475ecdd 100644
--- a/src/google/protobuf/util/internal/testdata/maps.proto
+++ b/src/google/protobuf/util/internal/testdata/maps.proto
@@ -44,6 +44,35 @@ message MapOut {
map<string, MapM> map1 = 1;
map<string, MapOut> map2 = 2;
map<int32, string> map3 = 3;
+ map<bool, string> map4 = 5;
+ string bar = 4;
+}
+
+// A message with exactly the same wire representation as MapOut, but using
+// repeated message fields instead of map fields. We use this message to test
+// the wire-format compatibility of the JSON transcoder (e.g., whether it
+// handles missing keys correctly).
+message MapOutWireFormat {
+ message Map1Entry {
+ string key = 1;
+ MapM value = 2;
+ }
+ repeated Map1Entry map1 = 1;
+ message Map2Entry {
+ string key = 1;
+ MapOut value = 2;
+ }
+ repeated Map2Entry map2 = 2;
+ message Map3Entry {
+ int32 key = 1;
+ string value = 2;
+ }
+ repeated Map3Entry map3 = 3;
+ message Map4Entry {
+ bool key = 1;
+ string value = 2;
+ }
+ repeated Map4Entry map4 = 5;
string bar = 4;
}
diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc
index a45a76e3..00a8ee7a 100644
--- a/src/google/protobuf/util/internal/type_info.cc
+++ b/src/google/protobuf/util/internal/type_info.cc
@@ -136,8 +136,7 @@ class TypeInfoForTypeResolver : public TypeInfo {
for (int i = 0; i < type->fields_size(); ++i) {
const google::protobuf::Field& field = type->fields(i);
StringPiece name = field.name();
- StringPiece camel_case_name =
- *string_storage_.insert(ToCamelCase(name)).first;
+ StringPiece camel_case_name = field.json_name();
const StringPiece* existing = InsertOrReturnExisting(
&camel_case_name_table_, camel_case_name, name);
if (existing && *existing != name) {
diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc
index 61899c24..1ddf2487 100644
--- a/src/google/protobuf/util/internal/utility.cc
+++ b/src/google/protobuf/util/internal/utility.cc
@@ -49,7 +49,8 @@ namespace converter {
namespace {
const StringPiece SkipWhiteSpace(StringPiece str) {
StringPiece::size_type i;
- for (i = 0; i < str.size() && isspace(str[i]); ++i) {}
+ for (i = 0; i < str.size() && isspace(str[i]); ++i) {
+ }
GOOGLE_DCHECK(i == str.size() || !isspace(str[i]));
return StringPiece(str, i);
}
@@ -160,6 +161,19 @@ const google::protobuf::Field* FindFieldInTypeOrNull(
return NULL;
}
+const google::protobuf::Field* FindJsonFieldInTypeOrNull(
+ const google::protobuf::Type* type, StringPiece json_name) {
+ if (type != NULL) {
+ for (int i = 0; i < type->fields_size(); ++i) {
+ const google::protobuf::Field& field = type->fields(i);
+ if (field.json_name() == json_name) {
+ return &field;
+ }
+ }
+ }
+ return NULL;
+}
+
const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
const google::protobuf::Enum* enum_type, StringPiece enum_name) {
if (enum_type != NULL) {
@@ -316,16 +330,23 @@ string FloatAsString(float value) {
return DoubleAsString(value);
}
-bool SafeStrToFloat(StringPiece str, float *value) {
+bool SafeStrToFloat(StringPiece str, float* value) {
double double_value;
if (!safe_strtod(str, &double_value)) {
return false;
}
- *value = static_cast<float>(double_value);
- if (MathLimits<float>::IsInf(*value)) {
+ if (MathLimits<double>::IsInf(double_value) ||
+ MathLimits<double>::IsNaN(double_value))
+ return false;
+
+ // Fail if the value is not representable in float.
+ if (double_value > std::numeric_limits<float>::max() ||
+ double_value < -std::numeric_limits<float>::max()) {
return false;
}
+
+ *value = static_cast<float>(double_value);
return true;
}
diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h
index 5ba97bd2..33df8eda 100644
--- a/src/google/protobuf/util/internal/utility.h
+++ b/src/google/protobuf/util/internal/utility.h
@@ -127,6 +127,11 @@ const google::protobuf::Option* FindOptionOrNull(
const google::protobuf::Field* FindFieldInTypeOrNull(
const google::protobuf::Type* type, StringPiece field_name);
+// Similar to FindFieldInTypeOrNull, but this looks up fields with given
+// json_name.
+const google::protobuf::Field* FindJsonFieldInTypeOrNull(
+ const google::protobuf::Type* type, StringPiece json_name);
+
// Finds and returns the EnumValue identified by enum_name in the passed tech
// Enum object. Returns NULL if none found.
const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto
index e8137677..a1e24c18 100644
--- a/src/google/protobuf/util/json_format_proto3.proto
+++ b/src/google/protobuf/util/json_format_proto3.proto
@@ -165,3 +165,12 @@ message TestListValue {
google.protobuf.ListValue value = 1;
repeated google.protobuf.ListValue repeated_value = 2;
}
+
+message TestBoolValue {
+ bool bool_value = 1;
+ map<bool, int32> bool_map = 2;
+}
+
+message TestCustomJsonName {
+ int32 value = 1 [json_name = "@value"];
+}
diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc
index 47237e5a..0f879dc7 100644
--- a/src/google/protobuf/util/message_differencer.cc
+++ b/src/google/protobuf/util/message_differencer.cc
@@ -238,9 +238,25 @@ void MessageDifferencer::TreatAsSet(const FieldDescriptor* field) {
GOOGLE_CHECK(key_comparator == NULL)
<< "Cannot treat this repeated field as both Map and Set for"
<< " comparison. Field name is: " << field->full_name();
+ GOOGLE_CHECK(list_fields_.find(field) == list_fields_.end())
+ << "Cannot treat the same field as both SET and LIST. Field name is: "
+ << field->full_name();
set_fields_.insert(field);
}
+void MessageDifferencer::TreatAsList(const FieldDescriptor* field) {
+ GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
+ << field->full_name();
+ const MapKeyComparator* key_comparator = GetMapKeyComparator(field);
+ GOOGLE_CHECK(key_comparator == NULL)
+ << "Cannot treat this repeated field as both Map and Set for"
+ << " comparison. Field name is: " << field->full_name();
+ GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
+ << "Cannot treat the same field as both SET and LIST. Field name is: "
+ << field->full_name();
+ list_fields_.insert(field);
+}
+
void MessageDifferencer::TreatAsMap(const FieldDescriptor* field,
const FieldDescriptor* key) {
GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
@@ -255,6 +271,9 @@ void MessageDifferencer::TreatAsMap(const FieldDescriptor* field,
GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
<< "Cannot treat this repeated field as both Map and Set for "
<< "comparison.";
+ GOOGLE_CHECK(list_fields_.find(field) == list_fields_.end())
+ << "Cannot treat this repeated field as both Map and List for "
+ << "comparison.";
MapKeyComparator* key_comparator =
new MultipleFieldsMapKeyComparator(this, key);
owned_key_comparators_.push_back(key_comparator);
@@ -920,7 +939,8 @@ bool MessageDifferencer::CheckPathChanged(
bool MessageDifferencer::IsTreatedAsSet(const FieldDescriptor* field) {
if (!field->is_repeated()) return false;
if (field->is_map()) return true;
- if (repeated_field_comparison_ == AS_SET) return true;
+ if (repeated_field_comparison_ == AS_SET)
+ return list_fields_.find(field) == list_fields_.end();
return (set_fields_.find(field) != set_fields_.end());
}
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
index 34c173db..3ea74e67 100644
--- a/src/google/protobuf/util/message_differencer.h
+++ b/src/google/protobuf/util/message_differencer.h
@@ -397,9 +397,16 @@ class LIBPROTOBUF_EXPORT MessageDifferencer {
// + n^3) in which n^3 is the time complexity of the maximum matching
// algorithm.
//
- // REQUIRES: field->is_repeated()
+ // REQUIRES: field->is_repeated() and field not registered with TreatAsList
void TreatAsSet(const FieldDescriptor* field);
+ // The elements of the given repeated field will be treated as a list for
+ // diffing purposes, so different orderings of the same elements will NOT be
+ // considered equal.
+ //
+ // REQUIRED: field->is_repeated() and field not registered with TreatAsSet
+ void TreatAsList(const FieldDescriptor* field);
+
// The elements of the given repeated field will be treated as a map for
// diffing purposes, with |key| being the map key. Thus, elements with the
// same key will be compared even if they do not appear at the same index.
@@ -791,6 +798,7 @@ class LIBPROTOBUF_EXPORT MessageDifferencer {
RepeatedFieldComparison repeated_field_comparison_;
FieldSet set_fields_;
+ FieldSet list_fields_;
// Keeps track of MapKeyComparators that are created within
// MessageDifferencer. These MapKeyComparators should be deleted
// before MessageDifferencer is destroyed.
diff --git a/src/google/protobuf/util/message_differencer_unittest.cc b/src/google/protobuf/util/message_differencer_unittest.cc
index 701b94ae..a867c881 100755
--- a/src/google/protobuf/util/message_differencer_unittest.cc
+++ b/src/google/protobuf/util/message_differencer_unittest.cc
@@ -1103,12 +1103,19 @@ TEST(MessageDifferencerTest, RepeatedFieldSetTest_Combination) {
msg1.add_rw("change"); msg2.add_rw("change");
// Compare
- util::MessageDifferencer differencer;
- differencer.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
- item->GetDescriptor()->FindFieldByName("a"));
- differencer.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv"));
- differencer.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra"));
- EXPECT_TRUE(differencer.Compare(msg1, msg2));
+ util::MessageDifferencer differencer1;
+ differencer1.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
+ item->GetDescriptor()->FindFieldByName("a"));
+ differencer1.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv"));
+ differencer1.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra"));
+ EXPECT_TRUE(differencer1.Compare(msg1, msg2));
+
+ util::MessageDifferencer differencer2;
+ differencer2.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
+ item->GetDescriptor()->FindFieldByName("a"));
+ differencer2.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ differencer2.TreatAsList(msg1.GetDescriptor()->FindFieldByName("rw"));
+ EXPECT_TRUE(differencer2.Compare(msg1, msg2));
}
TEST(MessageDifferencerTest, RepeatedFieldMapTest_Partial) {
@@ -1168,6 +1175,11 @@ TEST(MessageDifferencerTest, RepeatedFieldSetTest_Duplicates) {
differencer.TreatAsSet(GetFieldDescriptor(a, "rv"));
EXPECT_TRUE(differencer.Compare(b, a));
EXPECT_FALSE(differencer.Compare(c, a));
+
+ util::MessageDifferencer differencer1;
+ differencer1.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ EXPECT_TRUE(differencer1.Compare(b, a));
+ EXPECT_FALSE(differencer1.Compare(c, a));
}
TEST(MessageDifferencerTest, RepeatedFieldSetTest_PartialSimple) {
@@ -1442,11 +1454,10 @@ static const char* const kIgnoredFields[] = {"rm.b", "rm.m.b"};
class TestIgnorer : public util::MessageDifferencer::IgnoreCriteria {
public:
- bool IsIgnored(
+ virtual bool IsIgnored(
const Message& message1, const Message& message2,
const FieldDescriptor* field,
- const vector<util::MessageDifferencer::SpecificField>& parent_fields)
- override {
+ const vector<util::MessageDifferencer::SpecificField>& parent_fields) {
string name = "";
for (int i = 0; i < parent_fields.size(); ++i) {
name += parent_fields[i].field->name() + ".";
@@ -1488,8 +1499,10 @@ TEST(MessageDifferencerTest, TreatRepeatedFieldAsMapWithIgnoredKeyFields) {
class ValueProductMapKeyComparator
: public util::MessageDifferencer::MapKeyComparator {
public:
- virtual bool IsMatch(const Message &message1,
- const Message &message2) const {
+ typedef util::MessageDifferencer::SpecificField SpecificField;
+ virtual bool IsMatch(
+ const Message &message1, const Message &message2,
+ const vector<SpecificField>& parent_fields) const {
const Reflection* reflection1 = message1.GetReflection();
const Reflection* reflection2 = message2.GetReflection();
// FieldDescriptor for item.ra
@@ -2803,15 +2816,20 @@ class MatchingTest : public testing::Test {
const Message& msg1, const Message& msg2,
bool result) {
string output;
- io::StringOutputStream output_stream(&output);
- MessageDifferencer::StreamReporter reporter(&output_stream);
- reporter.set_report_modified_aggregates(true);
- differencer->set_report_matches(true);
- differencer->ReportDifferencesTo(&reporter);
- if (result) {
- EXPECT_TRUE(differencer->Compare(msg1, msg2));
- } else {
- EXPECT_FALSE(differencer->Compare(msg1, msg2));
+ {
+ // Before we return the "output" string, we must make sure the
+ // StreamReporter is destructored because its destructor will
+ // flush the stream.
+ io::StringOutputStream output_stream(&output);
+ MessageDifferencer::StreamReporter reporter(&output_stream);
+ reporter.set_report_modified_aggregates(true);
+ differencer->set_report_matches(true);
+ differencer->ReportDifferencesTo(&reporter);
+ if (result) {
+ EXPECT_TRUE(differencer->Compare(msg1, msg2));
+ } else {
+ EXPECT_FALSE(differencer->Compare(msg1, msg2));
+ }
}
return output;
}
diff --git a/src/google/protobuf/util/type_resolver_util.cc b/src/google/protobuf/util/type_resolver_util.cc
index a0996954..96393903 100644
--- a/src/google/protobuf/util/type_resolver_util.cc
+++ b/src/google/protobuf/util/type_resolver_util.cc
@@ -159,7 +159,10 @@ class DescriptorPoolTypeResolver : public TypeResolver {
}
field->set_number(descriptor->number());
field->set_name(descriptor->name());
- field->set_json_name(converter::ToCamelCase(descriptor->name()));
+ field->set_json_name(descriptor->json_name());
+ if (descriptor->has_default_value()) {
+ field->set_default_value(DefaultValueAsString(descriptor));
+ }
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
field->set_type_url(GetTypeUrl(descriptor->message_type()));
} else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
@@ -200,6 +203,46 @@ class DescriptorPoolTypeResolver : public TypeResolver {
return url_prefix_ + "/" + descriptor->full_name();
}
+ string DefaultValueAsString(const FieldDescriptor* descriptor) {
+ switch (descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return SimpleItoa(descriptor->default_value_int32());
+ break;
+ case FieldDescriptor::CPPTYPE_INT64:
+ return SimpleItoa(descriptor->default_value_int64());
+ break;
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return SimpleItoa(descriptor->default_value_uint32());
+ break;
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return SimpleItoa(descriptor->default_value_uint64());
+ break;
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return SimpleFtoa(descriptor->default_value_float());
+ break;
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return SimpleDtoa(descriptor->default_value_double());
+ break;
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return descriptor->default_value_bool() ? "true" : "false";
+ break;
+ case FieldDescriptor::CPPTYPE_STRING:
+ if (descriptor->type() == FieldDescriptor::TYPE_BYTES) {
+ return CEscape(descriptor->default_value_string());
+ } else {
+ return descriptor->default_value_string();
+ }
+ break;
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return descriptor->default_value_enum()->name();
+ break;
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ GOOGLE_LOG(DFATAL) << "Messages can't have default values!";
+ break;
+ }
+ return "";
+ }
+
string url_prefix_;
const DescriptorPool* pool_;
};
diff --git a/src/google/protobuf/util/type_resolver_util_test.cc b/src/google/protobuf/util/type_resolver_util_test.cc
index 74b2d0da..8a0bf652 100644
--- a/src/google/protobuf/util/type_resolver_util_test.cc
+++ b/src/google/protobuf/util/type_resolver_util_test.cc
@@ -43,6 +43,7 @@
#include <google/protobuf/map_unittest.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/util/json_format_proto3.pb.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
@@ -332,6 +333,19 @@ TEST_F(DescriptorPoolTypeResolverTest, TestEnum) {
EnumHasValue(type, "NEG", -1);
}
+TEST_F(DescriptorPoolTypeResolverTest, TestJsonName) {
+ Type type;
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<protobuf_unittest::TestAllTypes>(), &type)
+ .ok());
+ EXPECT_EQ("optionalInt32", FindField(type, "optional_int32")->json_name());
+
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<proto3::TestCustomJsonName>(), &type)
+ .ok());
+ EXPECT_EQ("@value", FindField(type, "value")->json_name());
+}
+
} // namespace
} // namespace util
} // namespace protobuf
diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h
index 991c3d04..79493ca0 100644
--- a/src/google/protobuf/wire_format_lite_inl.h
+++ b/src/google/protobuf/wire_format_lite_inl.h
@@ -412,12 +412,12 @@ inline bool WireFormatLite::ReadPackedPrimitive< \
CPPTYPE, WireFormatLite::DECLARED_TYPE>(input, values); \
}
-READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(uint32, TYPE_FIXED32);
-READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(uint64, TYPE_FIXED64);
-READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(int32, TYPE_SFIXED32);
-READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(int64, TYPE_SFIXED64);
-READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(float, TYPE_FLOAT);
-READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(double, TYPE_DOUBLE);
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(uint32, TYPE_FIXED32)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(uint64, TYPE_FIXED64)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(int32, TYPE_SFIXED32)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(int64, TYPE_SFIXED64)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(float, TYPE_FLOAT)
+READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE(double, TYPE_DOUBLE)
#undef READ_REPEATED_PACKED_FIXED_SIZE_PRIMITIVE
@@ -835,12 +835,14 @@ inline int WireFormatLite::EnumSize(int value) {
}
inline int WireFormatLite::StringSize(const string& value) {
- return io::CodedOutputStream::VarintSize32(value.size()) +
- value.size();
+ return static_cast<int>(
+ io::CodedOutputStream::VarintSize32(static_cast<uint32>(value.size())) +
+ value.size());
}
inline int WireFormatLite::BytesSize(const string& value) {
- return io::CodedOutputStream::VarintSize32(value.size()) +
- value.size();
+ return static_cast<int>(
+ io::CodedOutputStream::VarintSize32(static_cast<uint32>(value.size())) +
+ value.size());
}
diff --git a/src/google/protobuf/wrappers.pb.cc b/src/google/protobuf/wrappers.pb.cc
index b2a7e970..212dd219 100644
--- a/src/google/protobuf/wrappers.pb.cc
+++ b/src/google/protobuf/wrappers.pb.cc
@@ -2,11 +2,12 @@
// source: google/protobuf/wrappers.proto
#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
-#include "google/protobuf/wrappers.pb.h"
+#include <google/protobuf/wrappers.pb.h>
#include <algorithm>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/wire_format_lite_inl.h>
@@ -262,10 +263,10 @@ void protobuf_AddDesc_google_2fprotobuf_2fwrappers_2eproto() {
"e\030\001 \001(\004\"\033\n\nInt32Value\022\r\n\005value\030\001 \001(\005\"\034\n\013"
"UInt32Value\022\r\n\005value\030\001 \001(\r\"\032\n\tBoolValue\022"
"\r\n\005value\030\001 \001(\010\"\034\n\013StringValue\022\r\n\005value\030\001"
- " \001(\t\"\033\n\nBytesValue\022\r\n\005value\030\001 \001(\014BP\n\023com"
- ".google.protobufB\rWrappersProtoP\001\240\001\001\242\002\003G"
- "PB\252\002\036Google.Protobuf.WellKnownTypesb\006pro"
- "to3", 403);
+ " \001(\t\"\033\n\nBytesValue\022\r\n\005value\030\001 \001(\014BS\n\023com"
+ ".google.protobufB\rWrappersProtoP\001\240\001\001\370\001\001\242"
+ "\002\003GPB\252\002\036Google.Protobuf.WellKnownTypesb\006"
+ "proto3", 406);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"google/protobuf/wrappers.proto", &protobuf_RegisterTypes);
DoubleValue::default_instance_ = new DoubleValue();
@@ -308,9 +309,9 @@ static void MergeFromFail(int line) {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int DoubleValue::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
DoubleValue::DoubleValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -318,6 +319,14 @@ DoubleValue::DoubleValue()
// @@protoc_insertion_point(constructor:google.protobuf.DoubleValue)
}
+DoubleValue::DoubleValue(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.DoubleValue)
+}
+
void DoubleValue::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -342,10 +351,20 @@ DoubleValue::~DoubleValue() {
}
void DoubleValue::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void DoubleValue::ArenaDtor(void* object) {
+ DoubleValue* _this = reinterpret_cast< DoubleValue* >(object);
+ (void)_this;
+}
+void DoubleValue::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void DoubleValue::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -364,11 +383,7 @@ const DoubleValue& DoubleValue::default_instance() {
DoubleValue* DoubleValue::default_instance_ = NULL;
DoubleValue* DoubleValue::New(::google::protobuf::Arena* arena) const {
- DoubleValue* n = new DoubleValue;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<DoubleValue>(arena);
}
void DoubleValue::Clear() {
@@ -495,6 +510,18 @@ bool DoubleValue::IsInitialized() const {
void DoubleValue::Swap(DoubleValue* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ DoubleValue temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void DoubleValue::UnsafeArenaSwap(DoubleValue* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void DoubleValue::InternalSwap(DoubleValue* other) {
@@ -532,9 +559,9 @@ void DoubleValue::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int FloatValue::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
FloatValue::FloatValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -542,6 +569,14 @@ FloatValue::FloatValue()
// @@protoc_insertion_point(constructor:google.protobuf.FloatValue)
}
+FloatValue::FloatValue(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.FloatValue)
+}
+
void FloatValue::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -566,10 +601,20 @@ FloatValue::~FloatValue() {
}
void FloatValue::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void FloatValue::ArenaDtor(void* object) {
+ FloatValue* _this = reinterpret_cast< FloatValue* >(object);
+ (void)_this;
+}
+void FloatValue::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void FloatValue::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -588,11 +633,7 @@ const FloatValue& FloatValue::default_instance() {
FloatValue* FloatValue::default_instance_ = NULL;
FloatValue* FloatValue::New(::google::protobuf::Arena* arena) const {
- FloatValue* n = new FloatValue;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<FloatValue>(arena);
}
void FloatValue::Clear() {
@@ -719,6 +760,18 @@ bool FloatValue::IsInitialized() const {
void FloatValue::Swap(FloatValue* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ FloatValue temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void FloatValue::UnsafeArenaSwap(FloatValue* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void FloatValue::InternalSwap(FloatValue* other) {
@@ -756,9 +809,9 @@ void FloatValue::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Int64Value::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Int64Value::Int64Value()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -766,6 +819,14 @@ Int64Value::Int64Value()
// @@protoc_insertion_point(constructor:google.protobuf.Int64Value)
}
+Int64Value::Int64Value(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.Int64Value)
+}
+
void Int64Value::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -790,10 +851,20 @@ Int64Value::~Int64Value() {
}
void Int64Value::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void Int64Value::ArenaDtor(void* object) {
+ Int64Value* _this = reinterpret_cast< Int64Value* >(object);
+ (void)_this;
+}
+void Int64Value::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void Int64Value::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -812,11 +883,7 @@ const Int64Value& Int64Value::default_instance() {
Int64Value* Int64Value::default_instance_ = NULL;
Int64Value* Int64Value::New(::google::protobuf::Arena* arena) const {
- Int64Value* n = new Int64Value;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<Int64Value>(arena);
}
void Int64Value::Clear() {
@@ -945,6 +1012,18 @@ bool Int64Value::IsInitialized() const {
void Int64Value::Swap(Int64Value* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ Int64Value temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void Int64Value::UnsafeArenaSwap(Int64Value* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void Int64Value::InternalSwap(Int64Value* other) {
@@ -982,9 +1061,9 @@ void Int64Value::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int UInt64Value::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
UInt64Value::UInt64Value()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -992,6 +1071,14 @@ UInt64Value::UInt64Value()
// @@protoc_insertion_point(constructor:google.protobuf.UInt64Value)
}
+UInt64Value::UInt64Value(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.UInt64Value)
+}
+
void UInt64Value::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -1016,10 +1103,20 @@ UInt64Value::~UInt64Value() {
}
void UInt64Value::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void UInt64Value::ArenaDtor(void* object) {
+ UInt64Value* _this = reinterpret_cast< UInt64Value* >(object);
+ (void)_this;
+}
+void UInt64Value::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void UInt64Value::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -1038,11 +1135,7 @@ const UInt64Value& UInt64Value::default_instance() {
UInt64Value* UInt64Value::default_instance_ = NULL;
UInt64Value* UInt64Value::New(::google::protobuf::Arena* arena) const {
- UInt64Value* n = new UInt64Value;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<UInt64Value>(arena);
}
void UInt64Value::Clear() {
@@ -1171,6 +1264,18 @@ bool UInt64Value::IsInitialized() const {
void UInt64Value::Swap(UInt64Value* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ UInt64Value temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void UInt64Value::UnsafeArenaSwap(UInt64Value* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void UInt64Value::InternalSwap(UInt64Value* other) {
@@ -1208,9 +1313,9 @@ void UInt64Value::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int Int32Value::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
Int32Value::Int32Value()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1218,6 +1323,14 @@ Int32Value::Int32Value()
// @@protoc_insertion_point(constructor:google.protobuf.Int32Value)
}
+Int32Value::Int32Value(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.Int32Value)
+}
+
void Int32Value::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -1242,10 +1355,20 @@ Int32Value::~Int32Value() {
}
void Int32Value::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void Int32Value::ArenaDtor(void* object) {
+ Int32Value* _this = reinterpret_cast< Int32Value* >(object);
+ (void)_this;
+}
+void Int32Value::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void Int32Value::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -1264,11 +1387,7 @@ const Int32Value& Int32Value::default_instance() {
Int32Value* Int32Value::default_instance_ = NULL;
Int32Value* Int32Value::New(::google::protobuf::Arena* arena) const {
- Int32Value* n = new Int32Value;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<Int32Value>(arena);
}
void Int32Value::Clear() {
@@ -1397,6 +1516,18 @@ bool Int32Value::IsInitialized() const {
void Int32Value::Swap(Int32Value* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ Int32Value temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void Int32Value::UnsafeArenaSwap(Int32Value* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void Int32Value::InternalSwap(Int32Value* other) {
@@ -1434,9 +1565,9 @@ void Int32Value::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int UInt32Value::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
UInt32Value::UInt32Value()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1444,6 +1575,14 @@ UInt32Value::UInt32Value()
// @@protoc_insertion_point(constructor:google.protobuf.UInt32Value)
}
+UInt32Value::UInt32Value(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.UInt32Value)
+}
+
void UInt32Value::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -1468,10 +1607,20 @@ UInt32Value::~UInt32Value() {
}
void UInt32Value::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void UInt32Value::ArenaDtor(void* object) {
+ UInt32Value* _this = reinterpret_cast< UInt32Value* >(object);
+ (void)_this;
+}
+void UInt32Value::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void UInt32Value::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -1490,11 +1639,7 @@ const UInt32Value& UInt32Value::default_instance() {
UInt32Value* UInt32Value::default_instance_ = NULL;
UInt32Value* UInt32Value::New(::google::protobuf::Arena* arena) const {
- UInt32Value* n = new UInt32Value;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<UInt32Value>(arena);
}
void UInt32Value::Clear() {
@@ -1623,6 +1768,18 @@ bool UInt32Value::IsInitialized() const {
void UInt32Value::Swap(UInt32Value* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ UInt32Value temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void UInt32Value::UnsafeArenaSwap(UInt32Value* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void UInt32Value::InternalSwap(UInt32Value* other) {
@@ -1660,9 +1817,9 @@ void UInt32Value::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int BoolValue::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
BoolValue::BoolValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1670,6 +1827,14 @@ BoolValue::BoolValue()
// @@protoc_insertion_point(constructor:google.protobuf.BoolValue)
}
+BoolValue::BoolValue(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.BoolValue)
+}
+
void BoolValue::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -1694,10 +1859,20 @@ BoolValue::~BoolValue() {
}
void BoolValue::SharedDtor() {
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
if (this != default_instance_) {
}
}
+void BoolValue::ArenaDtor(void* object) {
+ BoolValue* _this = reinterpret_cast< BoolValue* >(object);
+ (void)_this;
+}
+void BoolValue::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void BoolValue::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -1716,11 +1891,7 @@ const BoolValue& BoolValue::default_instance() {
BoolValue* BoolValue::default_instance_ = NULL;
BoolValue* BoolValue::New(::google::protobuf::Arena* arena) const {
- BoolValue* n = new BoolValue;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<BoolValue>(arena);
}
void BoolValue::Clear() {
@@ -1847,6 +2018,18 @@ bool BoolValue::IsInitialized() const {
void BoolValue::Swap(BoolValue* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ BoolValue temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void BoolValue::UnsafeArenaSwap(BoolValue* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void BoolValue::InternalSwap(BoolValue* other) {
@@ -1884,9 +2067,9 @@ void BoolValue::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int StringValue::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
StringValue::StringValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -1894,6 +2077,14 @@ StringValue::StringValue()
// @@protoc_insertion_point(constructor:google.protobuf.StringValue)
}
+StringValue::StringValue(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.StringValue)
+}
+
void StringValue::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -1919,11 +2110,21 @@ StringValue::~StringValue() {
}
void StringValue::SharedDtor() {
- value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
+ value_.Destroy(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
if (this != default_instance_) {
}
}
+void StringValue::ArenaDtor(void* object) {
+ StringValue* _this = reinterpret_cast< StringValue* >(object);
+ (void)_this;
+}
+void StringValue::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void StringValue::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -1942,15 +2143,11 @@ const StringValue& StringValue::default_instance() {
StringValue* StringValue::default_instance_ = NULL;
StringValue* StringValue::New(::google::protobuf::Arena* arena) const {
- StringValue* n = new StringValue;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<StringValue>(arena);
}
void StringValue::Clear() {
- value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
bool StringValue::MergePartialFromCodedStream(
@@ -2065,8 +2262,7 @@ void StringValue::MergeFrom(const ::google::protobuf::Message& from) {
void StringValue::MergeFrom(const StringValue& from) {
if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
if (from.value().size() > 0) {
-
- value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_);
+ set_value(from.value());
}
}
@@ -2089,6 +2285,18 @@ bool StringValue::IsInitialized() const {
void StringValue::Swap(StringValue* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ StringValue temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void StringValue::UnsafeArenaSwap(StringValue* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void StringValue::InternalSwap(StringValue* other) {
@@ -2110,36 +2318,44 @@ void StringValue::InternalSwap(StringValue* other) {
// optional string value = 1;
void StringValue::clear_value() {
- value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
const ::std::string& StringValue::value() const {
// @@protoc_insertion_point(field_get:google.protobuf.StringValue.value)
- return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
void StringValue::set_value(const ::std::string& value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set:google.protobuf.StringValue.value)
}
void StringValue::set_value(const char* value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value),
+ GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_char:google.protobuf.StringValue.value)
}
- void StringValue::set_value(const char* value, size_t size) {
+ void StringValue::set_value(const char* value,
+ size_t size) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
- ::std::string(reinterpret_cast<const char*>(value), size));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(
+ reinterpret_cast<const char*>(value), size), GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_pointer:google.protobuf.StringValue.value)
}
::std::string* StringValue::mutable_value() {
// @@protoc_insertion_point(field_mutable:google.protobuf.StringValue.value)
- return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
::std::string* StringValue::release_value() {
- return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
+}
+ ::std::string* StringValue::unsafe_arena_release_value() {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+
+ return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
}
void StringValue::set_allocated_value(::std::string* value) {
if (value != NULL) {
@@ -2147,7 +2363,20 @@ void StringValue::clear_value() {
} else {
}
- value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value,
+ GetArenaNoVirtual());
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value)
+}
+ void StringValue::unsafe_arena_set_allocated_value(
+ ::std::string* value) {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value)
}
@@ -2155,9 +2384,9 @@ void StringValue::clear_value() {
// ===================================================================
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
const int BytesValue::kValueFieldNumber;
-#endif // !_MSC_VER
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
BytesValue::BytesValue()
: ::google::protobuf::Message(), _internal_metadata_(NULL) {
@@ -2165,6 +2394,14 @@ BytesValue::BytesValue()
// @@protoc_insertion_point(constructor:google.protobuf.BytesValue)
}
+BytesValue::BytesValue(::google::protobuf::Arena* arena)
+ : ::google::protobuf::Message(),
+ _internal_metadata_(arena) {
+ SharedCtor();
+ RegisterArenaDtor(arena);
+ // @@protoc_insertion_point(arena_constructor:google.protobuf.BytesValue)
+}
+
void BytesValue::InitAsDefaultInstance() {
_is_default_instance_ = true;
}
@@ -2190,11 +2427,21 @@ BytesValue::~BytesValue() {
}
void BytesValue::SharedDtor() {
- value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ if (GetArenaNoVirtual() != NULL) {
+ return;
+ }
+
+ value_.Destroy(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
if (this != default_instance_) {
}
}
+void BytesValue::ArenaDtor(void* object) {
+ BytesValue* _this = reinterpret_cast< BytesValue* >(object);
+ (void)_this;
+}
+void BytesValue::RegisterArenaDtor(::google::protobuf::Arena* arena) {
+}
void BytesValue::SetCachedSize(int size) const {
GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
_cached_size_ = size;
@@ -2213,15 +2460,11 @@ const BytesValue& BytesValue::default_instance() {
BytesValue* BytesValue::default_instance_ = NULL;
BytesValue* BytesValue::New(::google::protobuf::Arena* arena) const {
- BytesValue* n = new BytesValue;
- if (arena != NULL) {
- arena->Own(n);
- }
- return n;
+ return ::google::protobuf::Arena::CreateMessage<BytesValue>(arena);
}
void BytesValue::Clear() {
- value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
bool BytesValue::MergePartialFromCodedStream(
@@ -2324,8 +2567,7 @@ void BytesValue::MergeFrom(const ::google::protobuf::Message& from) {
void BytesValue::MergeFrom(const BytesValue& from) {
if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
if (from.value().size() > 0) {
-
- value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_);
+ set_value(from.value());
}
}
@@ -2348,6 +2590,18 @@ bool BytesValue::IsInitialized() const {
void BytesValue::Swap(BytesValue* other) {
if (other == this) return;
+ if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {
+ InternalSwap(other);
+ } else {
+ BytesValue temp;
+ temp.MergeFrom(*this);
+ CopyFrom(*other);
+ other->CopyFrom(temp);
+ }
+}
+void BytesValue::UnsafeArenaSwap(BytesValue* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());
InternalSwap(other);
}
void BytesValue::InternalSwap(BytesValue* other) {
@@ -2369,36 +2623,44 @@ void BytesValue::InternalSwap(BytesValue* other) {
// optional bytes value = 1;
void BytesValue::clear_value() {
- value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
const ::std::string& BytesValue::value() const {
// @@protoc_insertion_point(field_get:google.protobuf.BytesValue.value)
- return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
void BytesValue::set_value(const ::std::string& value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set:google.protobuf.BytesValue.value)
}
void BytesValue::set_value(const char* value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value),
+ GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_char:google.protobuf.BytesValue.value)
}
- void BytesValue::set_value(const void* value, size_t size) {
+ void BytesValue::set_value(const void* value,
+ size_t size) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
- ::std::string(reinterpret_cast<const char*>(value), size));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(
+ reinterpret_cast<const char*>(value), size), GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_pointer:google.protobuf.BytesValue.value)
}
::std::string* BytesValue::mutable_value() {
// @@protoc_insertion_point(field_mutable:google.protobuf.BytesValue.value)
- return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
::std::string* BytesValue::release_value() {
- return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
+}
+ ::std::string* BytesValue::unsafe_arena_release_value() {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+
+ return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
}
void BytesValue::set_allocated_value(::std::string* value) {
if (value != NULL) {
@@ -2406,7 +2668,20 @@ void BytesValue::clear_value() {
} else {
}
- value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value,
+ GetArenaNoVirtual());
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value)
+}
+ void BytesValue::unsafe_arena_set_allocated_value(
+ ::std::string* value) {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value)
}
diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h
index 15bcc7a2..7dca938c 100644
--- a/src/google/protobuf/wrappers.pb.h
+++ b/src/google/protobuf/wrappers.pb.h
@@ -61,9 +61,14 @@ class LIBPROTOBUF_EXPORT DoubleValue : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const DoubleValue& default_instance();
+ void UnsafeArenaSwap(DoubleValue* other);
void Swap(DoubleValue* other);
// implements Message ----------------------------------------------
@@ -90,6 +95,11 @@ class LIBPROTOBUF_EXPORT DoubleValue : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(DoubleValue* other);
+ protected:
+ explicit DoubleValue(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -115,6 +125,9 @@ class LIBPROTOBUF_EXPORT DoubleValue : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
double value_;
mutable int _cached_size_;
@@ -139,9 +152,14 @@ class LIBPROTOBUF_EXPORT FloatValue : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const FloatValue& default_instance();
+ void UnsafeArenaSwap(FloatValue* other);
void Swap(FloatValue* other);
// implements Message ----------------------------------------------
@@ -168,6 +186,11 @@ class LIBPROTOBUF_EXPORT FloatValue : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(FloatValue* other);
+ protected:
+ explicit FloatValue(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -193,6 +216,9 @@ class LIBPROTOBUF_EXPORT FloatValue : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
float value_;
mutable int _cached_size_;
@@ -217,9 +243,14 @@ class LIBPROTOBUF_EXPORT Int64Value : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const Int64Value& default_instance();
+ void UnsafeArenaSwap(Int64Value* other);
void Swap(Int64Value* other);
// implements Message ----------------------------------------------
@@ -246,6 +277,11 @@ class LIBPROTOBUF_EXPORT Int64Value : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(Int64Value* other);
+ protected:
+ explicit Int64Value(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -271,6 +307,9 @@ class LIBPROTOBUF_EXPORT Int64Value : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::int64 value_;
mutable int _cached_size_;
@@ -295,9 +334,14 @@ class LIBPROTOBUF_EXPORT UInt64Value : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const UInt64Value& default_instance();
+ void UnsafeArenaSwap(UInt64Value* other);
void Swap(UInt64Value* other);
// implements Message ----------------------------------------------
@@ -324,6 +368,11 @@ class LIBPROTOBUF_EXPORT UInt64Value : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(UInt64Value* other);
+ protected:
+ explicit UInt64Value(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -349,6 +398,9 @@ class LIBPROTOBUF_EXPORT UInt64Value : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::uint64 value_;
mutable int _cached_size_;
@@ -373,9 +425,14 @@ class LIBPROTOBUF_EXPORT Int32Value : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const Int32Value& default_instance();
+ void UnsafeArenaSwap(Int32Value* other);
void Swap(Int32Value* other);
// implements Message ----------------------------------------------
@@ -402,6 +459,11 @@ class LIBPROTOBUF_EXPORT Int32Value : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(Int32Value* other);
+ protected:
+ explicit Int32Value(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -427,6 +489,9 @@ class LIBPROTOBUF_EXPORT Int32Value : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::int32 value_;
mutable int _cached_size_;
@@ -451,9 +516,14 @@ class LIBPROTOBUF_EXPORT UInt32Value : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const UInt32Value& default_instance();
+ void UnsafeArenaSwap(UInt32Value* other);
void Swap(UInt32Value* other);
// implements Message ----------------------------------------------
@@ -480,6 +550,11 @@ class LIBPROTOBUF_EXPORT UInt32Value : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(UInt32Value* other);
+ protected:
+ explicit UInt32Value(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -505,6 +580,9 @@ class LIBPROTOBUF_EXPORT UInt32Value : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::uint32 value_;
mutable int _cached_size_;
@@ -529,9 +607,14 @@ class LIBPROTOBUF_EXPORT BoolValue : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const BoolValue& default_instance();
+ void UnsafeArenaSwap(BoolValue* other);
void Swap(BoolValue* other);
// implements Message ----------------------------------------------
@@ -558,6 +641,11 @@ class LIBPROTOBUF_EXPORT BoolValue : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(BoolValue* other);
+ protected:
+ explicit BoolValue(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -583,6 +671,9 @@ class LIBPROTOBUF_EXPORT BoolValue : public ::google::protobuf::Message {
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
bool value_;
mutable int _cached_size_;
@@ -607,9 +698,14 @@ class LIBPROTOBUF_EXPORT StringValue : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const StringValue& default_instance();
+ void UnsafeArenaSwap(StringValue* other);
void Swap(StringValue* other);
// implements Message ----------------------------------------------
@@ -636,6 +732,11 @@ class LIBPROTOBUF_EXPORT StringValue : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(StringValue* other);
+ protected:
+ explicit StringValue(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -661,11 +762,17 @@ class LIBPROTOBUF_EXPORT StringValue : public ::google::protobuf::Message {
::std::string* mutable_value();
::std::string* release_value();
void set_allocated_value(::std::string* value);
+ ::std::string* unsafe_arena_release_value();
+ void unsafe_arena_set_allocated_value(
+ ::std::string* value);
// @@protoc_insertion_point(class_scope:google.protobuf.StringValue)
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::internal::ArenaStringPtr value_;
mutable int _cached_size_;
@@ -690,9 +797,14 @@ class LIBPROTOBUF_EXPORT BytesValue : public ::google::protobuf::Message {
return *this;
}
+ inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }
+ inline void* GetMaybeArenaPointer() const {
+ return MaybeArenaPtr();
+ }
static const ::google::protobuf::Descriptor* descriptor();
static const BytesValue& default_instance();
+ void UnsafeArenaSwap(BytesValue* other);
void Swap(BytesValue* other);
// implements Message ----------------------------------------------
@@ -719,6 +831,11 @@ class LIBPROTOBUF_EXPORT BytesValue : public ::google::protobuf::Message {
void SharedDtor();
void SetCachedSize(int size) const;
void InternalSwap(BytesValue* other);
+ protected:
+ explicit BytesValue(::google::protobuf::Arena* arena);
+ private:
+ static void ArenaDtor(void* object);
+ inline void RegisterArenaDtor(::google::protobuf::Arena* arena);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return _internal_metadata_.arena();
@@ -744,11 +861,17 @@ class LIBPROTOBUF_EXPORT BytesValue : public ::google::protobuf::Message {
::std::string* mutable_value();
::std::string* release_value();
void set_allocated_value(::std::string* value);
+ ::std::string* unsafe_arena_release_value();
+ void unsafe_arena_set_allocated_value(
+ ::std::string* value);
// @@protoc_insertion_point(class_scope:google.protobuf.BytesValue)
private:
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
+ friend class ::google::protobuf::Arena;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
bool _is_default_instance_;
::google::protobuf::internal::ArenaStringPtr value_;
mutable int _cached_size_;
@@ -895,36 +1018,44 @@ inline void BoolValue::set_value(bool value) {
// optional string value = 1;
inline void StringValue::clear_value() {
- value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
inline const ::std::string& StringValue::value() const {
// @@protoc_insertion_point(field_get:google.protobuf.StringValue.value)
- return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
inline void StringValue::set_value(const ::std::string& value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set:google.protobuf.StringValue.value)
}
inline void StringValue::set_value(const char* value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value),
+ GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_char:google.protobuf.StringValue.value)
}
-inline void StringValue::set_value(const char* value, size_t size) {
+inline void StringValue::set_value(const char* value,
+ size_t size) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
- ::std::string(reinterpret_cast<const char*>(value), size));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(
+ reinterpret_cast<const char*>(value), size), GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_pointer:google.protobuf.StringValue.value)
}
inline ::std::string* StringValue::mutable_value() {
// @@protoc_insertion_point(field_mutable:google.protobuf.StringValue.value)
- return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
inline ::std::string* StringValue::release_value() {
- return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
+}
+inline ::std::string* StringValue::unsafe_arena_release_value() {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+
+ return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
}
inline void StringValue::set_allocated_value(::std::string* value) {
if (value != NULL) {
@@ -932,7 +1063,20 @@ inline void StringValue::set_allocated_value(::std::string* value) {
} else {
}
- value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value,
+ GetArenaNoVirtual());
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value)
+}
+inline void StringValue::unsafe_arena_set_allocated_value(
+ ::std::string* value) {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value)
}
@@ -942,36 +1086,44 @@ inline void StringValue::set_allocated_value(::std::string* value) {
// optional bytes value = 1;
inline void BytesValue::clear_value() {
- value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
inline const ::std::string& BytesValue::value() const {
// @@protoc_insertion_point(field_get:google.protobuf.BytesValue.value)
- return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
inline void BytesValue::set_value(const ::std::string& value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set:google.protobuf.BytesValue.value)
}
inline void BytesValue::set_value(const char* value) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value),
+ GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_char:google.protobuf.BytesValue.value)
}
-inline void BytesValue::set_value(const void* value, size_t size) {
+inline void BytesValue::set_value(const void* value,
+ size_t size) {
- value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
- ::std::string(reinterpret_cast<const char*>(value), size));
+ value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(
+ reinterpret_cast<const char*>(value), size), GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_pointer:google.protobuf.BytesValue.value)
}
inline ::std::string* BytesValue::mutable_value() {
// @@protoc_insertion_point(field_mutable:google.protobuf.BytesValue.value)
- return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
}
inline ::std::string* BytesValue::release_value() {
- return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual());
+}
+inline ::std::string* BytesValue::unsafe_arena_release_value() {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+
+ return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
}
inline void BytesValue::set_allocated_value(::std::string* value) {
if (value != NULL) {
@@ -979,7 +1131,20 @@ inline void BytesValue::set_allocated_value(::std::string* value) {
} else {
}
- value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value,
+ GetArenaNoVirtual());
+ // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value)
+}
+inline void BytesValue::unsafe_arena_set_allocated_value(
+ ::std::string* value) {
+ GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ value, GetArenaNoVirtual());
// @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value)
}
diff --git a/src/google/protobuf/wrappers.proto b/src/google/protobuf/wrappers.proto
index a1d6e446..040d8a24 100644
--- a/src/google/protobuf/wrappers.proto
+++ b/src/google/protobuf/wrappers.proto
@@ -37,11 +37,12 @@ syntax = "proto3";
package google.protobuf;
-option java_generate_equals_and_hash = true;
-option java_multiple_files = true;
-option java_outer_classname = "WrappersProto";
-option java_package = "com.google.protobuf";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option java_package = "com.google.protobuf";
+option java_outer_classname = "WrappersProto";
+option java_multiple_files = true;
+option java_generate_equals_and_hash = true;
option objc_class_prefix = "GPB";
// Wrapper message for `double`.
diff --git a/travis.sh b/travis.sh
index 3b6ec336..5a77a2a8 100755
--- a/travis.sh
+++ b/travis.sh
@@ -11,6 +11,15 @@
# For when some other test needs the C++ main build, including protoc and
# libprotobuf.
internal_build_cpp() {
+ if [ $(uname -s) == "Linux" ]; then
+ # Install GCC 4.8 to replace the default GCC 4.6. We need 4.8 for more
+ # decent C++ 11 support in order to compile conformance tests.
+ sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
+ sudo apt-get update -qq
+ sudo apt-get install -qq g++-4.8
+ export CXX="g++-4.8" CC="gcc-4.8"
+ fi
+
./autogen.sh
./configure
make -j2
@@ -47,6 +56,24 @@ build_csharp() {
cd conformance && make test_csharp && cd ..
}
+build_golang() {
+ # Go build needs `protoc`.
+ internal_build_cpp
+ # Add protoc to the path so that the examples build finds it.
+ export PATH="`pwd`/src:$PATH"
+
+ # Install Go and the Go protobuf compiler plugin.
+ sudo apt-get update -qq
+ sudo apt-get install -qq golang
+ export GOPATH="$HOME/gocode"
+ mkdir -p "$GOPATH/src/github.com/google"
+ ln -s "`pwd`" "$GOPATH/src/github.com/google/protobuf"
+ export PATH="$GOPATH/bin:$PATH"
+ go get github.com/golang/protobuf/protoc-gen-go
+
+ cd examples && make gotest && cd ..
+}
+
use_java() {
version=$1
case "$version" in
@@ -75,7 +102,17 @@ use_java() {
build_java() {
# Java build needs `protoc`.
internal_build_cpp
- cd java && mvn test && cd ..
+ cd java && mvn test && mvn install
+ cd util && mvn test
+ cd ../..
+}
+
+build_java_with_conformance_tests() {
+ # Java build needs `protoc`.
+ internal_build_cpp
+ cd java && mvn test && mvn install
+ cd util && mvn test && mvn assembly:single
+ cd ../..
cd conformance && make test_java && cd ..
}
@@ -91,7 +128,7 @@ build_java_jdk6() {
}
build_java_jdk7() {
use_java jdk7
- build_java
+ build_java_with_conformance_tests
}
build_java_oracle7() {
use_java oracle7
@@ -136,8 +173,9 @@ internal_objectivec_common () {
brew update
brew outdated xctool || brew upgrade xctool
# Reused the build script that takes care of configuring and ensuring things
- # are up to date.
- objectivec/DevTools/full_mac_build.sh --core-only --skip-xcode
+ # are up to date. Xcode and conformance tests will be directly invoked.
+ objectivec/DevTools/full_mac_build.sh \
+ --core-only --skip-xcode --skip-objc-conformance
}
internal_xctool_debug_and_release() {
@@ -178,6 +216,7 @@ build_objectivec_osx() {
-scheme ProtocolBuffers \
-destination "platform=OS X,arch=x86_64" \
test
+ cd conformance && make test_objc && cd ..
}
build_python() {
@@ -232,6 +271,11 @@ build_jruby() {
cd ruby && bash travis-test.sh jruby && cd ..
}
+build_javascript() {
+ internal_build_cpp
+ cd js && npm install && npm test && cd ..
+}
+
# -------- main --------
if [ "$#" -ne 1 ]; then
diff --git a/util/python/BUILD b/util/python/BUILD
new file mode 100644
index 00000000..358c381c
--- /dev/null
+++ b/util/python/BUILD
@@ -0,0 +1,8 @@
+# This is a placeholder for python headers. Projects needing to use
+# fast cpp protos in protobuf's python interface should build with
+# --define=use_fast_cpp_protos=true, and in addition, provide
+# //util/python:python_headers dependency that in turn provides Python.h.
+cc_library(
+ name = "python_headers",
+ visibility = ["//visibility:public"],
+)