aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornmittler <nathanmittler@google.com>2016-10-01 11:01:43 -0700
committernmittler <nathanmittler@google.com>2016-10-01 11:01:43 -0700
commit0fdfe635e86ef16113fe539cb5350a48aaa31c6f (patch)
treeec0669d80d5149393602e339ef871cb88dbcc835
parent787f3fb16380a403c99716a23ed09cd7b9ea5b67 (diff)
downloadprotobuf-java_experimental.tar.gz
protobuf-java_experimental.tar.bz2
protobuf-java_experimental.zip
Adding experimental runtimejava_experimental
-rw-r--r--Makefile.am38
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProto2LiteSchema.java183
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProto2Schema.java400
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProto2StandardSchema.java127
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProto3LiteSchema.java178
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProto3Schema.java347
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProto3StandardSchema.java124
-rw-r--r--java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java263
-rw-r--r--java/core/src/main/java/com/google/protobuf/BinaryProtocolUtil.java117
-rw-r--r--java/core/src/main/java/com/google/protobuf/BinaryReader.java1516
-rw-r--r--java/core/src/main/java/com/google/protobuf/BinaryWriter.java2804
-rw-r--r--java/core/src/main/java/com/google/protobuf/BufferAllocator.java71
-rw-r--r--java/core/src/main/java/com/google/protobuf/ByteString.java21
-rw-r--r--java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java264
-rw-r--r--java/core/src/main/java/com/google/protobuf/FieldInfo.java175
-rw-r--r--java/core/src/main/java/com/google/protobuf/FieldType.java535
-rw-r--r--java/core/src/main/java/com/google/protobuf/Int2ObjectHashMap.java754
-rw-r--r--java/core/src/main/java/com/google/protobuf/JavaType.java87
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageInfo.java158
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageInfoFactory.java38
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2LiteLookupSchema.java281
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2LiteTableSchema.java291
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2LookupSchema.java273
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2Manifest.java261
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2SchemaFactory.java112
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto2TableSchema.java270
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto3LiteLookupSchema.java253
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto3LiteTableSchema.java263
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto3LookupSchema.java245
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto3Manifest.java233
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto3SchemaFactory.java115
-rw-r--r--java/core/src/main/java/com/google/protobuf/Proto3TableSchema.java242
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtoSyntax.java38
-rw-r--r--java/core/src/main/java/com/google/protobuf/Protobuf.java148
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtobufLists.java96
-rw-r--r--java/core/src/main/java/com/google/protobuf/Reader.java312
-rw-r--r--java/core/src/main/java/com/google/protobuf/RopeByteString.java5
-rw-r--r--java/core/src/main/java/com/google/protobuf/Schema.java55
-rw-r--r--java/core/src/main/java/com/google/protobuf/SchemaFactory.java42
-rw-r--r--java/core/src/main/java/com/google/protobuf/SchemaUtil.java516
-rw-r--r--java/core/src/main/java/com/google/protobuf/Writer.java155
41 files changed, 12403 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am
index 83f1043d..fac76c43 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -187,6 +187,44 @@ csharp_EXTRA_DIST= \
csharp/src/packages/repositories.config
java_EXTRA_DIST= \
+ java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java \
+ java/core/src/main/java/com/google/protobuf/BinaryProtocolUtil.java \
+ java/core/src/main/java/com/google/protobuf/BinaryReader.java \
+ java/core/src/main/java/com/google/protobuf/BinaryWriter.java \
+ java/core/src/main/java/com/google/protobuf/BufferAllocator.java \
+ java/core/src/main/java/com/google/protobuf/FieldInfo.java \
+ java/core/src/main/java/com/google/protobuf/Int2ObjectHashMap.java \
+ java/core/src/main/java/com/google/protobuf/MessageInfo.java \
+ java/core/src/main/java/com/google/protobuf/MessageInfoFactory.java \
+ java/core/src/main/java/com/google/protobuf/Protobuf.java \
+ java/core/src/main/java/com/google/protobuf/ProtobufLists.java \
+ java/core/src/main/java/com/google/protobuf/Reader.java \
+ java/core/src/main/java/com/google/protobuf/Schema.java \
+ java/core/src/main/java/com/google/protobuf/SchemaFactory.java \
+ java/core/src/main/java/com/google/protobuf/SchemaUtil.java \
+ java/core/src/main/java/com/google/protobuf/Writer.java \
+ java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProto2LiteSchema.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProto2Schema.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProto2StandardSchema.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProto3LiteSchema.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProto3Schema.java \
+ java/core/src/main/java/com/google/protobuf/AbstractProto3StandardSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto2LiteLookupSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto2LiteTableSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto2LookupSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto2Manifest.java \
+ java/core/src/main/java/com/google/protobuf/Proto2SchemaFactory.java \
+ java/core/src/main/java/com/google/protobuf/Proto2TableSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto3LiteLookupSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto3LiteTableSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto3LookupSchema.java \
+ java/core/src/main/java/com/google/protobuf/Proto3Manifest.java \
+ java/core/src/main/java/com/google/protobuf/Proto3SchemaFactory.java \
+ java/core/src/main/java/com/google/protobuf/Proto3TableSchema.java \
+ java/core/src/main/java/com/google/protobuf/ProtoSyntax.java \
+ java/core/src/main/java/com/google/protobuf/FieldType.java \
+ java/core/src/main/java/com/google/protobuf/JavaType.java \
java/README.md \
java/core/generate-sources-build.xml \
java/core/generate-test-sources-build.xml \
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProto2LiteSchema.java b/java/core/src/main/java/com/google/protobuf/AbstractProto2LiteSchema.java
new file mode 100644
index 00000000..d23fe978
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProto2LiteSchema.java
@@ -0,0 +1,183 @@
+// 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.Proto2Manifest.FIELD_LENGTH;
+import static com.google.protobuf.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+/** Base class for all proto2-lite-based schemas. */
+abstract class AbstractProto2LiteSchema<T> extends AbstractProto2Schema<T> {
+ final Class<?> messageClass;
+
+ public AbstractProto2LiteSchema(Class<T> messageClass, Proto2Manifest manifest) {
+ super(manifest);
+ this.messageClass = messageClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final T newInstance() {
+ T msg = (T) UnsafeUtil.allocateInstance(messageClass);
+ for (long pos = manifest.address; pos < manifest.limit; pos += FIELD_LENGTH) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ case 1: //FLOAT:
+ case 2: //INT64:
+ case 3: //UINT64:
+ case 4: //INT32:
+ case 5: //FIXED64:
+ case 6: //FIXED32:
+ case 7: //BOOL:
+ case 9: //MESSAGE:
+ case 11: //UINT32:
+ case 12: //ENUM:
+ case 13: //SFIXED32:
+ case 14: //SFIXED64:
+ case 15: //SINT32:
+ case 16: //SINT64:
+ // Do nothing, just use default values.
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), "");
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ByteString.EMPTY);
+ break;
+ case 17: //DOUBLE_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), DoubleArrayList.emptyList());
+ break;
+ case 18: //FLOAT_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), FloatArrayList.emptyList());
+ break;
+ case 19: //INT64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 20: //UINT64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 21: //INT32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 22: //FIXED64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 23: //FIXED32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 24: //BOOL_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), BooleanArrayList.emptyList());
+ break;
+ case 25: //STRING_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LazyStringArrayList.EMPTY);
+ break;
+ case 26: //MESSAGE_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ProtobufArrayList.emptyList());
+ break;
+ case 27: //BYTES_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ProtobufArrayList.emptyList());
+ break;
+ case 28: //UINT32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 29: //ENUM_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 30: //SFIXED32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 31: //SFIXED64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 32: //SINT32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 33: //SINT64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), DoubleArrayList.emptyList());
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), FloatArrayList.emptyList());
+ break;
+ case 36: //INT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 38: //INT32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), BooleanArrayList.emptyList());
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case -4: //GROUP:
+ break;
+ case -3: //GROUP_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ProtobufArrayList.emptyList());
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Initialize the base class fields.
+ SchemaUtil.initLiteBaseClassFields(msg);
+ return msg;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProto2Schema.java b/java/core/src/main/java/com/google/protobuf/AbstractProto2Schema.java
new file mode 100644
index 00000000..fd701682
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProto2Schema.java
@@ -0,0 +1,400 @@
+// 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.Proto2Manifest.FIELD_LENGTH;
+import static com.google.protobuf.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+import java.util.List;
+
+/** Abstract base class for all proto3-based schemas. */
+abstract class AbstractProto2Schema<T> implements Schema<T> {
+ protected final Proto2Manifest manifest;
+
+ public AbstractProto2Schema(Proto2Manifest manifest) {
+ this.manifest = manifest;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final void writeTo(T message, Writer writer) {
+ for (long pos = manifest.address; pos < manifest.limit; pos += FIELD_LENGTH) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+
+ // Benchmarks have shown that switching on a byte is faster than an enum.
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeDouble(
+ manifest.numberAt(pos), UnsafeUtil.getDouble(message, offset(typeAndOffset)));
+ }
+ break;
+ case 1: //FLOAT:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeFloat(
+ manifest.numberAt(pos), UnsafeUtil.getFloat(message, offset(typeAndOffset)));
+ }
+ break;
+ case 2: //INT64:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeInt64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)));
+ }
+ break;
+ case 3: //UINT64:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeUInt64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)));
+ }
+ break;
+ case 4: //INT32:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeInt32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)));
+ }
+ break;
+ case 5: //FIXED64:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeFixed64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)));
+ }
+ break;
+ case 6: //FIXED32:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeFixed32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)));
+ }
+ break;
+ case 7: //BOOL:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeBool(
+ manifest.numberAt(pos), UnsafeUtil.getBoolean(message, offset(typeAndOffset)));
+ }
+ break;
+ case 8: //STRING:
+ if (manifest.isFieldPresent(message, pos)) {
+ writeString(
+ manifest.numberAt(pos),
+ UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ }
+ break;
+ case 9: //MESSAGE:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeMessage(
+ manifest.numberAt(pos), UnsafeUtil.getObject(message, offset(typeAndOffset)));
+ }
+ break;
+ case 10: //BYTES:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeBytes(
+ manifest.numberAt(pos),
+ (ByteString) UnsafeUtil.getObject(message, offset(typeAndOffset)));
+ }
+ break;
+ case 11: //UINT32:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeUInt32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)));
+ }
+ break;
+ case 12: //ENUM:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeEnum(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)));
+ }
+ break;
+ case 13: //SFIXED32:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeSFixed32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)));
+ }
+ break;
+ case 14: //SFIXED64:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeSFixed64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)));
+ }
+ break;
+ case 15: //SINT32:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeSInt32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)));
+ }
+ break;
+ case 16: //SINT64:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeSInt64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)));
+ }
+ break;
+ case 17: //DOUBLE_LIST:
+ SchemaUtil.writeDoubleList(
+ manifest.numberAt(pos),
+ (List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 18: //FLOAT_LIST:
+ SchemaUtil.writeFloatList(
+ manifest.numberAt(pos),
+ (List<Float>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 19: //INT64_LIST:
+ SchemaUtil.writeInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 20: //UINT64_LIST:
+ SchemaUtil.writeUInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 21: //INT32_LIST:
+ SchemaUtil.writeInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 22: //FIXED64_LIST:
+ SchemaUtil.writeFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 23: //FIXED32_LIST:
+ SchemaUtil.writeFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 24: //BOOL_LIST:
+ SchemaUtil.writeBoolList(
+ manifest.numberAt(pos),
+ (List<Boolean>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 25: //STRING_LIST:
+ SchemaUtil.writeStringList(
+ manifest.numberAt(pos),
+ (List<String>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.writeMessageList(
+ manifest.numberAt(pos),
+ (List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 27: //BYTES_LIST:
+ SchemaUtil.writeBytesList(
+ manifest.numberAt(pos),
+ (List<ByteString>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 28: //UINT32_LIST:
+ SchemaUtil.writeUInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 29: //ENUM_LIST:
+ SchemaUtil.writeEnumList(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 30: //SFIXED32_LIST:
+ SchemaUtil.writeSFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 31: //SFIXED64_LIST:
+ SchemaUtil.writeSFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 32: //SINT32_LIST:
+ SchemaUtil.writeSInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 33: //SINT64_LIST:
+ SchemaUtil.writeSInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ SchemaUtil.writeDoubleList(
+ manifest.numberAt(pos),
+ (List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ SchemaUtil.writeFloatList(
+ manifest.numberAt(pos),
+ (List<Float>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 36: //INT64_LIST_PACKED:
+ SchemaUtil.writeInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ SchemaUtil.writeUInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 38: //INT32_LIST_PACKED:
+ SchemaUtil.writeInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ SchemaUtil.writeFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ SchemaUtil.writeFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ SchemaUtil.writeBoolList(
+ manifest.numberAt(pos),
+ (List<Boolean>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ SchemaUtil.writeUInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ SchemaUtil.writeEnumList(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ SchemaUtil.writeSFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ SchemaUtil.writeSFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ SchemaUtil.writeSInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ SchemaUtil.writeSInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case -4: //GROUP:
+ if (manifest.isFieldPresent(message, pos)) {
+ writer.writeGroup(
+ manifest.numberAt(pos), UnsafeUtil.getObject(message, offset(typeAndOffset)));
+ }
+ break;
+ case -3: //GROUP_LIST:
+ SchemaUtil.writeGroupList(
+ manifest.numberAt(pos),
+ (List<?>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ default:
+ // Assume it's an empty entry - just go to the next entry.
+ break;
+ }
+ }
+ }
+
+ private void writeString(int fieldNumber, Object value, Writer writer) {
+ if (value instanceof String) {
+ writer.writeString(fieldNumber, (String) value);
+ } else {
+ writer.writeBytes(fieldNumber, (ByteString) value);
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProto2StandardSchema.java b/java/core/src/main/java/com/google/protobuf/AbstractProto2StandardSchema.java
new file mode 100644
index 00000000..46a58a7b
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProto2StandardSchema.java
@@ -0,0 +1,127 @@
+// 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.Proto2Manifest.FIELD_LENGTH;
+import static com.google.protobuf.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+import java.util.Collections;
+
+/** Abtract base class for standard (i.e. non-lite) proto2 schemas. */
+abstract class AbstractProto2StandardSchema<T> extends AbstractProto2Schema<T> {
+ final Class<?> messageClass;
+
+ public AbstractProto2StandardSchema(Class<T> messageClass, Proto2Manifest manifest) {
+ super(manifest);
+ this.messageClass = messageClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final T newInstance() {
+ T msg = (T) UnsafeUtil.allocateInstance(messageClass);
+ for (long pos = manifest.address; pos < manifest.limit; pos += FIELD_LENGTH) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ case 1: //FLOAT:
+ case 2: //INT64:
+ case 3: //UINT64:
+ case 4: //INT32:
+ case 5: //FIXED64:
+ case 6: //FIXED32:
+ case 7: //BOOL:
+ case 9: //MESSAGE:
+ case 11: //UINT32:
+ case 12: //ENUM:
+ case 13: //SFIXED32:
+ case 14: //SFIXED64:
+ case 15: //SINT32:
+ case 16: //SINT64:
+ // Do nothing, just use default values.
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), "");
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ByteString.EMPTY);
+ break;
+ case 17: //DOUBLE_LIST:
+ case 18: //FLOAT_LIST:
+ case 19: //INT64_LIST:
+ case 20: //UINT64_LIST:
+ case 21: //INT32_LIST:
+ case 22: //FIXED64_LIST:
+ case 23: //FIXED32_LIST:
+ case 24: //BOOL_LIST:
+ case 26: //MESSAGE_LIST:
+ case 27: //BYTES_LIST:
+ case 28: //UINT32_LIST:
+ case 29: //ENUM_LIST:
+ case 30: //SFIXED32_LIST:
+ case 31: //SFIXED64_LIST:
+ case 32: //SINT32_LIST:
+ case 33: //SINT64_LIST:
+ case 34: //DOUBLE_LIST_PACKED:
+ case 35: //FLOAT_LIST_PACKED:
+ case 36: //INT64_LIST_PACKED:
+ case 37: //UINT64_LIST_PACKED:
+ case 38: //INT32_LIST_PACKED:
+ case 39: //FIXED64_LIST_PACKED:
+ case 40: //FIXED32_LIST_PACKED:
+ case 41: //BOOL_LIST_PACKED:
+ case 42: //UINT32_LIST_PACKED:
+ case 43: //ENUM_LIST_PACKED:
+ case 44: //SFIXED32_LIST_PACKED:
+ case 45: //SFIXED64_LIST_PACKED:
+ case 46: //SINT32_LIST_PACKED:
+ case 47: //SINT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), Collections.emptyList());
+ break;
+ case 25: //STRING_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LazyStringArrayList.EMPTY);
+ break;
+ case -4: //GROUP
+ break;
+ case -3: //GROUP_LIST
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), Collections.emptyList());
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Initialize the base class fields.
+ SchemaUtil.initBaseClassFields(msg);
+ return msg;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProto3LiteSchema.java b/java/core/src/main/java/com/google/protobuf/AbstractProto3LiteSchema.java
new file mode 100644
index 00000000..ebb62ef9
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProto3LiteSchema.java
@@ -0,0 +1,178 @@
+// 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.Proto3Manifest.FIELD_LENGTH;
+import static com.google.protobuf.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+/** Base class for all proto3-lite-based schemas. */
+abstract class AbstractProto3LiteSchema<T> extends AbstractProto3Schema<T> {
+ final Class<?> messageClass;
+
+ public AbstractProto3LiteSchema(Class<T> messageClass, Proto3Manifest manifest) {
+ super(manifest);
+ this.messageClass = messageClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final T newInstance() {
+ T msg = (T) UnsafeUtil.allocateInstance(messageClass);
+ for (long pos = manifest.address; pos < manifest.limit; pos += FIELD_LENGTH) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ case 1: //FLOAT:
+ case 2: //INT64:
+ case 3: //UINT64:
+ case 4: //INT32:
+ case 5: //FIXED64:
+ case 6: //FIXED32:
+ case 7: //BOOL:
+ case 9: //MESSAGE:
+ case 11: //UINT32:
+ case 12: //ENUM:
+ case 13: //SFIXED32:
+ case 14: //SFIXED64:
+ case 15: //SINT32:
+ case 16: //SINT64:
+ // Do nothing, just use default values.
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), "");
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ByteString.EMPTY);
+ break;
+ case 17: //DOUBLE_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), DoubleArrayList.emptyList());
+ break;
+ case 18: //FLOAT_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), FloatArrayList.emptyList());
+ break;
+ case 19: //INT64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 20: //UINT64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 21: //INT32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 22: //FIXED64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 23: //FIXED32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 24: //BOOL_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), BooleanArrayList.emptyList());
+ break;
+ case 25: //STRING_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LazyStringArrayList.EMPTY);
+ break;
+ case 26: //MESSAGE_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ProtobufArrayList.emptyList());
+ break;
+ case 27: //BYTES_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ProtobufArrayList.emptyList());
+ break;
+ case 28: //UINT32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 29: //ENUM_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 30: //SFIXED32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 31: //SFIXED64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 32: //SINT32_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 33: //SINT64_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), DoubleArrayList.emptyList());
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), FloatArrayList.emptyList());
+ break;
+ case 36: //INT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 38: //INT32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), BooleanArrayList.emptyList());
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), IntArrayList.emptyList());
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LongArrayList.emptyList());
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Initialize the base class fields.
+ SchemaUtil.initLiteBaseClassFields(msg);
+ return msg;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProto3Schema.java b/java/core/src/main/java/com/google/protobuf/AbstractProto3Schema.java
new file mode 100644
index 00000000..e52b28f0
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProto3Schema.java
@@ -0,0 +1,347 @@
+// 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.Proto3Manifest.FIELD_LENGTH;
+import static com.google.protobuf.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+import java.util.List;
+
+/** Abstract base class for all proto3-based schemas. */
+abstract class AbstractProto3Schema<T> implements Schema<T> {
+ protected final Proto3Manifest manifest;
+
+ public AbstractProto3Schema(Proto3Manifest manifest) {
+ this.manifest = manifest;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final void writeTo(T message, Writer writer) {
+ for (long pos = manifest.address; pos < manifest.limit; pos += FIELD_LENGTH) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+
+ // Benchmarks have shown that switching on a byte is faster than an enum.
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ SchemaUtil.writeDouble(
+ manifest.numberAt(pos), UnsafeUtil.getDouble(message, offset(typeAndOffset)), writer);
+ break;
+ case 1: //FLOAT:
+ SchemaUtil.writeFloat(
+ manifest.numberAt(pos), UnsafeUtil.getFloat(message, offset(typeAndOffset)), writer);
+ break;
+ case 2: //INT64:
+ SchemaUtil.writeInt64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)), writer);
+ break;
+ case 3: //UINT64:
+ SchemaUtil.writeUInt64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)), writer);
+ break;
+ case 4: //INT32:
+ SchemaUtil.writeInt32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)), writer);
+ break;
+ case 5: //FIXED64:
+ SchemaUtil.writeFixed64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)), writer);
+ break;
+ case 6: //FIXED32:
+ SchemaUtil.writeFixed32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)), writer);
+ break;
+ case 7: //BOOL:
+ SchemaUtil.writeBool(
+ manifest.numberAt(pos),
+ UnsafeUtil.getBoolean(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 8: //STRING:
+ SchemaUtil.writeString(
+ manifest.numberAt(pos), UnsafeUtil.getObject(message, offset(typeAndOffset)), writer);
+ break;
+ case 9: //MESSAGE:
+ SchemaUtil.writeMessage(
+ manifest.numberAt(pos), UnsafeUtil.getObject(message, offset(typeAndOffset)), writer);
+ break;
+ case 10: //BYTES:
+ SchemaUtil.writeBytes(
+ manifest.numberAt(pos),
+ (ByteString) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 11: //UINT32:
+ SchemaUtil.writeUInt32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)), writer);
+ break;
+ case 12: //ENUM:
+ SchemaUtil.writeEnum(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)), writer);
+ break;
+ case 13: //SFIXED32:
+ SchemaUtil.writeSFixed32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)), writer);
+ break;
+ case 14: //SFIXED64:
+ SchemaUtil.writeSFixed64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)), writer);
+ break;
+ case 15: //SINT32:
+ SchemaUtil.writeSInt32(
+ manifest.numberAt(pos), UnsafeUtil.getInt(message, offset(typeAndOffset)), writer);
+ break;
+ case 16: //SINT64:
+ SchemaUtil.writeSInt64(
+ manifest.numberAt(pos), UnsafeUtil.getLong(message, offset(typeAndOffset)), writer);
+ break;
+ case 17: //DOUBLE_LIST:
+ SchemaUtil.writeDoubleList(
+ manifest.numberAt(pos),
+ (List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 18: //FLOAT_LIST:
+ SchemaUtil.writeFloatList(
+ manifest.numberAt(pos),
+ (List<Float>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 19: //INT64_LIST:
+ SchemaUtil.writeInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 20: //UINT64_LIST:
+ SchemaUtil.writeUInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 21: //INT32_LIST:
+ SchemaUtil.writeInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 22: //FIXED64_LIST:
+ SchemaUtil.writeFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 23: //FIXED32_LIST:
+ SchemaUtil.writeFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 24: //BOOL_LIST:
+ SchemaUtil.writeBoolList(
+ manifest.numberAt(pos),
+ (List<Boolean>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 25: //STRING_LIST:
+ SchemaUtil.writeStringList(
+ manifest.numberAt(pos),
+ (List<String>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.writeMessageList(
+ manifest.numberAt(pos),
+ (List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 27: //BYTES_LIST:
+ SchemaUtil.writeBytesList(
+ manifest.numberAt(pos),
+ (List<ByteString>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer);
+ break;
+ case 28: //UINT32_LIST:
+ SchemaUtil.writeUInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 29: //ENUM_LIST:
+ SchemaUtil.writeEnumList(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 30: //SFIXED32_LIST:
+ SchemaUtil.writeSFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 31: //SFIXED64_LIST:
+ SchemaUtil.writeSFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 32: //SINT32_LIST:
+ SchemaUtil.writeSInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 33: //SINT64_LIST:
+ SchemaUtil.writeSInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ false);
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ SchemaUtil.writeDoubleList(
+ manifest.numberAt(pos),
+ (List<Double>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ SchemaUtil.writeFloatList(
+ manifest.numberAt(pos),
+ (List<Float>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 36: //INT64_LIST_PACKED:
+ SchemaUtil.writeInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ SchemaUtil.writeUInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 38: //INT32_LIST_PACKED:
+ SchemaUtil.writeInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ SchemaUtil.writeFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ SchemaUtil.writeFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ SchemaUtil.writeBoolList(
+ manifest.numberAt(pos),
+ (List<Boolean>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ SchemaUtil.writeUInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ SchemaUtil.writeEnumList(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ SchemaUtil.writeSFixed32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ SchemaUtil.writeSFixed64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ SchemaUtil.writeSInt32List(
+ manifest.numberAt(pos),
+ (List<Integer>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ SchemaUtil.writeSInt64List(
+ manifest.numberAt(pos),
+ (List<Long>) UnsafeUtil.getObject(message, offset(typeAndOffset)),
+ writer,
+ true);
+ break;
+ default:
+ // Assume it's an empty entry - just go to the next entry.
+ break;
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProto3StandardSchema.java b/java/core/src/main/java/com/google/protobuf/AbstractProto3StandardSchema.java
new file mode 100644
index 00000000..3c4f5072
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProto3StandardSchema.java
@@ -0,0 +1,124 @@
+// 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.Proto3Manifest.FIELD_LENGTH;
+import static com.google.protobuf.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+import java.util.Collections;
+
+/**
+ * Abtract base class for standard (i.e. non-lite) proto3 schemas.
+ */
+abstract class AbstractProto3StandardSchema<T> extends AbstractProto3Schema<T> {
+ final Class<?> messageClass;
+
+ public AbstractProto3StandardSchema(Class<T> messageClass, Proto3Manifest manifest) {
+ super(manifest);
+ this.messageClass = messageClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final T newInstance() {
+ T msg = (T) UnsafeUtil.allocateInstance(messageClass);
+ for (long pos = manifest.address; pos < manifest.limit; pos += FIELD_LENGTH) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ case 1: //FLOAT:
+ case 2: //INT64:
+ case 3: //UINT64:
+ case 4: //INT32:
+ case 5: //FIXED64:
+ case 6: //FIXED32:
+ case 7: //BOOL:
+ case 9: //MESSAGE:
+ case 11: //UINT32:
+ case 12: //ENUM:
+ case 13: //SFIXED32:
+ case 14: //SFIXED64:
+ case 15: //SINT32:
+ case 16: //SINT64:
+ // Do nothing, just use default values.
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), "");
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), ByteString.EMPTY);
+ break;
+ case 17: //DOUBLE_LIST:
+ case 18: //FLOAT_LIST:
+ case 19: //INT64_LIST:
+ case 20: //UINT64_LIST:
+ case 21: //INT32_LIST:
+ case 22: //FIXED64_LIST:
+ case 23: //FIXED32_LIST:
+ case 24: //BOOL_LIST:
+ case 26: //MESSAGE_LIST:
+ case 27: //BYTES_LIST:
+ case 28: //UINT32_LIST:
+ case 29: //ENUM_LIST:
+ case 30: //SFIXED32_LIST:
+ case 31: //SFIXED64_LIST:
+ case 32: //SINT32_LIST:
+ case 33: //SINT64_LIST:
+ case 34: //DOUBLE_LIST_PACKED:
+ case 35: //FLOAT_LIST_PACKED:
+ case 36: //INT64_LIST_PACKED:
+ case 37: //UINT64_LIST_PACKED:
+ case 38: //INT32_LIST_PACKED:
+ case 39: //FIXED64_LIST_PACKED:
+ case 40: //FIXED32_LIST_PACKED:
+ case 41: //BOOL_LIST_PACKED:
+ case 42: //UINT32_LIST_PACKED:
+ case 43: //ENUM_LIST_PACKED:
+ case 44: //SFIXED32_LIST_PACKED:
+ case 45: //SFIXED64_LIST_PACKED:
+ case 46: //SINT32_LIST_PACKED:
+ case 47: //SINT64_LIST_PACKED:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), Collections.emptyList());
+ break;
+ case 25: //STRING_LIST:
+ UnsafeUtil.putObject(msg, offset(typeAndOffset), LazyStringArrayList.EMPTY);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Initialize the base class fields.
+ SchemaUtil.initBaseClassFields(msg);
+ return msg;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java b/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java
new file mode 100644
index 00000000..4c822348
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/AllocatedBuffer.java
@@ -0,0 +1,263 @@
+// 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.checkNotNull;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A buffer that was allocated by a {@link BufferAllocator}. For every buffer, it is guaranteed that
+ * at least one of {@link #hasArray()} or {@link #hasNioBuffer()} will be {@code true}.
+ */
+@ExperimentalApi
+public abstract class AllocatedBuffer {
+ /**
+ * Indicates whether this buffer contains a backing {@link ByteBuffer} (i.e. it is safe to call
+ * {@link #nioBuffer()}).
+ */
+ public abstract boolean hasNioBuffer();
+
+ /**
+ * Indicates whether this buffer contains a backing array (i.e. it is safe to call {@link
+ * #array()}).
+ */
+ public abstract boolean hasArray();
+
+ /**
+ * Returns the {@link ByteBuffer} that backs this buffer <i>(optional operation)</i>.
+ *
+ * <p>Call {@link #hasNioBuffer()} before invoking this method in order to ensure that this buffer
+ * has a backing {@link ByteBuffer}.
+ *
+ * @return The {@link ByteBuffer} that backs this buffer
+ * @throws UnsupportedOperationException If this buffer is not backed by a {@link ByteBuffer}.
+ */
+ public abstract ByteBuffer nioBuffer();
+
+ /**
+ * Returns the byte array that backs this buffer <i>(optional operation)</i>.
+ *
+ * <p>Call {@link #hasArray()} before invoking this method in order to ensure that this buffer has
+ * an accessible backing array.
+ *
+ * @return The array that backs this buffer
+ * @throws java.nio.ReadOnlyBufferException If this buffer is backed by an array but is read-only
+ * @throws UnsupportedOperationException If this buffer is not backed by an accessible array
+ */
+ public abstract byte[] array();
+
+ /**
+ * Returns the offset within this buffer's backing array of the first element of the buffer
+ * <i>(optional operation)</i>.
+ *
+ * <p>If this buffer is backed by an array then {@link #position()} corresponds to the array index
+ * {@link #position()} {@code +} {@link #arrayOffset()}.
+ *
+ * <p>Invoke the {@link #hasArray hasArray} method before invoking this method in order to ensure
+ * that this buffer has an accessible backing array.
+ *
+ * @return The offset within this buffer's array of the first element of the buffer
+ * @throws java.nio.ReadOnlyBufferException If this buffer is backed by an array but is read-only
+ * @throws UnsupportedOperationException If this buffer is not backed by an accessible array
+ */
+ public abstract int arrayOffset();
+
+ /**
+ * Returns this buffer's position.
+ *
+ * @return The position of this buffer
+ */
+ public abstract int position();
+
+ /**
+ * Sets this buffer's position.
+ *
+ * @param position The new position value; must be non-negative and no larger than the current
+ * limit
+ * @return This buffer
+ * @throws IllegalArgumentException If the preconditions on {@code position} do not hold
+ */
+ public abstract AllocatedBuffer position(int position);
+
+ /**
+ * Returns this buffer's limit.
+ *
+ * @return The limit of this buffer
+ */
+ public abstract int limit();
+
+ /**
+ * Returns the number of elements between the current {@link #position()} and the {@link #limit()}
+ * .
+ *
+ * @return The number of elements remaining in this buffer
+ */
+ public abstract int remaining();
+
+ /**
+ * Creates a new {@link AllocatedBuffer} that is backed by the given array. The returned buffer
+ * will have {@link #hasArray} == {@code true}, {@link #arrayOffset()} == {@code 0}, {@link
+ * #position()} == {@code 0} and {@link #limit()} equal to the length of {@code bytes}.
+ */
+ public static AllocatedBuffer wrap(byte[] bytes) {
+ return wrapNoCheck(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Creates a new {@link AllocatedBuffer} that is backed by the given array. The returned buffer
+ * will have {@link #hasArray} == {@code true}, {@link #arrayOffset()} == {@code offset}, {@link
+ * #position()} == {@code 0} and {@link #limit()} == {@code length}.
+ */
+ public static AllocatedBuffer wrap(final byte[] bytes, final int offset, final int length) {
+ if (offset < 0 || length < 0 || (offset + length) > bytes.length) {
+ throw new IndexOutOfBoundsException(
+ String.format("bytes.length=%d, offset=%d, lenght=%d", bytes.length, offset, length));
+ }
+
+ return wrapNoCheck(bytes, offset, length);
+ }
+
+ /**
+ * Creates a new {@link AllocatedBuffer} that is backed by the given {@link ByteBuffer}. The
+ * returned buffer will have {@link #hasNioBuffer} == {@code true}.
+ */
+ public static AllocatedBuffer wrap(final ByteBuffer buffer) {
+ checkNotNull(buffer, "buffer");
+
+ return new AllocatedBuffer() {
+
+ @Override
+ public boolean hasNioBuffer() {
+ return true;
+ }
+
+ @Override
+ public ByteBuffer nioBuffer() {
+ return buffer;
+ }
+
+ @Override
+ public boolean hasArray() {
+ return buffer.hasArray();
+ }
+
+ @Override
+ public byte[] array() {
+ return buffer.array();
+ }
+
+ @Override
+ public int arrayOffset() {
+ return buffer.arrayOffset();
+ }
+
+ @Override
+ public int position() {
+ return buffer.position();
+ }
+
+ @Override
+ public AllocatedBuffer position(int position) {
+ buffer.position(position);
+ return this;
+ }
+
+ @Override
+ public int limit() {
+ return buffer.limit();
+ }
+
+ @Override
+ public int remaining() {
+ return buffer.remaining();
+ }
+ };
+ }
+
+ private static AllocatedBuffer wrapNoCheck(
+ final byte[] bytes, final int offset, final int length) {
+ return new AllocatedBuffer() {
+ // Relative to offset.
+ private int position;
+
+ @Override
+ public boolean hasNioBuffer() {
+ return false;
+ }
+
+ @Override
+ public ByteBuffer nioBuffer() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasArray() {
+ return true;
+ }
+
+ @Override
+ public byte[] array() {
+ return bytes;
+ }
+
+ @Override
+ public int arrayOffset() {
+ return offset;
+ }
+
+ @Override
+ public int position() {
+ return position;
+ }
+
+ @Override
+ public AllocatedBuffer position(int position) {
+ if (position < 0 || position > length) {
+ throw new IllegalArgumentException("Invalid position: " + position);
+ }
+ this.position = position;
+ return this;
+ }
+
+ @Override
+ public int limit() {
+ // Relative to offset.
+ return length;
+ }
+
+ @Override
+ public int remaining() {
+ return length - position;
+ }
+ };
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/BinaryProtocolUtil.java b/java/core/src/main/java/com/google/protobuf/BinaryProtocolUtil.java
new file mode 100644
index 00000000..8143785e
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/BinaryProtocolUtil.java
@@ -0,0 +1,117 @@
+// 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;
+
+/** Common methods and constants for the binary protobuf protocol. */
+@ExperimentalApi
+final class BinaryProtocolUtil {
+ private BinaryProtocolUtil() {}
+
+ private static final int TAG_TYPE_BITS = 3;
+ private static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
+
+ static final byte WIRETYPE_VARINT = 0;
+ static final byte WIRETYPE_FIXED64 = 1;
+ static final byte WIRETYPE_LENGTH_DELIMITED = 2;
+ static final byte WIRETYPE_START_GROUP = 3;
+ static final byte WIRETYPE_END_GROUP = 4;
+ static final byte WIRETYPE_FIXED32 = 5;
+ static final byte MAX_VARINT32_SIZE = 5;
+ static final byte MAX_VARINT64_SIZE = 10;
+ static final byte FIXED32_SIZE = 4;
+ static final byte FIXED64_SIZE = 8;
+
+ static int tagFor(int fieldNumber, byte wireType) {
+ return (fieldNumber << TAG_TYPE_BITS) | wireType;
+ }
+
+ static int getFieldNumber(final int tag) {
+ return tag >>> TAG_TYPE_BITS;
+ }
+
+ static int getWireType(final int tag) {
+ return tag & TAG_TYPE_MASK;
+ }
+
+ /**
+ * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers into values that can be
+ * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
+ * to be varint encoded, thus always taking 10 bytes on the wire.)
+ *
+ * @param n An unsigned 32-bit integer, stored in a signed int because Java has no explicit
+ * unsigned support.
+ * @return A signed 32-bit integer.
+ */
+ static int decodeZigZag32(final int n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be
+ * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
+ * to be varint encoded, thus always taking 10 bytes on the wire.)
+ *
+ * @param n An unsigned 64-bit integer, stored in a signed int because Java has no explicit
+ * unsigned support.
+ * @return A signed 64-bit integer.
+ */
+ static long decodeZigZag64(final long n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers into values that can be
+ * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
+ * to be varint encoded, thus always taking 10 bytes on the wire.)
+ *
+ * @param n A signed 32-bit integer.
+ * @return An unsigned 32-bit integer, stored in a signed int because Java has no explicit
+ * unsigned support.
+ */
+ static int encodeZigZag32(final int n) {
+ // Note: the right-shift must be arithmetic
+ return (n << 1) ^ (n >> 31);
+ }
+
+ /**
+ * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be
+ * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
+ * to be varint encoded, thus always taking 10 bytes on the wire.)
+ *
+ * @param n A signed 64-bit integer.
+ * @return An unsigned 64-bit integer, stored in a signed int because Java has no explicit
+ * unsigned support.
+ */
+ static long encodeZigZag64(final long n) {
+ // Note: the right-shift must be arithmetic
+ return (n << 1) ^ (n >> 63);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/BinaryReader.java b/java/core/src/main/java/com/google/protobuf/BinaryReader.java
new file mode 100644
index 00000000..ab20c619
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/BinaryReader.java
@@ -0,0 +1,1516 @@
+// 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.BinaryProtocolUtil.FIXED32_SIZE;
+import static com.google.protobuf.BinaryProtocolUtil.FIXED64_SIZE;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_END_GROUP;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_FIXED32;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_FIXED64;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_LENGTH_DELIMITED;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_START_GROUP;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_VARINT;
+import static com.google.protobuf.BinaryProtocolUtil.decodeZigZag32;
+import static com.google.protobuf.BinaryProtocolUtil.decodeZigZag64;
+import static com.google.protobuf.BinaryProtocolUtil.getWireType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * A {@link Reader} that reads from a buffer containing a message serialized with the binary
+ * protocol.
+ */
+@ExperimentalApi
+public abstract class BinaryReader implements Reader {
+ private static final int FIXED32_MULTIPLE_MASK = FIXED32_SIZE - 1;
+ private static final int FIXED64_MULTIPLE_MASK = FIXED64_SIZE - 1;
+
+ /**
+ * Creates a new reader using the given {@code buffer} as input.
+ *
+ * @param buffer the input buffer. The buffer (including position, limit, etc.) will not be
+ * modified. To increment the buffer position after the read completes, use the value returned
+ * by {@link #getTotalBytesRead()}.
+ * @param bufferIsImmutable if {@code true} the reader assumes that the content of {@code buffer}
+ * will never change and any allocated {@link ByteString} instances will by directly wrap
+ * slices of {@code buffer}.
+ * @return the reader
+ */
+ public static BinaryReader newInstance(ByteBuffer buffer, boolean bufferIsImmutable) {
+ if (buffer.hasArray()) {
+ // TODO(nathanmittler): Add support for unsafe operations.
+ return new SafeHeapReader(buffer, bufferIsImmutable);
+ }
+ // TODO(nathanmittler): Add support for direct buffers
+ throw new IllegalArgumentException("Direct buffers not yet supported");
+ }
+
+ /** Only allow subclassing for inner classes. */
+ private BinaryReader() {}
+
+ /** Returns the total number of bytes read so far from the input buffer. */
+ public abstract int getTotalBytesRead();
+
+ /**
+ * A {@link BinaryReader} implementation that operates on a heap {@link ByteBuffer}. Uses only
+ * safe operations on the underlying array.
+ */
+ private static final class SafeHeapReader extends BinaryReader {
+ private final boolean bufferIsImmutable;
+ private final byte[] buffer;
+ private int pos;
+ private final int initialPos;
+ private int limit;
+ private int tag;
+ private int endGroupTag;
+
+ public SafeHeapReader(ByteBuffer bytebuf, boolean bufferIsImmutable) {
+ this.bufferIsImmutable = bufferIsImmutable;
+ buffer = bytebuf.array();
+ initialPos = pos = bytebuf.arrayOffset() + bytebuf.position();
+ limit = bytebuf.arrayOffset() + bytebuf.limit();
+ }
+
+ private boolean isAtEnd() {
+ return pos == limit;
+ }
+
+ @Override
+ public int getTotalBytesRead() {
+ return pos - initialPos;
+ }
+
+ @Override
+ public int getFieldNumber() throws IOException {
+ if (isAtEnd()) {
+ return Reader.READ_DONE;
+ }
+ tag = readVarint32();
+ if (tag == endGroupTag) {
+ return Reader.READ_DONE;
+ }
+ return BinaryProtocolUtil.getFieldNumber(tag);
+ }
+
+ @Override
+ public boolean skipField() throws IOException {
+ if (isAtEnd() || tag == endGroupTag) {
+ return false;
+ }
+
+ switch (getWireType(tag)) {
+ case WIRETYPE_VARINT:
+ skipVarint();
+ return true;
+ case WIRETYPE_FIXED64:
+ skipBytes(FIXED64_SIZE);
+ return true;
+ case WIRETYPE_LENGTH_DELIMITED:
+ skipBytes(readVarint32());
+ return true;
+ case WIRETYPE_FIXED32:
+ skipBytes(FIXED32_SIZE);
+ return true;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public double readDouble() throws IOException {
+ requireWireType(WIRETYPE_FIXED64);
+ return Double.longBitsToDouble(readLittleEndian64());
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ requireWireType(WIRETYPE_FIXED32);
+ return Float.intBitsToFloat(readLittleEndian32());
+ }
+
+ @Override
+ public long readUInt64() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return readVarint64();
+ }
+
+ @Override
+ public long readInt64() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return readVarint64();
+ }
+
+ @Override
+ public int readInt32() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return readVarint32();
+ }
+
+ @Override
+ public long readFixed64() throws IOException {
+ requireWireType(WIRETYPE_FIXED64);
+ return readLittleEndian64();
+ }
+
+ @Override
+ public int readFixed32() throws IOException {
+ requireWireType(WIRETYPE_FIXED32);
+ return readLittleEndian32();
+ }
+
+ @Override
+ public boolean readBool() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return readVarint32() != 0;
+ }
+
+ @Override
+ public String readString() throws IOException {
+ requireWireType(WIRETYPE_LENGTH_DELIMITED);
+ final int size = readVarint32();
+ if (size == 0) {
+ return "";
+ }
+
+ requireBytes(size);
+ // TODO(nathanmittler): need to make this an option.
+ if (!Utf8.isValidUtf8(buffer, pos, pos + size)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ String result = new String(buffer, pos, size, Internal.UTF_8);
+ pos += size;
+ return result;
+ }
+
+ @Override
+ public <T> T readMessage(Class<T> clazz) throws IOException {
+ requireWireType(WIRETYPE_LENGTH_DELIMITED);
+ return readMessage(Protobuf.getInstance().schemaFor(clazz));
+ }
+
+ private <T> T readMessage(Schema<T> schema) throws IOException {
+ int size = readVarint32();
+ requireBytes(size);
+
+ // Update the limit.
+ int prevLimit = limit;
+ int newLimit = pos + size;
+ limit = newLimit;
+
+ try {
+ // Allocate and read the message.
+ T message = schema.newInstance();
+ schema.mergeFrom(message, this);
+
+ if (pos != newLimit) {
+ throw InvalidProtocolBufferException.parseFailure();
+ }
+ return message;
+ } finally {
+ // Restore the limit.
+ limit = prevLimit;
+ }
+ }
+
+ @Override
+ public <T> T readGroup(Class<T> clazz) throws IOException {
+ requireWireType(WIRETYPE_START_GROUP);
+ return readGroup(Protobuf.getInstance().schemaFor(clazz));
+ }
+
+ private <T> T readGroup(Schema<T> schema) throws IOException {
+ int prevEndGroupTag = endGroupTag;
+ endGroupTag =
+ BinaryProtocolUtil.tagFor(BinaryProtocolUtil.getFieldNumber(tag), WIRETYPE_END_GROUP);
+
+ try {
+ // Allocate and read the message.
+ T message = schema.newInstance();
+ schema.mergeFrom(message, this);
+
+ if (tag != endGroupTag) {
+ throw InvalidProtocolBufferException.parseFailure();
+ }
+ return message;
+ } finally {
+ // Restore the old end group tag.
+ endGroupTag = prevEndGroupTag;
+ }
+ }
+
+ @Override
+ public ByteString readBytes() throws IOException {
+ requireWireType(WIRETYPE_LENGTH_DELIMITED);
+ int size = readVarint32();
+ if (size == 0) {
+ return ByteString.EMPTY;
+ }
+
+ requireBytes(size);
+ ByteString bytes =
+ bufferIsImmutable
+ ? ByteString.wrap(buffer, pos, size)
+ : ByteString.copyFrom(buffer, pos, size);
+ pos += size;
+ return bytes;
+ }
+
+ @Override
+ public int readUInt32() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return readVarint32();
+ }
+
+ @Override
+ public int readEnum() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return readVarint32();
+ }
+
+ @Override
+ public int readSFixed32() throws IOException {
+ requireWireType(WIRETYPE_FIXED32);
+ return readLittleEndian32();
+ }
+
+ @Override
+ public long readSFixed64() throws IOException {
+ requireWireType(WIRETYPE_FIXED64);
+ return readLittleEndian64();
+ }
+
+ @Override
+ public int readSInt32() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return decodeZigZag32(readVarint32());
+ }
+
+ @Override
+ public long readSInt64() throws IOException {
+ requireWireType(WIRETYPE_VARINT);
+ return decodeZigZag64(readVarint64());
+ }
+
+ @Override
+ public void readDoubleList(List<Double> target) throws IOException {
+ if (target instanceof DoubleArrayList) {
+ DoubleArrayList plist = (DoubleArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed64Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addDouble(Double.longBitsToDouble(readLittleEndian64_NoCheck()));
+ }
+ break;
+ case WIRETYPE_FIXED64:
+ while (true) {
+ plist.addDouble(readDouble());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed64Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(Double.longBitsToDouble(readLittleEndian64_NoCheck()));
+ }
+ break;
+ case WIRETYPE_FIXED64:
+ while (true) {
+ target.add(readDouble());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readFloatList(List<Float> target) throws IOException {
+ if (target instanceof FloatArrayList) {
+ FloatArrayList plist = (FloatArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed32Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addFloat(Float.intBitsToFloat(readLittleEndian32_NoCheck()));
+ }
+ break;
+ case WIRETYPE_FIXED32:
+ while (true) {
+ plist.addFloat(readFloat());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed32Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(Float.intBitsToFloat(readLittleEndian32_NoCheck()));
+ }
+ break;
+ case WIRETYPE_FIXED32:
+ while (true) {
+ target.add(readFloat());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readUInt64List(List<Long> target) throws IOException {
+ if (target instanceof LongArrayList) {
+ LongArrayList plist = (LongArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addLong(readVarint64());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addLong(readUInt64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readVarint64());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readUInt64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readInt64List(List<Long> target) throws IOException {
+ if (target instanceof LongArrayList) {
+ LongArrayList plist = (LongArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addLong(readVarint64());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addLong(readInt64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readVarint64());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readInt64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readInt32List(List<Integer> target) throws IOException {
+ if (target instanceof IntArrayList) {
+ IntArrayList plist = (IntArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addInt(readVarint32());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addInt(readInt32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readVarint32());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readInt32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readFixed64List(List<Long> target) throws IOException {
+ if (target instanceof LongArrayList) {
+ LongArrayList plist = (LongArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed64Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addLong(readLittleEndian64_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED64:
+ while (true) {
+ plist.addLong(readFixed64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed64Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readLittleEndian64_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED64:
+ while (true) {
+ target.add(readFixed64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readFixed32List(List<Integer> target) throws IOException {
+ if (target instanceof IntArrayList) {
+ IntArrayList plist = (IntArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed32Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addInt(readLittleEndian32_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED32:
+ while (true) {
+ plist.addInt(readFixed32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed32Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readLittleEndian32_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED32:
+ while (true) {
+ target.add(readFixed32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readBoolList(List<Boolean> target) throws IOException {
+ if (target instanceof BooleanArrayList) {
+ BooleanArrayList plist = (BooleanArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addBoolean(readVarint32() != 0);
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addBoolean(readBool());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readVarint32() != 0);
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readBool());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readStringList(List<String> target) throws IOException {
+ if (getWireType(tag) != WIRETYPE_LENGTH_DELIMITED) {
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+
+ while (true) {
+ target.add(readString());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ }
+
+ @Override
+ public <T> void readMessageList(List<T> target, Class<T> targetType) throws IOException {
+ if (getWireType(tag) != WIRETYPE_LENGTH_DELIMITED) {
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+
+ final Schema<T> schema = Protobuf.getInstance().schemaFor(targetType);
+ final int listTag = tag;
+ while (true) {
+ target.add(readMessage(schema));
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != listTag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ }
+
+ @Override
+ public <T> void readGroupList(List<T> target, Class<T> targetType) throws IOException {
+ if (getWireType(tag) != WIRETYPE_START_GROUP) {
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+
+ final Schema<T> schema = Protobuf.getInstance().schemaFor(targetType);
+ final int listTag = tag;
+ while (true) {
+ target.add(readGroup(schema));
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != listTag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void readBytesList(List<ByteString> target) throws IOException {
+ if (getWireType(tag) != WIRETYPE_LENGTH_DELIMITED) {
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+
+ while (true) {
+ target.add(readBytes());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void readUInt32List(List<Integer> target) throws IOException {
+ if (target instanceof IntArrayList) {
+ IntArrayList plist = (IntArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addInt(readVarint32());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addInt(readUInt32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readVarint32());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readUInt32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readEnumList(List<Integer> target) throws IOException {
+ if (target instanceof IntArrayList) {
+ IntArrayList plist = (IntArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addInt(readVarint32());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addInt(readEnum());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readVarint32());
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readEnum());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readSFixed32List(List<Integer> target) throws IOException {
+ if (target instanceof IntArrayList) {
+ IntArrayList plist = (IntArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed32Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addInt(readLittleEndian32_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED32:
+ while (true) {
+ plist.addInt(readSFixed32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed32Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readLittleEndian32_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED32:
+ while (true) {
+ target.add(readSFixed32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readSFixed64List(List<Long> target) throws IOException {
+ if (target instanceof LongArrayList) {
+ LongArrayList plist = (LongArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed64Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addLong(readLittleEndian64_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED64:
+ while (true) {
+ plist.addLong(readSFixed64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ verifyPackedFixed64Length(bytes);
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(readLittleEndian64_NoCheck());
+ }
+ break;
+ case WIRETYPE_FIXED64:
+ while (true) {
+ target.add(readSFixed64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readSInt32List(List<Integer> target) throws IOException {
+ if (target instanceof IntArrayList) {
+ IntArrayList plist = (IntArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addInt(decodeZigZag32(readVarint32()));
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addInt(readSInt32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(decodeZigZag32(readVarint32()));
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readSInt32());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ @Override
+ public void readSInt64List(List<Long> target) throws IOException {
+ if (target instanceof LongArrayList) {
+ LongArrayList plist = (LongArrayList) target;
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ plist.addLong(decodeZigZag64(readVarint64()));
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ plist.addLong(readSInt64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ } else {
+ switch (getWireType(tag)) {
+ case WIRETYPE_LENGTH_DELIMITED:
+ final int bytes = readVarint32();
+ final int fieldEndPos = pos + bytes;
+ while (pos < fieldEndPos) {
+ target.add(decodeZigZag64(readVarint64()));
+ }
+ break;
+ case WIRETYPE_VARINT:
+ while (true) {
+ target.add(readSInt64());
+
+ if (isAtEnd()) {
+ return;
+ }
+ int prevPos = pos;
+ int nextTag = readVarint32();
+ if (nextTag != tag) {
+ // We've reached the end of the repeated field. Rewind the buffer position to before
+ // the new tag.
+ pos = prevPos;
+ return;
+ }
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+ }
+
+ /** Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits. */
+ private int readVarint32() throws IOException {
+ // See implementation notes for readRawVarint64
+ int i = pos;
+
+ if (limit == pos) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ int x;
+ if ((x = buffer[i++]) >= 0) {
+ pos = i;
+ return x;
+ } else if (limit - i < 9) {
+ return (int) readVarint64SlowPath();
+ } else if ((x ^= (buffer[i++] << 7)) < 0) {
+ x ^= (~0 << 7);
+ } else if ((x ^= (buffer[i++] << 14)) >= 0) {
+ x ^= (~0 << 7) ^ (~0 << 14);
+ } else if ((x ^= (buffer[i++] << 21)) < 0) {
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
+ } else {
+ int y = buffer[i++];
+ x ^= y << 28;
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
+ if (y < 0
+ && buffer[i++] < 0
+ && buffer[i++] < 0
+ && buffer[i++] < 0
+ && buffer[i++] < 0
+ && buffer[i++] < 0) {
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+ }
+ pos = i;
+ return x;
+ }
+
+ public long readVarint64() throws IOException {
+ // Implementation notes:
+ //
+ // Optimized for one-byte values, expected to be common.
+ // The particular code below was selected from various candidates
+ // empirically, by winning VarintBenchmark.
+ //
+ // Sign extension of (signed) Java bytes is usually a nuisance, but
+ // we exploit it here to more easily obtain the sign of bytes read.
+ // Instead of cleaning up the sign extension bits by masking eagerly,
+ // we delay until we find the final (positive) byte, when we clear all
+ // accumulated bits with one xor. We depend on javac to constant fold.
+ int i = pos;
+
+ if (limit == i) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ final byte[] buffer = this.buffer;
+ long x;
+ int y;
+ if ((y = buffer[i++]) >= 0) {
+ pos = i;
+ return y;
+ } else if (limit - i < 9) {
+ return readVarint64SlowPath();
+ } else if ((y ^= (buffer[i++] << 7)) < 0) {
+ x = y ^ (~0 << 7);
+ } else if ((y ^= (buffer[i++] << 14)) >= 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14));
+ } else if ((y ^= (buffer[i++] << 21)) < 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
+ } else if ((x = y ^ ((long) buffer[i++] << 28)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
+ } else if ((x ^= ((long) buffer[i++] << 35)) < 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
+ } else if ((x ^= ((long) buffer[i++] << 42)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
+ } else if ((x ^= ((long) buffer[i++] << 49)) < 0L) {
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49);
+ } else {
+ x ^= ((long) buffer[i++] << 56);
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49)
+ ^ (~0L << 56);
+ if (x < 0L) {
+ if (buffer[i++] < 0L) {
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+ }
+ }
+ pos = i;
+ return x;
+ }
+
+ private long readVarint64SlowPath() throws IOException {
+ long result = 0;
+ for (int shift = 0; shift < 64; shift += 7) {
+ final byte b = readByte();
+ result |= (long) (b & 0x7F) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ private byte readByte() throws IOException {
+ if (pos == limit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ return buffer[pos++];
+ }
+
+ private int readLittleEndian32() throws IOException {
+ requireBytes(FIXED32_SIZE);
+ return readLittleEndian32_NoCheck();
+ }
+
+ private long readLittleEndian64() throws IOException {
+ requireBytes(FIXED64_SIZE);
+ return readLittleEndian64_NoCheck();
+ }
+
+ private int readLittleEndian32_NoCheck() {
+ int p = pos;
+ final byte[] buffer = this.buffer;
+ pos = p + FIXED32_SIZE;
+ return (((buffer[p] & 0xff))
+ | ((buffer[p + 1] & 0xff) << 8)
+ | ((buffer[p + 2] & 0xff) << 16)
+ | ((buffer[p + 3] & 0xff) << 24));
+ }
+
+ private long readLittleEndian64_NoCheck() {
+ int p = pos;
+ final byte[] buffer = this.buffer;
+ pos = p + FIXED64_SIZE;
+ return (((buffer[p] & 0xffL))
+ | ((buffer[p + 1] & 0xffL) << 8)
+ | ((buffer[p + 2] & 0xffL) << 16)
+ | ((buffer[p + 3] & 0xffL) << 24)
+ | ((buffer[p + 4] & 0xffL) << 32)
+ | ((buffer[p + 5] & 0xffL) << 40)
+ | ((buffer[p + 6] & 0xffL) << 48)
+ | ((buffer[p + 7] & 0xffL) << 56));
+ }
+
+ private void skipVarint() throws IOException {
+ if (limit - pos >= 10) {
+ final byte[] buffer = this.buffer;
+ int p = pos;
+ for (int i = 0; i < 10; i++) {
+ if (buffer[p++] >= 0) {
+ pos = p;
+ return;
+ }
+ }
+ }
+ skipVarintSlowPath();
+ }
+
+ private void skipVarintSlowPath() throws IOException {
+ for (int i = 0; i < 10; i++) {
+ if (readByte() >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ private void skipBytes(final int size) throws IOException {
+ requireBytes(size);
+
+ pos += size;
+ }
+
+ private void requireBytes(int size) throws IOException {
+ if (size < 0 || size > (limit - pos)) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ }
+
+ private void requireWireType(int requiredWireType) throws IOException {
+ if (getWireType(tag) != requiredWireType) {
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ private void verifyPackedFixed64Length(int bytes) throws IOException {
+ requireBytes(bytes);
+ if ((bytes & FIXED64_MULTIPLE_MASK) != 0) {
+ // Require that the number of bytes be a multiple of 8.
+ throw InvalidProtocolBufferException.parseFailure();
+ }
+ }
+
+ private void verifyPackedFixed32Length(int bytes) throws IOException {
+ requireBytes(bytes);
+ if ((bytes & FIXED32_MULTIPLE_MASK) != 0) {
+ // Require that the number of bytes be a multiple of 4.
+ throw InvalidProtocolBufferException.parseFailure();
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/BinaryWriter.java b/java/core/src/main/java/com/google/protobuf/BinaryWriter.java
new file mode 100644
index 00000000..2039a556
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/BinaryWriter.java
@@ -0,0 +1,2804 @@
+// 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.BinaryProtocolUtil.FIXED32_SIZE;
+import static com.google.protobuf.BinaryProtocolUtil.FIXED64_SIZE;
+import static com.google.protobuf.BinaryProtocolUtil.MAX_VARINT32_SIZE;
+import static com.google.protobuf.BinaryProtocolUtil.MAX_VARINT64_SIZE;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_END_GROUP;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_FIXED32;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_FIXED64;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_LENGTH_DELIMITED;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_START_GROUP;
+import static com.google.protobuf.BinaryProtocolUtil.WIRETYPE_VARINT;
+import static com.google.protobuf.BinaryProtocolUtil.encodeZigZag32;
+import static com.google.protobuf.BinaryProtocolUtil.encodeZigZag64;
+import static com.google.protobuf.BinaryProtocolUtil.tagFor;
+import static com.google.protobuf.Internal.checkNotNull;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * A protobuf writer that serializes messages in their binary form. Messages are serialized in
+ * reverse in order to avoid calculating the serialized size of each nested message. Since the
+ * message size is not known in advance, the writer employs a strategy of chunking and buffer
+ * chaining. Buffers are allocated as-needed by a provided {@link BufferAllocator}. Once writing is
+ * finished, the application can access the buffers in forward-writing order by calling {@link
+ * #complete()}.
+ *
+ * <p>Once {@link #complete()} has been called, the writer can not be reused for additional writes.
+ * The {@link #getTotalBytesWritten()} will continue to reflect the total of the write and will not
+ * be reset.
+ */
+@ExperimentalApi
+public abstract class BinaryWriter extends ByteOutput implements Writer {
+ public static final int DEFAULT_CHUNK_SIZE = 4096;
+
+ private final BufferAllocator alloc;
+ private final int chunkSize;
+
+ final ArrayDeque<AllocatedBuffer> buffers = new ArrayDeque<AllocatedBuffer>(4);
+ int totalDoneBytes;
+
+ /**
+ * Creates a new {@link BinaryWriter} that will allocate heap buffers of {@link
+ * #DEFAULT_CHUNK_SIZE} as necessary.
+ */
+ public static BinaryWriter newHeapInstance(BufferAllocator alloc) {
+ return newHeapInstance(alloc, DEFAULT_CHUNK_SIZE);
+ }
+
+ /**
+ * Creates a new {@link BinaryWriter} that will allocate heap buffers of {@code chunkSize} as
+ * necessary.
+ */
+ public static BinaryWriter newHeapInstance(BufferAllocator alloc, int chunkSize) {
+ return isUnsafeHeapSupported()
+ ? newUnsafeHeapInstance(alloc, chunkSize)
+ : newSafeHeapInstance(alloc, chunkSize);
+ }
+
+ /**
+ * Creates a new {@link BinaryWriter} that will allocate direct (i.e. non-heap) buffers of {@link
+ * #DEFAULT_CHUNK_SIZE} as necessary.
+ */
+ public static BinaryWriter newDirectInstance(BufferAllocator alloc) {
+ return newDirectInstance(alloc, DEFAULT_CHUNK_SIZE);
+ }
+
+ /**
+ * Creates a new {@link BinaryWriter} that will allocate direct (i.e. non-heap) buffers of {@code
+ * chunkSize} as necessary.
+ */
+ public static BinaryWriter newDirectInstance(BufferAllocator alloc, int chunkSize) {
+ return isUnsafeDirectSupported()
+ ? newUnsafeDirectInstance(alloc, chunkSize)
+ : newSafeDirectInstance(alloc, chunkSize);
+ }
+
+ static boolean isUnsafeHeapSupported() {
+ return UnsafeHeapWriter.isSupported();
+ }
+
+ static boolean isUnsafeDirectSupported() {
+ return UnsafeDirectWriter.isSupported();
+ }
+
+ static BinaryWriter newSafeHeapInstance(BufferAllocator alloc, int chunkSize) {
+ return new SafeHeapWriter(alloc, chunkSize);
+ }
+
+ static BinaryWriter newUnsafeHeapInstance(BufferAllocator alloc, int chunkSize) {
+ if (!isUnsafeHeapSupported()) {
+ throw new UnsupportedOperationException("Unsafe operations not supported");
+ }
+ return new UnsafeHeapWriter(alloc, chunkSize);
+ }
+
+ static BinaryWriter newSafeDirectInstance(BufferAllocator alloc, int chunkSize) {
+ return new SafeDirectWriter(alloc, chunkSize);
+ }
+
+ static BinaryWriter newUnsafeDirectInstance(BufferAllocator alloc, int chunkSize) {
+ if (!isUnsafeDirectSupported()) {
+ throw new UnsupportedOperationException("Unsafe operations not supported");
+ }
+ return new UnsafeDirectWriter(alloc, chunkSize);
+ }
+
+ /** Only allow subclassing for inner classes. */
+ private BinaryWriter(BufferAllocator alloc, int chunkSize) {
+ if (chunkSize <= 0) {
+ throw new IllegalArgumentException("chunkSize must be > 0");
+ }
+ this.alloc = checkNotNull(alloc, "alloc");
+ this.chunkSize = chunkSize;
+ }
+
+ /**
+ * Completes the write operation and returns a queue of {@link AllocatedBuffer} objects in
+ * forward-writing order. This method should only be called once.
+ *
+ * <p>After calling this method, the writer can not be reused. Create a new writer for future
+ * writes.
+ */
+ public final Queue<AllocatedBuffer> complete() {
+ finishCurrentBuffer();
+ return buffers;
+ }
+
+ @Override
+ public final void writeSFixed32(int fieldNumber, int value) {
+ writeFixed32(fieldNumber, value);
+ }
+
+ @Override
+ public final void writeInt64(int fieldNumber, long value) {
+ writeUInt64(fieldNumber, value);
+ }
+
+ @Override
+ public final void writeSFixed64(int fieldNumber, long value) {
+ writeFixed64(fieldNumber, value);
+ }
+
+ @Override
+ public final void writeFloat(int fieldNumber, float value) {
+ writeFixed32(fieldNumber, Float.floatToRawIntBits(value));
+ }
+
+ @Override
+ public final void writeDouble(int fieldNumber, double value) {
+ writeFixed64(fieldNumber, Double.doubleToRawLongBits(value));
+ }
+
+ @Override
+ public final void writeEnum(int fieldNumber, int value) {
+ writeInt32(fieldNumber, value);
+ }
+
+ @Override
+ public final void writeInt32List(int fieldNumber, List<Integer> list, boolean packed) {
+ if (list instanceof IntArrayList) {
+ writeInt32List_Internal(fieldNumber, (IntArrayList) list, packed);
+ } else {
+ writeInt32List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeInt32List_Internal(int fieldNumber, List<Integer> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeInt32(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeInt32(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeInt32List_Internal(int fieldNumber, IntArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeInt32(list.getInt(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeInt32(fieldNumber, list.getInt(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeFixed32List(int fieldNumber, List<Integer> list, boolean packed) {
+ if (list instanceof IntArrayList) {
+ writeFixed32List_Internal(fieldNumber, (IntArrayList) list, packed);
+ } else {
+ writeFixed32List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeFixed32List_Internal(
+ int fieldNumber, List<Integer> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed32(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed32(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeFixed32List_Internal(int fieldNumber, IntArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed32(list.getInt(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed32(fieldNumber, list.getInt(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeInt64List(int fieldNumber, List<Long> list, boolean packed) {
+ writeUInt64List(fieldNumber, list, packed);
+ }
+
+ @Override
+ public final void writeUInt64List(int fieldNumber, List<Long> list, boolean packed) {
+ if (list instanceof LongArrayList) {
+ writeUInt64List_Internal(fieldNumber, (LongArrayList) list, packed);
+ } else {
+ writeUInt64List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeUInt64List_Internal(int fieldNumber, List<Long> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeVarint64(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeUInt64(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeUInt64List_Internal(int fieldNumber, LongArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeVarint64(list.getLong(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeUInt64(fieldNumber, list.getLong(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeFixed64List(int fieldNumber, List<Long> list, boolean packed) {
+ if (list instanceof LongArrayList) {
+ writeFixed64List_Internal(fieldNumber, (LongArrayList) list, packed);
+ } else {
+ writeFixed64List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeFixed64List_Internal(int fieldNumber, List<Long> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed64(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed64(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeFixed64List_Internal(
+ int fieldNumber, LongArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed64(list.getLong(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed64(fieldNumber, list.getLong(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeFloatList(int fieldNumber, List<Float> list, boolean packed) {
+ if (list instanceof FloatArrayList) {
+ writeFloatList_Internal(fieldNumber, (FloatArrayList) list, packed);
+ } else {
+ writeFloatList_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeFloatList_Internal(int fieldNumber, List<Float> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed32(Float.floatToRawIntBits(list.get(i)));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFloat(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeFloatList_Internal(int fieldNumber, FloatArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed32(Float.floatToRawIntBits(list.getFloat(i)));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFloat(fieldNumber, list.getFloat(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeDoubleList(int fieldNumber, List<Double> list, boolean packed) {
+ if (list instanceof DoubleArrayList) {
+ writeDoubleList_Internal(fieldNumber, (DoubleArrayList) list, packed);
+ } else {
+ writeDoubleList_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeDoubleList_Internal(int fieldNumber, List<Double> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed64(Double.doubleToRawLongBits(list.get(i)));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeDouble(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeDoubleList_Internal(
+ int fieldNumber, DoubleArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * FIXED64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeFixed64(Double.doubleToRawLongBits(list.getDouble(i)));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeDouble(fieldNumber, list.getDouble(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeEnumList(int fieldNumber, List<Integer> list, boolean packed) {
+ writeInt32List(fieldNumber, list, packed);
+ }
+
+ @Override
+ public final void writeBoolList(int fieldNumber, List<Boolean> list, boolean packed) {
+ if (list instanceof BooleanArrayList) {
+ writeBoolList_Internal(fieldNumber, (BooleanArrayList) list, packed);
+ } else {
+ writeBoolList_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeBoolList_Internal(int fieldNumber, List<Boolean> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + list.size());
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeBool(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeBool(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeBoolList_Internal(
+ int fieldNumber, BooleanArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + list.size());
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeBool(list.getBoolean(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeBool(fieldNumber, list.getBoolean(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeStringList(int fieldNumber, List<String> list) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ writeString(fieldNumber, list.get(i));
+ }
+ }
+
+ @Override
+ public final void writeBytesList(int fieldNumber, List<ByteString> list) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ writeBytes(fieldNumber, list.get(i));
+ }
+ }
+
+ @Override
+ public final void writeUInt32List(int fieldNumber, List<Integer> list, boolean packed) {
+ if (list instanceof IntArrayList) {
+ writeUInt32List_Internal(fieldNumber, (IntArrayList) list, packed);
+ } else {
+ writeUInt32List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeUInt32List_Internal(int fieldNumber, List<Integer> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeVarint32(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeUInt32(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeUInt32List_Internal(int fieldNumber, IntArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeVarint32(list.getInt(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeUInt32(fieldNumber, list.getInt(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeSFixed32List(int fieldNumber, List<Integer> list, boolean packed) {
+ writeFixed32List(fieldNumber, list, packed);
+ }
+
+ @Override
+ public final void writeSFixed64List(int fieldNumber, List<Long> list, boolean packed) {
+ writeFixed64List(fieldNumber, list, packed);
+ }
+
+ @Override
+ public final void writeSInt32List(int fieldNumber, List<Integer> list, boolean packed) {
+ if (list instanceof IntArrayList) {
+ writeSInt32List_Internal(fieldNumber, (IntArrayList) list, packed);
+ } else {
+ writeSInt32List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeSInt32List_Internal(int fieldNumber, List<Integer> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt32(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt32(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeSInt32List_Internal(int fieldNumber, IntArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT32_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt32(list.getInt(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt32(fieldNumber, list.getInt(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeSInt64List(int fieldNumber, List<Long> list, boolean packed) {
+ if (list instanceof LongArrayList) {
+ writeSInt64List_Internal(fieldNumber, (LongArrayList) list, packed);
+ } else {
+ writeSInt64List_Internal(fieldNumber, list, packed);
+ }
+ }
+
+ private final void writeSInt64List_Internal(int fieldNumber, List<Long> list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt64(list.get(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt64(fieldNumber, list.get(i));
+ }
+ }
+ }
+
+ private final void writeSInt64List_Internal(int fieldNumber, LongArrayList list, boolean packed) {
+ if (packed) {
+ requireSpace((MAX_VARINT32_SIZE * 2) + (list.size() * MAX_VARINT64_SIZE));
+ int prevBytes = getTotalBytesWritten();
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt64(list.getLong(i));
+ }
+ int length = getTotalBytesWritten() - prevBytes;
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ } else {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ writeSInt64(fieldNumber, list.getLong(i));
+ }
+ }
+ }
+
+ @Override
+ public final void writeMessageList(int fieldNumber, List<?> list) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ writeMessage(fieldNumber, list.get(i));
+ }
+ }
+
+ @Override
+ public final void writeGroupList(int fieldNumber, List<?> list) {
+ for (int i = list.size() - 1; i >= 0; i--) {
+ writeGroup(fieldNumber, list.get(i));
+ }
+ }
+
+ final AllocatedBuffer newHeapBuffer() {
+ return alloc.allocateHeapBuffer(chunkSize);
+ }
+
+ final AllocatedBuffer newHeapBuffer(int capacity) {
+ return alloc.allocateHeapBuffer(Math.max(capacity, chunkSize));
+ }
+
+ final AllocatedBuffer newDirectBuffer() {
+ return alloc.allocateDirectBuffer(chunkSize);
+ }
+
+ final AllocatedBuffer newDirectBuffer(int capacity) {
+ return alloc.allocateDirectBuffer(Math.max(capacity, chunkSize));
+ }
+
+ /**
+ * Gets the total number of bytes that have been written. This will not be reset by a call to
+ * {@link #complete()}.
+ */
+ public abstract int getTotalBytesWritten();
+
+ abstract void requireSpace(int size);
+
+ abstract void finishCurrentBuffer();
+
+ abstract void writeTag(int fieldNumber, byte wireType);
+
+ abstract void writeVarint32(int value);
+
+ abstract void writeInt32(int value);
+
+ abstract void writeSInt32(int value);
+
+ abstract void writeFixed32(int value);
+
+ abstract void writeVarint64(long value);
+
+ abstract void writeSInt64(long value);
+
+ abstract void writeFixed64(long value);
+
+ abstract void writeBool(boolean value);
+
+ abstract void writeString(String in);
+
+ /**
+ * Not using the version in CodedOutputStream due to the fact that benchmarks have shown a
+ * performance improvement when returning a byte (rather than an int).
+ */
+ private static byte computeUInt64SizeNoTag(long value) {
+ // handle two popular special cases up front ...
+ if ((value & (~0L << 7)) == 0L) {
+ // Byte 1
+ return 1;
+ }
+ if (value < 0L) {
+ // Byte 10
+ return 10;
+ }
+ // ... leaving us with 8 remaining, which we can divide and conquer
+ byte n = 2;
+ if ((value & (~0L << 35)) != 0L) {
+ // Byte 6-9
+ n += 4; // + (value >>> 63);
+ value >>>= 28;
+ }
+ if ((value & (~0L << 21)) != 0L) {
+ // Byte 4-5 or 8-9
+ n += 2;
+ value >>>= 14;
+ }
+ if ((value & (~0L << 14)) != 0L) {
+ // Byte 3 or 7
+ n += 1;
+ }
+ return n;
+ }
+
+ /** Writer that uses safe operations on target array. */
+ private static final class SafeHeapWriter extends BinaryWriter {
+ private AllocatedBuffer allocatedBuffer;
+ private byte[] buffer;
+ private int offset;
+ private int limit;
+ private int offsetMinusOne;
+ private int limitMinusOne;
+ private int pos;
+
+ SafeHeapWriter(BufferAllocator alloc, int chunkSize) {
+ super(alloc, chunkSize);
+ nextBuffer();
+ }
+
+ @Override
+ void finishCurrentBuffer() {
+ if (allocatedBuffer != null) {
+ totalDoneBytes += bytesWrittenToCurrentBuffer();
+ allocatedBuffer.position((pos - allocatedBuffer.arrayOffset()) + 1);
+ allocatedBuffer = null;
+ pos = 0;
+ limitMinusOne = 0;
+ }
+ }
+
+ private void nextBuffer() {
+ nextBuffer(newHeapBuffer());
+ }
+
+ private void nextBuffer(int capacity) {
+ nextBuffer(newHeapBuffer(capacity));
+ }
+
+ private void nextBuffer(AllocatedBuffer allocatedBuffer) {
+ if (!allocatedBuffer.hasArray()) {
+ throw new RuntimeException("Allocator returned non-heap buffer");
+ }
+
+ finishCurrentBuffer();
+
+ buffers.addFirst(allocatedBuffer);
+
+ this.allocatedBuffer = allocatedBuffer;
+ this.buffer = allocatedBuffer.array();
+ int arrayOffset = allocatedBuffer.arrayOffset();
+ this.limit = arrayOffset + allocatedBuffer.limit();
+ this.offset = arrayOffset + allocatedBuffer.position();
+ this.offsetMinusOne = offset - 1;
+ this.limitMinusOne = limit - 1;
+ this.pos = limitMinusOne;
+ }
+
+ @Override
+ public int getTotalBytesWritten() {
+ return totalDoneBytes + bytesWrittenToCurrentBuffer();
+ }
+
+ int bytesWrittenToCurrentBuffer() {
+ return limitMinusOne - pos;
+ }
+
+ int spaceLeft() {
+ return pos - offsetMinusOne;
+ }
+
+ @Override
+ public void writeUInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeSInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE);
+ writeFixed32(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED32);
+ }
+
+ @Override
+ public void writeUInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeVarint64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeSInt64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE);
+ writeFixed64(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED64);
+ }
+
+ @Override
+ public void writeBool(int fieldNumber, boolean value) {
+ requireSpace(MAX_VARINT32_SIZE + 1);
+ write((byte) (value ? 1 : 0));
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeString(int fieldNumber, String value) {
+ int prevBytes = getTotalBytesWritten();
+ writeString(value);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(2 * MAX_VARINT32_SIZE);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeBytes(int fieldNumber, ByteString value) {
+ try {
+ value.writeToReverse(this);
+ } catch (IOException e) {
+ // Should never happen since the writer does not throw.
+ throw new RuntimeException(e);
+ }
+
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value.size());
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeMessage(int fieldNumber, Object value) {
+ int prevBytes = getTotalBytesWritten();
+ Protobuf.getInstance().writeTo(value, this);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeGroup(int fieldNumber, Object value) {
+ writeTag(fieldNumber, WIRETYPE_END_GROUP);
+ Protobuf.getInstance().writeTo(value, this);
+ writeTag(fieldNumber, WIRETYPE_START_GROUP);
+ }
+
+ @Override
+ void writeInt32(int value) {
+ if (value >= 0) {
+ writeVarint32(value);
+ } else {
+ writeVarint64(value);
+ }
+ }
+
+ @Override
+ void writeSInt32(int value) {
+ writeVarint32(encodeZigZag32(value));
+ }
+
+ @Override
+ void writeSInt64(long value) {
+ writeVarint64(encodeZigZag64(value));
+ }
+
+ @Override
+ void writeBool(boolean value) {
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ void writeTag(int fieldNumber, byte wireType) {
+ writeVarint32(tagFor(fieldNumber, wireType));
+ }
+
+ @Override
+ void writeVarint32(int value) {
+ if ((value & (~0 << 7)) == 0) {
+ writeVarint32OneByte(value);
+ } else if ((value & (~0 << 14)) == 0) {
+ writeVarint32TwoBytes(value);
+ } else if ((value & (~0 << 21)) == 0) {
+ writeVarint32ThreeBytes(value);
+ } else if ((value & (~0 << 28)) == 0) {
+ writeVarint32FourBytes(value);
+ } else {
+ writeVarint32FiveBytes(value);
+ }
+ }
+
+ private void writeVarint32OneByte(int value) {
+ buffer[pos--] = (byte) value;
+ }
+
+ private void writeVarint32TwoBytes(int value) {
+ buffer[pos--] = (byte) (value >>> 7);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint32ThreeBytes(int value) {
+ buffer[pos--] = (byte) (value >>> 14);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint32FourBytes(int value) {
+ buffer[pos--] = (byte) (value >>> 21);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint32FiveBytes(int value) {
+ buffer[pos--] = (byte) (value >>> 28);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ @Override
+ void writeVarint64(long value) {
+ switch (computeUInt64SizeNoTag(value)) {
+ case 1:
+ writeVarint64OneByte(value);
+ break;
+ case 2:
+ writeVarint64TwoBytes(value);
+ break;
+ case 3:
+ writeVarint64ThreeBytes(value);
+ break;
+ case 4:
+ writeVarint64FourBytes(value);
+ break;
+ case 5:
+ writeVarint64FiveBytes(value);
+ break;
+ case 6:
+ writeVarint64SixBytes(value);
+ break;
+ case 7:
+ writeVarint64SevenBytes(value);
+ break;
+ case 8:
+ writeVarint64EightBytes(value);
+ break;
+ case 9:
+ writeVarint64NineBytes(value);
+ break;
+ case 10:
+ writeVarint64TenBytes(value);
+ break;
+ }
+ }
+
+ private void writeVarint64OneByte(long value) {
+ buffer[pos--] = (byte) value;
+ }
+
+ private void writeVarint64TwoBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 7);
+ buffer[pos--] = (byte) (((int) value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64ThreeBytes(long value) {
+ buffer[pos--] = (byte) (((int) value) >>> 14);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64FourBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 21);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64FiveBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 28);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64SixBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 35);
+ buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64SevenBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 42);
+ buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64EightBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 49);
+ buffer[pos--] = (byte) (((value >>> 42) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64NineBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 56);
+ buffer[pos--] = (byte) (((value >>> 49) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 42) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ private void writeVarint64TenBytes(long value) {
+ buffer[pos--] = (byte) (value >>> 63);
+ buffer[pos--] = (byte) (((value >>> 56) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 49) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 42) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 35) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 28) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 21) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 14) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) (((value >>> 7) & 0x7F) | 0x80);
+ buffer[pos--] = (byte) ((value & 0x7F) | 0x80);
+ }
+
+ @Override
+ void writeFixed32(int value) {
+ buffer[pos--] = (byte) ((value >> 24) & 0xFF);
+ buffer[pos--] = (byte) ((value >> 16) & 0xFF);
+ buffer[pos--] = (byte) ((value >> 8) & 0xFF);
+ buffer[pos--] = (byte) (value & 0xFF);
+ }
+
+ @Override
+ void writeFixed64(long value) {
+ buffer[pos--] = (byte) ((int) (value >> 56) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value >> 48) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value >> 40) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value >> 32) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value >> 24) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value >> 16) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value >> 8) & 0xFF);
+ buffer[pos--] = (byte) ((int) (value) & 0xFF);
+ }
+
+ @Override
+ void writeString(String in) {
+ // Request enough space to write the ASCII string.
+ requireSpace(in.length());
+
+ // We know the buffer is big enough...
+ int i = in.length() - 1;
+ // Set pos to the start of the ASCII string.
+ pos -= i;
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) {
+ buffer[pos + i] = (byte) c;
+ }
+ if (i == -1) {
+ // Move pos past the String.
+ pos -= 1;
+ return;
+ }
+ pos += i;
+ for (char c; i >= 0; i--) {
+ c = in.charAt(i);
+ if (c < 0x80 && pos > offsetMinusOne) {
+ buffer[pos--] = (byte) c;
+ } else if (c < 0x800 && pos > offset) { // 11 bits, two UTF-8 bytes
+ buffer[pos--] = (byte) (0x80 | (0x3F & c));
+ buffer[pos--] = (byte) ((0xF << 6) | (c >>> 6));
+ } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c)
+ && pos > (offset + 1)) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ buffer[pos--] = (byte) (0x80 | (0x3F & c));
+ buffer[pos--] = (byte) (0x80 | (0x3F & (c >>> 6)));
+ buffer[pos--] = (byte) ((0xF << 5) | (c >>> 12));
+ } else if (pos > (offset + 2)) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
+ // four UTF-8 bytes
+ char high = 0;
+ if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) {
+ throw new Utf8.UnpairedSurrogateException(i - 1, i);
+ }
+ i--;
+ int codePoint = Character.toCodePoint(high, c);
+ buffer[pos--] = (byte) (0x80 | (0x3F & codePoint));
+ buffer[pos--] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
+ buffer[pos--] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
+ buffer[pos--] = (byte) ((0xF << 4) | (codePoint >>> 18));
+ } else {
+ // Buffer is full - allocate a new one and revisit the current character.
+ requireSpace(i);
+ i++;
+ }
+ }
+ }
+
+ @Override
+ public void write(byte value) {
+ buffer[pos--] = value;
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) {
+ if (spaceLeft() < length) {
+ nextBuffer(length);
+ }
+
+ pos -= length;
+ System.arraycopy(value, offset, buffer, pos + 1, length);
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) {
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value, offset, length));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ return;
+ }
+
+ pos -= length;
+ System.arraycopy(value, offset, buffer, pos + 1, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ nextBuffer(length);
+ }
+
+ pos -= length;
+ value.get(buffer, pos + 1, length);
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ }
+
+ pos -= length;
+ value.get(buffer, pos + 1, length);
+ }
+
+ @Override
+ void requireSpace(int size) {
+ if (spaceLeft() < size) {
+ nextBuffer(size);
+ }
+ }
+ }
+
+ /** Writer that uses unsafe operations on a target array. */
+ private static final class UnsafeHeapWriter extends BinaryWriter {
+ private AllocatedBuffer allocatedBuffer;
+ private byte[] buffer;
+ private long offset;
+ private long limit;
+ private long offsetMinusOne;
+ private long limitMinusOne;
+ private long pos;
+
+ UnsafeHeapWriter(BufferAllocator alloc, int chunkSize) {
+ super(alloc, chunkSize);
+ nextBuffer();
+ }
+
+ /** Indicates whether the required unsafe operations are supported on this platform. */
+ static boolean isSupported() {
+ return UnsafeUtil.hasUnsafeArrayOperations();
+ }
+
+ @Override
+ void finishCurrentBuffer() {
+ if (allocatedBuffer != null) {
+ totalDoneBytes += bytesWrittenToCurrentBuffer();
+ allocatedBuffer.position((arrayPos() - allocatedBuffer.arrayOffset()) + 1);
+ allocatedBuffer = null;
+ pos = 0;
+ limitMinusOne = 0;
+ }
+ }
+
+ private int arrayPos() {
+ return (int) (pos - UnsafeUtil.getArrayBaseOffset());
+ }
+
+ private void nextBuffer() {
+ nextBuffer(newHeapBuffer());
+ }
+
+ private void nextBuffer(int capacity) {
+ nextBuffer(newHeapBuffer(capacity));
+ }
+
+ private void nextBuffer(AllocatedBuffer allocatedBuffer) {
+ if (!allocatedBuffer.hasArray()) {
+ throw new RuntimeException("Allocator returned non-heap buffer");
+ }
+
+ finishCurrentBuffer();
+ buffers.addFirst(allocatedBuffer);
+
+ this.allocatedBuffer = allocatedBuffer;
+ this.buffer = allocatedBuffer.array();
+ int arrayOffset = allocatedBuffer.arrayOffset();
+ long byteArrayOffset = UnsafeUtil.getArrayBaseOffset();
+ this.limit = byteArrayOffset + arrayOffset + allocatedBuffer.limit();
+ this.offset = byteArrayOffset + arrayOffset + allocatedBuffer.position();
+ this.offsetMinusOne = offset - 1;
+ this.limitMinusOne = limit - 1;
+ this.pos = limitMinusOne;
+ }
+
+ @Override
+ public int getTotalBytesWritten() {
+ return totalDoneBytes + bytesWrittenToCurrentBuffer();
+ }
+
+ int bytesWrittenToCurrentBuffer() {
+ return (int) (limitMinusOne - pos);
+ }
+
+ int spaceLeft() {
+ return (int) (pos - offsetMinusOne);
+ }
+
+ @Override
+ public void writeUInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeSInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE);
+ writeFixed32(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED32);
+ }
+
+ @Override
+ public void writeUInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeVarint64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeSInt64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE);
+ writeFixed64(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED64);
+ }
+
+ @Override
+ public void writeBool(int fieldNumber, boolean value) {
+ requireSpace(MAX_VARINT32_SIZE + 1);
+ write((byte) (value ? 1 : 0));
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeString(int fieldNumber, String value) {
+ int prevBytes = getTotalBytesWritten();
+ writeString(value);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(2 * MAX_VARINT32_SIZE);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeBytes(int fieldNumber, ByteString value) {
+ try {
+ value.writeToReverse(this);
+ } catch (IOException e) {
+ // Should never happen since the writer does not throw.
+ throw new RuntimeException(e);
+ }
+
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value.size());
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeMessage(int fieldNumber, Object value) {
+ int prevBytes = getTotalBytesWritten();
+ Protobuf.getInstance().writeTo(value, this);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeGroup(int fieldNumber, Object value) {
+ writeTag(fieldNumber, WIRETYPE_END_GROUP);
+ Protobuf.getInstance().writeTo(value, this);
+ writeTag(fieldNumber, WIRETYPE_START_GROUP);
+ }
+
+ @Override
+ void writeInt32(int value) {
+ if (value >= 0) {
+ writeVarint32(value);
+ } else {
+ writeVarint64(value);
+ }
+ }
+
+ @Override
+ void writeSInt32(int value) {
+ writeVarint32(encodeZigZag32(value));
+ }
+
+ @Override
+ void writeSInt64(long value) {
+ writeVarint64(encodeZigZag64(value));
+ }
+
+ @Override
+ void writeBool(boolean value) {
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ void writeTag(int fieldNumber, byte wireType) {
+ writeVarint32(tagFor(fieldNumber, wireType));
+ }
+
+ @Override
+ void writeVarint32(int value) {
+ if ((value & (~0 << 7)) == 0) {
+ writeVarint32OneByte(value);
+ } else if ((value & (~0 << 14)) == 0) {
+ writeVarint32TwoBytes(value);
+ } else if ((value & (~0 << 21)) == 0) {
+ writeVarint32ThreeBytes(value);
+ } else if ((value & (~0 << 28)) == 0) {
+ writeVarint32FourBytes(value);
+ } else {
+ writeVarint32FiveBytes(value);
+ }
+ }
+
+ private void writeVarint32OneByte(int value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) value);
+ }
+
+ private void writeVarint32TwoBytes(int value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 7));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32ThreeBytes(int value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 14));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32FourBytes(int value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 21));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32FiveBytes(int value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 28));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ @Override
+ void writeVarint64(long value) {
+ switch (computeUInt64SizeNoTag(value)) {
+ case 1:
+ writeVarint64OneByte(value);
+ break;
+ case 2:
+ writeVarint64TwoBytes(value);
+ break;
+ case 3:
+ writeVarint64ThreeBytes(value);
+ break;
+ case 4:
+ writeVarint64FourBytes(value);
+ break;
+ case 5:
+ writeVarint64FiveBytes(value);
+ break;
+ case 6:
+ writeVarint64SixBytes(value);
+ break;
+ case 7:
+ writeVarint64SevenBytes(value);
+ break;
+ case 8:
+ writeVarint64EightBytes(value);
+ break;
+ case 9:
+ writeVarint64NineBytes(value);
+ break;
+ case 10:
+ writeVarint64TenBytes(value);
+ break;
+ }
+ }
+
+ private void writeVarint64OneByte(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) value);
+ }
+
+ private void writeVarint64TwoBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 7));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((int) value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64ThreeBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((int) value) >>> 14));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64FourBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 21));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64FiveBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 28));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64SixBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 35));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64SevenBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 42));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64EightBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 49));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 42) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64NineBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 56));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 49) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 42) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64TenBytes(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value >>> 63));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 56) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 49) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 42) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ @Override
+ void writeFixed32(int value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value >> 24) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value >> 16) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((value >> 8) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (value & 0xFF));
+ }
+
+ @Override
+ void writeFixed64(long value) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 56) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 48) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 40) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 32) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 24) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 16) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value >> 8) & 0xFF));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((int) (value) & 0xFF));
+ }
+
+ @Override
+ void writeString(String in) {
+ // Request enough space to write the ASCII string.
+ requireSpace(in.length());
+
+ // We know the buffer is big enough...
+ int i = in.length() - 1;
+ // Set pos to the start of the ASCII string.
+ //pos -= i;
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) c);
+ }
+ if (i == -1) {
+ // Move pos past the String.
+ return;
+ }
+ for (char c; i >= 0; i--) {
+ c = in.charAt(i);
+ if (c < 0x80 && pos > offsetMinusOne) {
+ UnsafeUtil.putByte(buffer, pos--, (byte) c);
+ } else if (c < 0x800 && pos > offset) { // 11 bits, two UTF-8 bytes
+ UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((0xF << 6) | (c >>> 6)));
+ } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c)
+ && pos > offset + 1) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & (c >>> 6))));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((0xF << 5) | (c >>> 12)));
+ } else if (pos > offset + 2) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
+ // four UTF-8 bytes
+ final char high;
+ if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) {
+ throw new Utf8.UnpairedSurrogateException(i - 1, i);
+ }
+ i--;
+ int codePoint = Character.toCodePoint(high, c);
+ UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & codePoint)));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ UnsafeUtil.putByte(buffer, pos--, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ UnsafeUtil.putByte(buffer, pos--, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ } else {
+ // Buffer is full - allocate a new one and revisit the current character.
+ requireSpace(i);
+ i++;
+ }
+ }
+ }
+
+ @Override
+ public void write(byte value) {
+ UnsafeUtil.putByte(buffer, pos--, value);
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) {
+ if (offset < 0 || offset + length > value.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("value.length=%d, offset=%d, length=%d", value.length, offset, length));
+ }
+ requireSpace(length);
+
+ pos -= length;
+ System.arraycopy(value, offset, buffer, arrayPos() + 1, length);
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) {
+ if (offset < 0 || offset + length > value.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("value.length=%d, offset=%d, length=%d", value.length, offset, length));
+ }
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value, offset, length));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ return;
+ }
+
+ pos -= length;
+ System.arraycopy(value, offset, buffer, arrayPos() + 1, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) {
+ int length = value.remaining();
+ requireSpace(length);
+
+ pos -= length;
+ value.get(buffer, arrayPos() + 1, length);
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ }
+
+ pos -= length;
+ value.get(buffer, arrayPos() + 1, length);
+ }
+
+ @Override
+ void requireSpace(int size) {
+ if (spaceLeft() < size) {
+ nextBuffer(size);
+ }
+ }
+ }
+
+ /** Writer that uses safe operations on a target {@link ByteBuffer}. */
+ private static final class SafeDirectWriter extends BinaryWriter {
+ private ByteBuffer buffer;
+ private int limitMinusOne;
+ private int pos;
+
+ SafeDirectWriter(BufferAllocator alloc, int chunkSize) {
+ super(alloc, chunkSize);
+ nextBuffer();
+ }
+
+ private void nextBuffer() {
+ nextBuffer(newDirectBuffer());
+ }
+
+ private void nextBuffer(int capacity) {
+ nextBuffer(newDirectBuffer(capacity));
+ }
+
+ private void nextBuffer(AllocatedBuffer allocatedBuffer) {
+ if (!allocatedBuffer.hasNioBuffer()) {
+ throw new RuntimeException("Allocated buffer does not have NIO buffer");
+ }
+ ByteBuffer nioBuffer = allocatedBuffer.nioBuffer();
+ if (!nioBuffer.isDirect()) {
+ throw new RuntimeException("Allocator returned non-direct buffer");
+ }
+
+ finishCurrentBuffer();
+ buffers.addFirst(allocatedBuffer);
+
+ buffer = nioBuffer;
+ buffer.limit(buffer.capacity());
+ buffer.position(0);
+ // Set byte order to little endian for fast writing of fixed 32/64.
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ limitMinusOne = buffer.limit() - 1;
+ pos = limitMinusOne;
+ }
+
+ @Override
+ public int getTotalBytesWritten() {
+ return totalDoneBytes + bytesWrittenToCurrentBuffer();
+ }
+
+ private int bytesWrittenToCurrentBuffer() {
+ return limitMinusOne - pos;
+ }
+
+ private int spaceLeft() {
+ return pos + 1;
+ }
+
+ @Override
+ void finishCurrentBuffer() {
+ if (buffer != null) {
+ totalDoneBytes += bytesWrittenToCurrentBuffer();
+ // Update the indices on the netty buffer.
+ buffer.position(pos + 1);
+ buffer = null;
+ pos = 0;
+ limitMinusOne = 0;
+ }
+ }
+
+ @Override
+ public void writeUInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeSInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE);
+ writeFixed32(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED32);
+ }
+
+ @Override
+ public void writeUInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeVarint64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeSInt64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE);
+ writeFixed64(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED64);
+ }
+
+ @Override
+ public void writeBool(int fieldNumber, boolean value) {
+ requireSpace(MAX_VARINT32_SIZE + 1);
+ write((byte) (value ? 1 : 0));
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeString(int fieldNumber, String value) {
+ int prevBytes = getTotalBytesWritten();
+ writeString(value);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(2 * MAX_VARINT32_SIZE);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeBytes(int fieldNumber, ByteString value) {
+ try {
+ value.writeToReverse(this);
+ } catch (IOException e) {
+ // Should never happen since the writer does not throw.
+ throw new RuntimeException(e);
+ }
+
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value.size());
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeMessage(int fieldNumber, Object value) {
+ int prevBytes = getTotalBytesWritten();
+ Protobuf.getInstance().writeTo(value, this);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeGroup(int fieldNumber, Object value) {
+ writeTag(fieldNumber, WIRETYPE_END_GROUP);
+ Protobuf.getInstance().writeTo(value, this);
+ writeTag(fieldNumber, WIRETYPE_START_GROUP);
+ }
+
+ @Override
+ void writeInt32(int value) {
+ if (value >= 0) {
+ writeVarint32(value);
+ } else {
+ writeVarint64(value);
+ }
+ }
+
+ @Override
+ void writeSInt32(int value) {
+ writeVarint32(encodeZigZag32(value));
+ }
+
+ @Override
+ void writeSInt64(long value) {
+ writeVarint64(encodeZigZag64(value));
+ }
+
+ @Override
+ void writeBool(boolean value) {
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ void writeTag(int fieldNumber, byte wireType) {
+ writeVarint32(tagFor(fieldNumber, wireType));
+ }
+
+ @Override
+ void writeVarint32(int value) {
+ if ((value & (~0 << 7)) == 0) {
+ writeVarint32OneByte(value);
+ } else if ((value & (~0 << 14)) == 0) {
+ writeVarint32TwoBytes(value);
+ } else if ((value & (~0 << 21)) == 0) {
+ writeVarint32ThreeBytes(value);
+ } else if ((value & (~0 << 28)) == 0) {
+ writeVarint32FourBytes(value);
+ } else {
+ writeVarint32FiveBytes(value);
+ }
+ }
+
+ private void writeVarint32OneByte(int value) {
+ buffer.put(pos--, (byte) value);
+ }
+
+ private void writeVarint32TwoBytes(int value) {
+ // Byte order is little-endian.
+ pos -= 2;
+ buffer.putShort(pos + 1, (short) (((value & (0x7F << 7)) << 1) | ((value & 0x7F) | 0x80)));
+ }
+
+ private void writeVarint32ThreeBytes(int value) {
+ // Byte order is little-endian.
+ pos -= 3;
+ buffer.putInt(
+ pos,
+ ((value & (0x7F << 14)) << 10)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 9)
+ | ((value & 0x7F) | 0x80) << 8);
+ }
+
+ private void writeVarint32FourBytes(int value) {
+ // Byte order is little-endian.
+ pos -= 4;
+ buffer.putInt(
+ pos + 1,
+ ((value & (0x7F << 21)) << 3)
+ | (((value & (0x7F << 14)) | (0x80 << 14)) << 2)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 1)
+ | ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32FiveBytes(int value) {
+ // Byte order is little-endian.
+ buffer.put(pos--, (byte) (value >>> 28));
+ pos -= 4;
+ buffer.putInt(
+ pos + 1,
+ ((((value >>> 21) & 0x7F) | 0x80) << 24)
+ | ((((value >>> 14) & 0x7F) | 0x80) << 16)
+ | ((((value >>> 7) & 0x7F) | 0x80) << 8)
+ | ((value & 0x7F) | 0x80));
+ }
+
+ @Override
+ void writeVarint64(long value) {
+ switch (computeUInt64SizeNoTag(value)) {
+ case 1:
+ writeVarint64OneByte(value);
+ break;
+ case 2:
+ writeVarint64TwoBytes(value);
+ break;
+ case 3:
+ writeVarint64ThreeBytes(value);
+ break;
+ case 4:
+ writeVarint64FourBytes(value);
+ break;
+ case 5:
+ writeVarint64FiveBytes(value);
+ break;
+ case 6:
+ writeVarint64SixBytes(value);
+ break;
+ case 7:
+ writeVarint64SevenBytes(value);
+ break;
+ case 8:
+ writeVarint64EightBytes(value);
+ break;
+ case 9:
+ writeVarint64NineBytes(value);
+ break;
+ case 10:
+ writeVarint64TenBytes(value);
+ break;
+ }
+ }
+
+ private void writeVarint64OneByte(long value) {
+ writeVarint32OneByte((int) value);
+ }
+
+ private void writeVarint64TwoBytes(long value) {
+ writeVarint32TwoBytes((int) value);
+ }
+
+ private void writeVarint64ThreeBytes(long value) {
+ writeVarint32ThreeBytes((int) value);
+ }
+
+ private void writeVarint64FourBytes(long value) {
+ writeVarint32FourBytes((int) value);
+ }
+
+ private void writeVarint64FiveBytes(long value) {
+ // Byte order is little-endian.
+ pos -= 5;
+ buffer.putLong(
+ pos - 2,
+ ((value & (0x7FL << 28)) << 28)
+ | (((value & (0x7F << 21)) | (0x80 << 21)) << 27)
+ | (((value & (0x7F << 14)) | (0x80 << 14)) << 26)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 25)
+ | (((value & 0x7F) | 0x80)) << 24);
+ }
+
+ private void writeVarint64SixBytes(long value) {
+ // Byte order is little-endian.
+ pos -= 6;
+ buffer.putLong(
+ pos - 1,
+ ((value & (0x7FL << 35)) << 21)
+ | (((value & (0x7FL << 28)) | (0x80L << 28)) << 20)
+ | (((value & (0x7F << 21)) | (0x80 << 21)) << 19)
+ | (((value & (0x7F << 14)) | (0x80 << 14)) << 18)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 17)
+ | (((value & 0x7F) | 0x80)) << 16);
+ }
+
+ private void writeVarint64SevenBytes(long value) {
+ // Byte order is little-endian.
+ pos -= 7;
+ buffer.putLong(
+ pos,
+ ((value & (0x7FL << 42)) << 14)
+ | (((value & (0x7FL << 35)) | (0x80L << 35)) << 13)
+ | (((value & (0x7FL << 28)) | (0x80L << 28)) << 12)
+ | (((value & (0x7F << 21)) | (0x80 << 21)) << 11)
+ | (((value & (0x7F << 14)) | (0x80 << 14)) << 10)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 9)
+ | (((value & 0x7F) | 0x80)) << 8);
+ }
+
+ private void writeVarint64EightBytes(long value) {
+ // Byte order is little-endian.
+ pos -= 8;
+ buffer.putLong(
+ pos + 1,
+ ((value & (0x7FL << 49)) << 7)
+ | (((value & (0x7FL << 42)) | (0x80L << 42)) << 6)
+ | (((value & (0x7FL << 35)) | (0x80L << 35)) << 5)
+ | (((value & (0x7FL << 28)) | (0x80L << 28)) << 4)
+ | (((value & (0x7F << 21)) | (0x80 << 21)) << 3)
+ | (((value & (0x7F << 14)) | (0x80 << 14)) << 2)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 1)
+ | ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64EightBytesWithSign(long value) {
+ // Byte order is little-endian.
+ pos -= 8;
+ buffer.putLong(
+ pos + 1,
+ (((value & (0x7FL << 49)) | (0x80L << 49)) << 7)
+ | (((value & (0x7FL << 42)) | (0x80L << 42)) << 6)
+ | (((value & (0x7FL << 35)) | (0x80L << 35)) << 5)
+ | (((value & (0x7FL << 28)) | (0x80L << 28)) << 4)
+ | (((value & (0x7F << 21)) | (0x80 << 21)) << 3)
+ | (((value & (0x7F << 14)) | (0x80 << 14)) << 2)
+ | (((value & (0x7F << 7)) | (0x80 << 7)) << 1)
+ | ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64NineBytes(long value) {
+ buffer.put(pos--, (byte) (value >>> 56));
+ writeVarint64EightBytesWithSign(value & 0xFFFFFFFFFFFFFFL);
+ }
+
+ private void writeVarint64TenBytes(long value) {
+ buffer.put(pos--, (byte) (value >>> 63));
+ buffer.put(pos--, (byte) (((value >>> 56) & 0x7F) | 0x80));
+ writeVarint64EightBytesWithSign(value & 0xFFFFFFFFFFFFFFL);
+ }
+
+ @Override
+ void writeFixed32(int value) {
+ pos -= 4;
+ buffer.putInt(pos + 1, value);
+ }
+
+ @Override
+ void writeFixed64(long value) {
+ pos -= 8;
+ buffer.putLong(pos + 1, value);
+ }
+
+ @Override
+ void writeString(String in) {
+ // Request enough space to write the ASCII string.
+ requireSpace(in.length());
+
+ // We know the buffer is big enough...
+ int i = in.length() - 1;
+ pos -= i;
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) {
+ buffer.put(pos + i, (byte) c);
+ }
+ if (i == -1) {
+ // Move the position past the ASCII string.
+ pos -= 1;
+ return;
+ }
+ pos += i;
+ for (char c; i >= 0; i--) {
+ c = in.charAt(i);
+ if (c < 0x80 && pos >= 0) {
+ buffer.put(pos--, (byte) c);
+ } else if (c < 0x800 && pos > 0) { // 11 bits, two UTF-8 bytes
+ buffer.put(pos--, (byte) (0x80 | (0x3F & c)));
+ buffer.put(pos--, (byte) ((0xF << 6) | (c >>> 6)));
+ } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && pos > 1) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ buffer.put(pos--, (byte) (0x80 | (0x3F & c)));
+ buffer.put(pos--, (byte) (0x80 | (0x3F & (c >>> 6))));
+ buffer.put(pos--, (byte) ((0xF << 5) | (c >>> 12)));
+ } else if (pos > 2) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
+ // four UTF-8 bytes
+ char high = 0;
+ if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) {
+ throw new Utf8.UnpairedSurrogateException(i - 1, i);
+ }
+ i--;
+ int codePoint = Character.toCodePoint(high, c);
+ buffer.put(pos--, (byte) (0x80 | (0x3F & codePoint)));
+ buffer.put(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ buffer.put(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ buffer.put(pos--, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ } else {
+ // Buffer is full - allocate a new one and revisit the current character.
+ requireSpace(i);
+ i++;
+ }
+ }
+ }
+
+ @Override
+ public void write(byte value) {
+ buffer.put(pos--, value);
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) {
+ if (spaceLeft() < length) {
+ nextBuffer(length);
+ }
+
+ pos -= length;
+ buffer.position(pos + 1);
+ buffer.put(value, offset, length);
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) {
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value, offset, length));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ return;
+ }
+
+ pos -= length;
+ buffer.position(pos + 1);
+ buffer.put(value, offset, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ nextBuffer(length);
+ }
+
+ pos -= length;
+ buffer.position(pos + 1);
+ buffer.put(value);
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ return;
+ }
+
+ pos -= length;
+ buffer.position(pos + 1);
+ buffer.put(value);
+ }
+
+ @Override
+ void requireSpace(int size) {
+ if (spaceLeft() < size) {
+ nextBuffer(size);
+ }
+ }
+ }
+
+ /** Writer that uses unsafe operations on a target {@link ByteBuffer}. */
+ private static final class UnsafeDirectWriter extends BinaryWriter {
+ private ByteBuffer buffer;
+ private long bufferOffset;
+ private long limitMinusOne;
+ private long pos;
+
+ UnsafeDirectWriter(BufferAllocator alloc, int chunkSize) {
+ super(alloc, chunkSize);
+ nextBuffer();
+ }
+
+ /** Indicates whether the required unsafe operations are supported on this platform. */
+ private static boolean isSupported() {
+ return UnsafeUtil.hasUnsafeByteBufferOperations();
+ }
+
+ private void nextBuffer() {
+ nextBuffer(newDirectBuffer());
+ }
+
+ private void nextBuffer(int capacity) {
+ nextBuffer(newDirectBuffer(capacity));
+ }
+
+ private void nextBuffer(AllocatedBuffer allocatedBuffer) {
+ if (!allocatedBuffer.hasNioBuffer()) {
+ throw new RuntimeException("Allocated buffer does not have NIO buffer");
+ }
+ ByteBuffer nioBuffer = allocatedBuffer.nioBuffer();
+ if (!nioBuffer.isDirect()) {
+ throw new RuntimeException("Allocator returned non-direct buffer");
+ }
+
+ finishCurrentBuffer();
+ buffers.addFirst(allocatedBuffer);
+
+ buffer = nioBuffer;
+ buffer.limit(buffer.capacity());
+ buffer.position(0);
+
+ bufferOffset = UnsafeUtil.addressOffset(buffer);
+ limitMinusOne = bufferOffset + (buffer.limit() - 1);
+ pos = limitMinusOne;
+ }
+
+ @Override
+ public int getTotalBytesWritten() {
+ return totalDoneBytes + bytesWrittenToCurrentBuffer();
+ }
+
+ private int bytesWrittenToCurrentBuffer() {
+ return (int) (limitMinusOne - pos);
+ }
+
+ private int spaceLeft() {
+ return bufferPos() + 1;
+ }
+
+ @Override
+ void finishCurrentBuffer() {
+ if (buffer != null) {
+ totalDoneBytes += bytesWrittenToCurrentBuffer();
+ // Update the indices on the netty buffer.
+ buffer.position(bufferPos() + 1);
+ buffer = null;
+ pos = 0;
+ limitMinusOne = 0;
+ }
+ }
+
+ private int bufferPos() {
+ return (int) (pos - bufferOffset);
+ }
+
+ @Override
+ public void writeUInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeSInt32(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed32(int fieldNumber, int value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED32_SIZE);
+ writeFixed32(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED32);
+ }
+
+ @Override
+ public void writeUInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeVarint64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeSInt64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + MAX_VARINT64_SIZE);
+ writeSInt64(value);
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeFixed64(int fieldNumber, long value) {
+ requireSpace(MAX_VARINT32_SIZE + FIXED64_SIZE);
+ writeFixed64(value);
+ writeTag(fieldNumber, WIRETYPE_FIXED64);
+ }
+
+ @Override
+ public void writeBool(int fieldNumber, boolean value) {
+ requireSpace(MAX_VARINT32_SIZE + 1);
+ write((byte) (value ? 1 : 0));
+ writeTag(fieldNumber, WIRETYPE_VARINT);
+ }
+
+ @Override
+ public void writeString(int fieldNumber, String value) {
+ int prevBytes = getTotalBytesWritten();
+ writeString(value);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(2 * MAX_VARINT32_SIZE);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeBytes(int fieldNumber, ByteString value) {
+ try {
+ value.writeToReverse(this);
+ } catch (IOException e) {
+ // Should never happen since the writer does not throw.
+ throw new RuntimeException(e);
+ }
+
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(value.size());
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeMessage(int fieldNumber, Object value) {
+ int prevBytes = getTotalBytesWritten();
+ Protobuf.getInstance().writeTo(value, this);
+ int length = getTotalBytesWritten() - prevBytes;
+ requireSpace(MAX_VARINT32_SIZE * 2);
+ writeVarint32(length);
+ writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
+ }
+
+ @Override
+ public void writeGroup(int fieldNumber, Object value) {
+ writeTag(fieldNumber, WIRETYPE_END_GROUP);
+ Protobuf.getInstance().writeTo(value, this);
+ writeTag(fieldNumber, WIRETYPE_START_GROUP);
+ }
+
+ @Override
+ void writeInt32(int value) {
+ if (value >= 0) {
+ writeVarint32(value);
+ } else {
+ writeVarint64(value);
+ }
+ }
+
+ @Override
+ void writeSInt32(int value) {
+ writeVarint32(encodeZigZag32(value));
+ }
+
+ @Override
+ void writeSInt64(long value) {
+ writeVarint64(encodeZigZag64(value));
+ }
+
+ @Override
+ void writeBool(boolean value) {
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ void writeTag(int fieldNumber, byte wireType) {
+ writeVarint32(tagFor(fieldNumber, wireType));
+ }
+
+ @Override
+ void writeVarint32(int value) {
+ if ((value & (~0 << 7)) == 0) {
+ writeVarint32OneByte(value);
+ } else if ((value & (~0 << 14)) == 0) {
+ writeVarint32TwoBytes(value);
+ } else if ((value & (~0 << 21)) == 0) {
+ writeVarint32ThreeBytes(value);
+ } else if ((value & (~0 << 28)) == 0) {
+ writeVarint32FourBytes(value);
+ } else {
+ writeVarint32FiveBytes(value);
+ }
+ }
+
+ private void writeVarint32OneByte(int value) {
+ UnsafeUtil.putByte(pos--, (byte) value);
+ }
+
+ private void writeVarint32TwoBytes(int value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 7));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32ThreeBytes(int value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 14));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32FourBytes(int value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 21));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint32FiveBytes(int value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 28));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ @Override
+ void writeVarint64(long value) {
+ switch (computeUInt64SizeNoTag(value)) {
+ case 1:
+ writeVarint64OneByte(value);
+ break;
+ case 2:
+ writeVarint64TwoBytes(value);
+ break;
+ case 3:
+ writeVarint64ThreeBytes(value);
+ break;
+ case 4:
+ writeVarint64FourBytes(value);
+ break;
+ case 5:
+ writeVarint64FiveBytes(value);
+ break;
+ case 6:
+ writeVarint64SixBytes(value);
+ break;
+ case 7:
+ writeVarint64SevenBytes(value);
+ break;
+ case 8:
+ writeVarint64EightBytes(value);
+ break;
+ case 9:
+ writeVarint64NineBytes(value);
+ break;
+ case 10:
+ writeVarint64TenBytes(value);
+ break;
+ }
+ }
+
+ private void writeVarint64OneByte(long value) {
+ UnsafeUtil.putByte(pos--, (byte) value);
+ }
+
+ private void writeVarint64TwoBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 7));
+ UnsafeUtil.putByte(pos--, (byte) (((int) value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64ThreeBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (((int) value) >>> 14));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64FourBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 21));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64FiveBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 28));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64SixBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 35));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64SevenBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 42));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64EightBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 49));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 42) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64NineBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 56));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 49) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 42) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ private void writeVarint64TenBytes(long value) {
+ UnsafeUtil.putByte(pos--, (byte) (value >>> 63));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 56) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 49) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 42) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 35) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 28) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 21) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 14) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) (((value >>> 7) & 0x7F) | 0x80));
+ UnsafeUtil.putByte(pos--, (byte) ((value & 0x7F) | 0x80));
+ }
+
+ @Override
+ void writeFixed32(int value) {
+ UnsafeUtil.putByte(pos--, (byte) ((value >> 24) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((value >> 16) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((value >> 8) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) (value & 0xFF));
+ }
+
+ @Override
+ void writeFixed64(long value) {
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 56) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 48) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 40) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 32) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 24) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 16) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value >> 8) & 0xFF));
+ UnsafeUtil.putByte(pos--, (byte) ((int) (value) & 0xFF));
+ }
+
+ @Override
+ void writeString(String in) {
+ // Request enough space to write the ASCII string.
+ requireSpace(in.length());
+
+ // We know the buffer is big enough...
+ int i = in.length() - 1;
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ for (char c; i >= 0 && (c = in.charAt(i)) < 0x80; i--) {
+ UnsafeUtil.putByte(pos--, (byte) c);
+ }
+ if (i == -1) {
+ // ASCII.
+ return;
+ }
+ for (char c; i >= 0; i--) {
+ c = in.charAt(i);
+ if (c < 0x80 && pos >= bufferOffset) {
+ UnsafeUtil.putByte(pos--, (byte) c);
+ } else if (c < 0x800 && pos > bufferOffset) { // 11 bits, two UTF-8 bytes
+ UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(pos--, (byte) ((0xF << 6) | (c >>> 6)));
+ } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c)
+ && pos > bufferOffset + 1) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & c)));
+ UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & (c >>> 6))));
+ UnsafeUtil.putByte(pos--, (byte) ((0xF << 5) | (c >>> 12)));
+ } else if (pos > bufferOffset + 2) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
+ // four UTF-8 bytes
+ final char high;
+ if (i == 0 || !Character.isSurrogatePair(high = in.charAt(i - 1), c)) {
+ throw new Utf8.UnpairedSurrogateException(i - 1, i);
+ }
+ i--;
+ int codePoint = Character.toCodePoint(high, c);
+ UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & codePoint)));
+ UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ UnsafeUtil.putByte(pos--, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ UnsafeUtil.putByte(pos--, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ } else {
+ // Buffer is full - allocate a new one and revisit the current character.
+ requireSpace(i);
+ i++;
+ }
+ }
+ }
+
+ @Override
+ public void write(byte value) {
+ UnsafeUtil.putByte(pos--, value);
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) {
+ if (spaceLeft() < length) {
+ nextBuffer(length);
+ }
+
+ pos -= length;
+ buffer.position(bufferPos() + 1);
+ buffer.put(value, offset, length);
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) {
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value, offset, length));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ return;
+ }
+
+ pos -= length;
+ buffer.position(bufferPos() + 1);
+ buffer.put(value, offset, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ nextBuffer(length);
+ }
+
+ pos -= length;
+ long sourceOffset = UnsafeUtil.addressOffset(value);
+ long sourcePos = sourceOffset + value.position();
+ UnsafeUtil.copyMemory(sourcePos, pos + 1, length);
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) {
+ int length = value.remaining();
+ if (spaceLeft() < length) {
+ // We consider the value to be immutable (likely the internals of a ByteString). Just
+ // wrap it in a Netty buffer and add it to the output buffer.
+ totalDoneBytes += length;
+ buffers.addFirst(AllocatedBuffer.wrap(value));
+
+ // Advance the writer to the next buffer.
+ // TODO(nathanmittler): Consider slicing if space available above some threshold.
+ nextBuffer();
+ return;
+ }
+
+ pos -= length;
+ long sourceOffset = UnsafeUtil.addressOffset(value);
+ long sourcePos = sourceOffset + value.position();
+ UnsafeUtil.copyMemory(sourcePos, pos + 1, length);
+ }
+
+ @Override
+ void requireSpace(int size) {
+ if (spaceLeft() < size) {
+ nextBuffer(size);
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/BufferAllocator.java b/java/core/src/main/java/com/google/protobuf/BufferAllocator.java
new file mode 100644
index 00000000..f8d865d9
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/BufferAllocator.java
@@ -0,0 +1,71 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.nio.ByteBuffer;
+
+/**
+ * An object responsible for allocation of buffers. This is an extension point to enable buffer
+ * pooling within an application.
+ */
+@ExperimentalApi
+public abstract class BufferAllocator {
+ private static final BufferAllocator UNPOOLED =
+ new BufferAllocator() {
+ @Override
+ public AllocatedBuffer allocateHeapBuffer(int capacity) {
+ return AllocatedBuffer.wrap(new byte[capacity]);
+ }
+
+ @Override
+ public AllocatedBuffer allocateDirectBuffer(int capacity) {
+ return AllocatedBuffer.wrap(ByteBuffer.allocateDirect(capacity));
+ }
+ };
+
+ /**
+ * Returns an unpooled buffer allocator, which will create a new buffer for each request.
+ */
+ public static BufferAllocator unpooled() {
+ return UNPOOLED;
+ }
+
+ /**
+ * Allocates a buffer with the given capacity that is backed by an array on the heap.
+ */
+ public abstract AllocatedBuffer allocateHeapBuffer(int capacity);
+
+ /**
+ * Allocates a direct (i.e. non-heap) buffer with the given capacity.
+ */
+ public abstract AllocatedBuffer allocateDirectBuffer(int capacity);
+}
+
diff --git a/java/core/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java
index 5b24976d..9d640385 100644
--- a/java/core/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/ByteString.java
@@ -28,6 +28,7 @@
// (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;
@@ -52,9 +53,9 @@ import java.util.NoSuchElementException;
/**
* Immutable sequence of bytes. Substring is supported by sharing the reference
- * to the immutable underlying bytes. Concatenation is likewise supported
- * without copying (long strings) by building a tree of pieces in
- * {@link RopeByteString}.
+ * to the immutable underlying bytes, as with {@link String}. Concatenation is
+ * likewise supported without copying (long strings) by building a tree of
+ * pieces in {@link RopeByteString}.
* <p>
* Like {@link String}, the contents of a {@link ByteString} can never be
* observed to change, not even in the presence of a data race or incorrect
@@ -691,6 +692,16 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*/
abstract void writeTo(ByteOutput byteOutput) throws IOException;
+ /**
+ * This method behaves exactly the same as {@link #writeTo(ByteOutput)} unless the {@link
+ * ByteString} is a rope. For ropes, the leaf nodes are written in reverse order to the {@code
+ * byteOutput}.
+ *
+ * @param byteOutput the output target to receive the bytes
+ * @throws IOException if an I/O error occurs
+ * @see UnsafeByteOperations#unsafeWriteToReverse(ByteString, ByteOutput)
+ */
+ abstract void writeToReverse(ByteOutput byteOutput) throws IOException;
/**
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
@@ -833,6 +844,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
return true;
}
+ @Override
+ void writeToReverse(ByteOutput byteOutput) throws IOException {
+ writeTo(byteOutput);
+ }
/**
* Check equality of the substring of given length of this object starting at
diff --git a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java
new file mode 100644
index 00000000..92f0470c
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java
@@ -0,0 +1,264 @@
+// 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.FieldInfo.forField;
+import static com.google.protobuf.FieldInfo.forPresenceCheckedField;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/** A factory for message info based on protobuf descriptors for a {@link GeneratedMessage}. */
+@ExperimentalApi
+public final class DescriptorMessageInfoFactory implements MessageInfoFactory {
+ private static final String GET_DEFAULT_INSTANCE_METHOD_NAME = "getDefaultInstance";
+ private static final DescriptorMessageInfoFactory instance = new DescriptorMessageInfoFactory();
+
+ // Disallow construction - it's a singleton.
+ private DescriptorMessageInfoFactory() {
+ }
+
+ public static DescriptorMessageInfoFactory getInstance() {
+ return instance;
+ }
+
+ @Override
+ public MessageInfo messageInfoFor(Class<?> messageType) {
+ if (!GeneratedMessage.class.isAssignableFrom(messageType)) {
+ throw new IllegalArgumentException("Only generated protobuf messages are supported");
+ }
+
+ return convert(messageType, descriptorForType(messageType));
+ }
+
+ private static Descriptor descriptorForType(Class<?> messageType) {
+ try {
+ Method method = messageType.getDeclaredMethod(GET_DEFAULT_INSTANCE_METHOD_NAME);
+ GeneratedMessage message = (GeneratedMessage) method.invoke(null);
+ return message.getDescriptorForType();
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Unable to get default instance for message class " + messageType.getName(), e);
+ }
+ }
+
+ private static MessageInfo convert(Class<?> messageType, Descriptor desc) {
+ switch (desc.getFile().getSyntax()) {
+ case PROTO2:
+ return convertProto2(messageType, desc);
+ case PROTO3:
+ return convertProto3(messageType, desc);
+ default:
+ throw new IllegalArgumentException("Unsupported syntax: " + desc.getFile().getSyntax());
+ }
+ }
+
+ private static MessageInfo convertProto2(Class<?> messageType, Descriptor desc) {
+ List<FieldDescriptor> fieldDescriptors = desc.getFields();
+ MessageInfo.Builder builder = MessageInfo.newBuilder(fieldDescriptors.size());
+ builder.withSyntax(ProtoSyntax.PROTO2);
+
+ int bitFieldIndex = 0;
+ int presenceMask = 1;
+ Field bitField = bitField(messageType, bitFieldIndex++);
+
+ // Fields in the descriptor are ordered by the index position in which they appear in the
+ // proto file. This is the same order used to determine the presence mask used in the
+ // bitFields. So to determine the appropriate presence mask to be used for a field, we simply
+ // need to shift the presence mask whenever a presence-checked field is encountered.
+ for (int i = 0; i < fieldDescriptors.size(); ++i) {
+ FieldDescriptor fd = fieldDescriptors.get(i);
+ Field field = field(messageType, fd);
+ int number = fd.getNumber();
+ FieldType type = getFieldType(fd);
+
+ if (fd.isRepeated()) {
+ // Repeated fields are not presence-checked.
+ builder.add(forField(field, number, type));
+ continue;
+ }
+
+ // It's a presence-checked field.
+ builder.add(forPresenceCheckedField(field, number, type, bitField, presenceMask));
+
+ // Update the presence mask/bitfield
+ presenceMask <<= 1;
+ if (presenceMask == 0) {
+ // We've assigned all of the bits in the current bitField. Advance to the next one.
+ bitField = bitField(messageType, bitFieldIndex++);
+ }
+ }
+
+ return builder.build();
+ }
+
+ private static MessageInfo convertProto3(Class<?> messageType, Descriptor desc) {
+ List<FieldDescriptor> fieldDescriptors = desc.getFields();
+ MessageInfo.Builder builder = MessageInfo.newBuilder(fieldDescriptors.size());
+ builder.withSyntax(ProtoSyntax.PROTO3);
+ for (int i = 0; i < fieldDescriptors.size(); ++i) {
+ FieldDescriptor fd = fieldDescriptors.get(i);
+ builder.add(forField(field(messageType, fd), fd.getNumber(), getFieldType(fd)));
+ }
+
+ return builder.build();
+ }
+
+ private static FieldType getFieldType(FieldDescriptor fd) {
+ switch (fd.getType()) {
+ case BOOL:
+ if (!fd.isRepeated()) {
+ return FieldType.BOOL;
+ }
+ return fd.isPacked() ? FieldType.BOOL_LIST_PACKED : FieldType.BOOL_LIST;
+ case BYTES:
+ return fd.isRepeated() ? FieldType.BYTES_LIST : FieldType.BYTES;
+ case DOUBLE:
+ if (!fd.isRepeated()) {
+ return FieldType.DOUBLE;
+ }
+ return fd.isPacked() ? FieldType.DOUBLE_LIST_PACKED : FieldType.DOUBLE_LIST;
+ case ENUM:
+ if (!fd.isRepeated()) {
+ return FieldType.ENUM;
+ }
+ return fd.isPacked() ? FieldType.ENUM_LIST_PACKED : FieldType.ENUM_LIST;
+ case FIXED32:
+ if (!fd.isRepeated()) {
+ return FieldType.FIXED32;
+ }
+ return fd.isPacked() ? FieldType.FIXED32_LIST_PACKED : FieldType.FIXED32_LIST;
+ case FIXED64:
+ if (!fd.isRepeated()) {
+ return FieldType.FIXED64;
+ }
+ return fd.isPacked() ? FieldType.FIXED64_LIST_PACKED : FieldType.FIXED64_LIST;
+ case FLOAT:
+ if (!fd.isRepeated()) {
+ return FieldType.FLOAT;
+ }
+ return fd.isPacked() ? FieldType.FLOAT_LIST_PACKED : FieldType.FLOAT_LIST;
+ case GROUP:
+ return fd.isRepeated() ? FieldType.GROUP_LIST : FieldType.GROUP;
+ case INT32:
+ if (!fd.isRepeated()) {
+ return FieldType.INT32;
+ }
+ return fd.isPacked() ? FieldType.INT32_LIST_PACKED : FieldType.INT32_LIST;
+ case INT64:
+ if (!fd.isRepeated()) {
+ return FieldType.INT64;
+ }
+ return fd.isPacked() ? FieldType.INT64_LIST_PACKED : FieldType.INT64_LIST;
+ case MESSAGE:
+ // TODO(nathanmittler): Add support for maps.
+ return fd.isRepeated() ? FieldType.MESSAGE_LIST : FieldType.MESSAGE;
+ case SFIXED32:
+ if (!fd.isRepeated()) {
+ return FieldType.SFIXED32;
+ }
+ return fd.isPacked() ? FieldType.SFIXED32_LIST_PACKED : FieldType.SFIXED32_LIST;
+ case SFIXED64:
+ if (!fd.isRepeated()) {
+ return FieldType.SFIXED64;
+ }
+ return fd.isPacked() ? FieldType.SFIXED64_LIST_PACKED : FieldType.SFIXED64_LIST;
+ case SINT32:
+ if (!fd.isRepeated()) {
+ return FieldType.SINT32;
+ }
+ return fd.isPacked() ? FieldType.SINT32_LIST_PACKED : FieldType.SINT32_LIST;
+ case SINT64:
+ if (!fd.isRepeated()) {
+ return FieldType.SINT64;
+ }
+ return fd.isPacked() ? FieldType.SINT64_LIST_PACKED : FieldType.SINT64_LIST;
+ case STRING:
+ return fd.isRepeated() ? FieldType.STRING_LIST : FieldType.STRING;
+ case UINT32:
+ if (!fd.isRepeated()) {
+ return FieldType.UINT32;
+ }
+ return fd.isPacked() ? FieldType.UINT32_LIST_PACKED : FieldType.UINT32_LIST;
+ case UINT64:
+ if (!fd.isRepeated()) {
+ return FieldType.UINT64;
+ }
+ return fd.isPacked() ? FieldType.UINT64_LIST_PACKED : FieldType.UINT64_LIST;
+ default:
+ throw new IllegalArgumentException("Unsupported field type: " + fd.getType());
+ }
+ }
+
+ private static Field bitField(Class<?> messageType, int index) {
+ return field(messageType, "bitField" + index + "_");
+ }
+
+ private static Field field(Class<?> messageType, FieldDescriptor fd) {
+ return field(messageType, getFieldName(fd));
+ }
+
+ private static Field field(Class<?> messageType, String fieldName) {
+ try {
+ return messageType.getDeclaredField(fieldName);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Unable to find field " + fieldName + " in message class " + messageType.getName());
+ }
+ }
+
+ // This method must match exactly with the corresponding function in protocol compiler.
+ // See: https://github.com/google/protobuf/blob/v3.0.0/src/google/protobuf/compiler/java/java_helpers.cc#L153
+ private static String getFieldName(FieldDescriptor fd) {
+ String snakeCase =
+ fd.getType() == FieldDescriptor.Type.GROUP ? fd.getMessageType().getName() : fd.getName();
+ StringBuilder sb = new StringBuilder(snakeCase.length() + 1);
+ boolean capNext = false;
+ for (int ctr = 0; ctr < snakeCase.length(); ctr++) {
+ char next = snakeCase.charAt(ctr);
+ if (next == '_') {
+ capNext = true;
+ } else if (capNext) {
+ sb.append(Character.toUpperCase(next));
+ capNext = false;
+ } else if (ctr == 0) {
+ sb.append(Character.toLowerCase(next));
+ } else {
+ sb.append(next);
+ }
+ }
+ sb.append('_');
+ return sb.toString();
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/FieldInfo.java b/java/core/src/main/java/com/google/protobuf/FieldInfo.java
new file mode 100644
index 00000000..37d7b102
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/FieldInfo.java
@@ -0,0 +1,175 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static com.google.protobuf.Internal.checkNotNull;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/** Information for a single field in a protobuf message class. */
+@ExperimentalApi
+public final class FieldInfo implements Comparable<FieldInfo> {
+ private final Field field;
+ private final FieldType type;
+ private final int fieldNumber;
+ private final Field presenceField;
+ private final int presenceMask;
+
+ /** Constructs a new descriptor for a field. */
+ public static FieldInfo forField(Field field, int fieldNumber, FieldType fieldType) {
+ return new FieldInfo(field, fieldNumber, fieldType, null, 0);
+ }
+
+ /** Constructor for a field that uses a presence bit field (i.e. proto2 only). */
+ public static FieldInfo forPresenceCheckedField(
+ Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask) {
+ checkNotNull(presenceField, "presenceField");
+ return new FieldInfo(field, fieldNumber, fieldType, presenceField, presenceMask);
+ }
+
+ private FieldInfo(
+ Field field, int fieldNumber, FieldType type, Field presenceField, int presenceMask) {
+ if (fieldNumber <= 0) {
+ throw new IllegalArgumentException("fieldNumber must be positive: " + fieldNumber);
+ }
+ if (presenceField != null && !isExactlyOneBitSet(presenceMask)) {
+ throw new IllegalArgumentException(
+ "presenceMask must have exactly one bit set: " + presenceMask);
+ }
+ this.field = checkNotNull(field, "field");
+ this.type = checkNotNull(type, "type");
+ this.fieldNumber = fieldNumber;
+ this.presenceField = presenceField;
+ this.presenceMask = presenceMask;
+ }
+
+ /** Gets the subject {@link Field} of this descriptor. */
+ public Field getField() {
+ return field;
+ }
+
+ /** Gets the type information for the field. */
+ public FieldType getType() {
+ return type;
+ }
+
+ /** Gets the field number for the field. */
+ public int getFieldNumber() {
+ return fieldNumber;
+ }
+
+ @Override
+ public int compareTo(FieldInfo o) {
+ return fieldNumber - o.fieldNumber;
+ }
+
+ /**
+ * For list fields, returns the generic argument that represents the type stored in the list. For
+ * non-list fields, returns {@code null}.
+ */
+ public Class<?> getListElementType() {
+ if (!type.isList()) {
+ return null;
+ }
+
+ Type genericType = field.getGenericType();
+ if (!(genericType instanceof ParameterizedType)) {
+ throw new IllegalStateException(
+ "Cannot determine parameterized type for list field " + fieldNumber);
+ }
+
+ Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+ return (Class<?>) type;
+ }
+
+ /** Gets the presence bit field. Only valid for unary fields. For lists, returns {@code null}. */
+ public Field getPresenceField() {
+ return presenceField;
+ }
+
+ /**
+ * If {@link #getPresenceField()} is non-{@code null}, returns the mask used to identify the
+ * presence bit for this field in the message.
+ */
+ public int getPresenceMask() {
+ return presenceMask;
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /** A builder for {@link FieldInfo} instances. */
+ public static final class Builder {
+ private Field field;
+ private FieldType type;
+ private int fieldNumber;
+ private Field presenceField;
+ private int presenceMask;
+
+ private Builder() {}
+
+ public Builder withField(Field field) {
+ this.field = field;
+ return this;
+ }
+
+ public Builder withType(FieldType type) {
+ this.type = type;
+ return this;
+ }
+
+ public Builder withFieldNumber(int fieldNumber) {
+ this.fieldNumber = fieldNumber;
+ return this;
+ }
+
+ public Builder withPresenceField(Field presenceField) {
+ this.presenceField = checkNotNull(presenceField, "presenceField");
+ return this;
+ }
+
+ public Builder withPresenceMask(int presenceMask) {
+ this.presenceMask = presenceMask;
+ return this;
+ }
+
+ public FieldInfo build() {
+ return new FieldInfo(field, fieldNumber, type, presenceField, presenceMask);
+ }
+ }
+
+ private static boolean isExactlyOneBitSet(int value) {
+ return value != 0 && (value & (value - 1)) == 0;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/FieldType.java b/java/core/src/main/java/com/google/protobuf/FieldType.java
new file mode 100644
index 00000000..1e368c11
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/FieldType.java
@@ -0,0 +1,535 @@
+// 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.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.List;
+
+/**
+ * Enumeration identifying all relevant type information for a protobuf field.
+ */
+@ExperimentalApi
+public enum FieldType {
+ DOUBLE(0, Collection.SCALAR, JavaType.DOUBLE, JavaType.VOID),
+ FLOAT(1, Collection.SCALAR, JavaType.FLOAT, JavaType.VOID),
+ INT64(2, Collection.SCALAR, JavaType.LONG, JavaType.VOID),
+ UINT64(3, Collection.SCALAR, JavaType.LONG, JavaType.VOID),
+ INT32(4, Collection.SCALAR, JavaType.INT, JavaType.VOID),
+ FIXED64(5, Collection.SCALAR, JavaType.LONG, JavaType.VOID),
+ FIXED32(6, Collection.SCALAR, JavaType.INT, JavaType.VOID),
+ BOOL(7, Collection.SCALAR, JavaType.BOOLEAN, JavaType.VOID),
+ STRING(8, Collection.SCALAR, JavaType.STRING, JavaType.VOID),
+ MESSAGE(9, Collection.SCALAR, JavaType.MESSAGE, JavaType.VOID),
+ BYTES(10, Collection.SCALAR, JavaType.BYTE_STRING, JavaType.VOID),
+ UINT32(11, Collection.SCALAR, JavaType.INT, JavaType.VOID),
+ ENUM(12, Collection.SCALAR, JavaType.ENUM, JavaType.VOID),
+ SFIXED32(13, Collection.SCALAR, JavaType.INT, JavaType.VOID),
+ SFIXED64(14, Collection.SCALAR, JavaType.LONG, JavaType.VOID),
+ SINT32(15, Collection.SCALAR, JavaType.INT, JavaType.VOID),
+ SINT64(16, Collection.SCALAR, JavaType.LONG, JavaType.VOID),
+ DOUBLE_LIST(17, Collection.VECTOR, JavaType.DOUBLE, JavaType.VOID),
+ FLOAT_LIST(18, Collection.VECTOR, JavaType.FLOAT, JavaType.VOID),
+ INT64_LIST(19, Collection.VECTOR, JavaType.LONG, JavaType.VOID),
+ UINT64_LIST(20, Collection.VECTOR, JavaType.LONG, JavaType.VOID),
+ INT32_LIST(21, Collection.VECTOR, JavaType.INT, JavaType.VOID),
+ FIXED64_LIST(22, Collection.VECTOR, JavaType.LONG, JavaType.VOID),
+ FIXED32_LIST(23, Collection.VECTOR, JavaType.INT, JavaType.VOID),
+ BOOL_LIST(24, Collection.VECTOR, JavaType.BOOLEAN, JavaType.VOID),
+ STRING_LIST(25, Collection.VECTOR, JavaType.STRING, JavaType.VOID),
+ MESSAGE_LIST(26, Collection.VECTOR, JavaType.MESSAGE, JavaType.VOID),
+ BYTES_LIST(27, Collection.VECTOR, JavaType.BYTE_STRING, JavaType.VOID),
+ UINT32_LIST(28, Collection.VECTOR, JavaType.INT, JavaType.VOID),
+ ENUM_LIST(29, Collection.VECTOR, JavaType.ENUM, JavaType.VOID),
+ SFIXED32_LIST(30, Collection.VECTOR, JavaType.INT, JavaType.VOID),
+ SFIXED64_LIST(31, Collection.VECTOR, JavaType.LONG, JavaType.VOID),
+ SINT32_LIST(32, Collection.VECTOR, JavaType.INT, JavaType.VOID),
+ SINT64_LIST(33, Collection.VECTOR, JavaType.LONG, JavaType.VOID),
+ DOUBLE_LIST_PACKED(34, Collection.PACKED_VECTOR, JavaType.DOUBLE, JavaType.VOID),
+ FLOAT_LIST_PACKED(35, Collection.PACKED_VECTOR, JavaType.FLOAT, JavaType.VOID),
+ INT64_LIST_PACKED(36, Collection.PACKED_VECTOR, JavaType.LONG, JavaType.VOID),
+ UINT64_LIST_PACKED(37, Collection.PACKED_VECTOR, JavaType.LONG, JavaType.VOID),
+ INT32_LIST_PACKED(38, Collection.PACKED_VECTOR, JavaType.INT, JavaType.VOID),
+ FIXED64_LIST_PACKED(39, Collection.PACKED_VECTOR, JavaType.LONG, JavaType.VOID),
+ FIXED32_LIST_PACKED(40, Collection.PACKED_VECTOR, JavaType.INT, JavaType.VOID),
+ BOOL_LIST_PACKED(41, Collection.PACKED_VECTOR, JavaType.BOOLEAN, JavaType.VOID),
+ UINT32_LIST_PACKED(42, Collection.PACKED_VECTOR, JavaType.INT, JavaType.VOID),
+ ENUM_LIST_PACKED(43, Collection.PACKED_VECTOR, JavaType.ENUM, JavaType.VOID),
+ SFIXED32_LIST_PACKED(44, Collection.PACKED_VECTOR, JavaType.INT, JavaType.VOID),
+ SFIXED64_LIST_PACKED(45, Collection.PACKED_VECTOR, JavaType.LONG, JavaType.VOID),
+ SINT32_LIST_PACKED(46, Collection.PACKED_VECTOR, JavaType.INT, JavaType.VOID),
+ SINT64_LIST_PACKED(47, Collection.PACKED_VECTOR, JavaType.LONG, JavaType.VOID),
+ INT32_TO_INT32_MAP(48, Collection.MAP, JavaType.INT, JavaType.INT),
+ INT32_TO_INT64_MAP(49, Collection.MAP, JavaType.INT, JavaType.LONG),
+ INT32_TO_UINT32_MAP(50, Collection.MAP, JavaType.INT, JavaType.INT),
+ INT32_TO_UINT64_MAP(51, Collection.MAP, JavaType.INT, JavaType.LONG),
+ INT32_TO_SINT32_MAP(52, Collection.MAP, JavaType.INT, JavaType.INT),
+ INT32_TO_SINT64_MAP(53, Collection.MAP, JavaType.INT, JavaType.LONG),
+ INT32_TO_FIXED32_MAP(54, Collection.MAP, JavaType.INT, JavaType.INT),
+ INT32_TO_FIXED64_MAP(55, Collection.MAP, JavaType.INT, JavaType.LONG),
+ INT32_TO_SFIXED32_MAP(56, Collection.MAP, JavaType.INT, JavaType.INT),
+ INT32_TO_SFIXED64_MAP(57, Collection.MAP, JavaType.INT, JavaType.LONG),
+ INT32_TO_BOOL_MAP(58, Collection.MAP, JavaType.INT, JavaType.BOOLEAN),
+ INT32_TO_STRING_MAP(59, Collection.MAP, JavaType.INT, JavaType.STRING),
+ INT32_TO_ENUM_MAP(60, Collection.MAP, JavaType.INT, JavaType.ENUM),
+ INT32_TO_MESSAGE_MAP(61, Collection.MAP, JavaType.INT, JavaType.MESSAGE),
+ INT32_TO_BYTES_MAP(62, Collection.MAP, JavaType.INT, JavaType.BYTE_STRING),
+ INT32_TO_DOUBLE_MAP(63, Collection.MAP, JavaType.INT, JavaType.DOUBLE),
+ INT32_TO_FLOAT_MAP(64, Collection.MAP, JavaType.INT, JavaType.FLOAT),
+ INT64_TO_INT32_MAP(65, Collection.MAP, JavaType.LONG, JavaType.INT),
+ INT64_TO_INT64_MAP(66, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ INT64_TO_UINT32_MAP(67, Collection.MAP, JavaType.LONG, JavaType.INT),
+ INT64_TO_UINT64_MAP(68, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ INT64_TO_SINT32_MAP(69, Collection.MAP, JavaType.LONG, JavaType.INT),
+ INT64_TO_SINT64_MAP(70, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ INT64_TO_FIXED32_MAP(71, Collection.MAP, JavaType.LONG, JavaType.INT),
+ INT64_TO_FIXED64_MAP(72, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ INT64_TO_SFIXED32_MAP(73, Collection.MAP, JavaType.LONG, JavaType.INT),
+ INT64_TO_SFIXED64_MAP(74, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ INT64_TO_BOOL_MAP(75, Collection.MAP, JavaType.LONG, JavaType.BOOLEAN),
+ INT64_TO_STRING_MAP(76, Collection.MAP, JavaType.LONG, JavaType.STRING),
+ INT64_TO_ENUM_MAP(77, Collection.MAP, JavaType.LONG, JavaType.ENUM),
+ INT64_TO_MESSAGE_MAP(78, Collection.MAP, JavaType.LONG, JavaType.MESSAGE),
+ INT64_TO_BYTES_MAP(79, Collection.MAP, JavaType.LONG, JavaType.BYTE_STRING),
+ INT64_TO_DOUBLE_MAP(80, Collection.MAP, JavaType.LONG, JavaType.DOUBLE),
+ INT64_TO_FLOAT_MAP(81, Collection.MAP, JavaType.LONG, JavaType.FLOAT),
+ UINT32_TO_INT32_MAP(82, Collection.MAP, JavaType.INT, JavaType.INT),
+ UINT32_TO_INT64_MAP(83, Collection.MAP, JavaType.INT, JavaType.LONG),
+ UINT32_TO_UINT32_MAP(84, Collection.MAP, JavaType.INT, JavaType.INT),
+ UINT32_TO_UINT64_MAP(85, Collection.MAP, JavaType.INT, JavaType.LONG),
+ UINT32_TO_SINT32_MAP(86, Collection.MAP, JavaType.INT, JavaType.INT),
+ UINT32_TO_SINT64_MAP(87, Collection.MAP, JavaType.INT, JavaType.LONG),
+ UINT32_TO_FIXED32_MAP(88, Collection.MAP, JavaType.INT, JavaType.INT),
+ UINT32_TO_FIXED64_MAP(89, Collection.MAP, JavaType.INT, JavaType.LONG),
+ UINT32_TO_SFIXED32_MAP(90, Collection.MAP, JavaType.INT, JavaType.INT),
+ UINT32_TO_SFIXED64_MAP(91, Collection.MAP, JavaType.INT, JavaType.LONG),
+ UINT32_TO_BOOL_MAP(92, Collection.MAP, JavaType.INT, JavaType.BOOLEAN),
+ UINT32_TO_STRING_MAP(93, Collection.MAP, JavaType.INT, JavaType.STRING),
+ UINT32_TO_ENUM_MAP(94, Collection.MAP, JavaType.INT, JavaType.ENUM),
+ UINT32_TO_MESSAGE_MAP(95, Collection.MAP, JavaType.INT, JavaType.MESSAGE),
+ UINT32_TO_BYTES_MAP(96, Collection.MAP, JavaType.INT, JavaType.BYTE_STRING),
+ UINT32_TO_DOUBLE_MAP(97, Collection.MAP, JavaType.INT, JavaType.DOUBLE),
+ UINT32_TO_FLOAT_MAP(98, Collection.MAP, JavaType.INT, JavaType.FLOAT),
+ UINT64_TO_INT32_MAP(99, Collection.MAP, JavaType.LONG, JavaType.INT),
+ UINT64_TO_INT64_MAP(100, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ UINT64_TO_UINT32_MAP(101, Collection.MAP, JavaType.LONG, JavaType.INT),
+ UINT64_TO_UINT64_MAP(102, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ UINT64_TO_SINT32_MAP(103, Collection.MAP, JavaType.LONG, JavaType.INT),
+ UINT64_TO_SINT64_MAP(104, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ UINT64_TO_FIXED32_MAP(105, Collection.MAP, JavaType.LONG, JavaType.INT),
+ UINT64_TO_FIXED64_MAP(106, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ UINT64_TO_SFIXED32_MAP(107, Collection.MAP, JavaType.LONG, JavaType.INT),
+ UINT64_TO_SFIXED64_MAP(108, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ UINT64_TO_BOOL_MAP(109, Collection.MAP, JavaType.LONG, JavaType.BOOLEAN),
+ UINT64_TO_STRING_MAP(110, Collection.MAP, JavaType.LONG, JavaType.STRING),
+ UINT64_TO_ENUM_MAP(111, Collection.MAP, JavaType.LONG, JavaType.ENUM),
+ UINT64_TO_MESSAGE_MAP(112, Collection.MAP, JavaType.LONG, JavaType.MESSAGE),
+ UINT64_TO_BYTES_MAP(113, Collection.MAP, JavaType.LONG, JavaType.BYTE_STRING),
+ UINT64_TO_DOUBLE_MAP(114, Collection.MAP, JavaType.LONG, JavaType.DOUBLE),
+ UINT64_TO_FLOAT_MAP(115, Collection.MAP, JavaType.LONG, JavaType.FLOAT),
+ SINT32_TO_INT32_MAP(116, Collection.MAP, JavaType.INT, JavaType.INT),
+ SINT32_TO_INT64_MAP(117, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SINT32_TO_UINT32_MAP(118, Collection.MAP, JavaType.INT, JavaType.INT),
+ SINT32_TO_UINT64_MAP(119, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SINT32_TO_SINT32_MAP(120, Collection.MAP, JavaType.INT, JavaType.INT),
+ SINT32_TO_SINT64_MAP(121, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SINT32_TO_FIXED32_MAP(122, Collection.MAP, JavaType.INT, JavaType.INT),
+ SINT32_TO_FIXED64_MAP(123, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SINT32_TO_SFIXED32_MAP(124, Collection.MAP, JavaType.INT, JavaType.INT),
+ SINT32_TO_SFIXED64_MAP(125, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SINT32_TO_BOOL_MAP(126, Collection.MAP, JavaType.INT, JavaType.BOOLEAN),
+ SINT32_TO_STRING_MAP(127, Collection.MAP, JavaType.INT, JavaType.STRING),
+ SINT32_TO_ENUM_MAP(128, Collection.MAP, JavaType.INT, JavaType.ENUM),
+ SINT32_TO_MESSAGE_MAP(129, Collection.MAP, JavaType.INT, JavaType.MESSAGE),
+ SINT32_TO_BYTES_MAP(130, Collection.MAP, JavaType.INT, JavaType.BYTE_STRING),
+ SINT32_TO_DOUBLE_MAP(131, Collection.MAP, JavaType.INT, JavaType.DOUBLE),
+ SINT32_TO_FLOAT_MAP(132, Collection.MAP, JavaType.INT, JavaType.FLOAT),
+ SINT64_TO_INT32_MAP(133, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SINT64_TO_INT64_MAP(134, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SINT64_TO_UINT32_MAP(135, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SINT64_TO_UINT64_MAP(136, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SINT64_TO_SINT32_MAP(137, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SINT64_TO_SINT64_MAP(138, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SINT64_TO_FIXED32_MAP(139, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SINT64_TO_FIXED64_MAP(140, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SINT64_TO_SFIXED32_MAP(141, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SINT64_TO_SFIXED64_MAP(142, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SINT64_TO_BOOL_MAP(143, Collection.MAP, JavaType.LONG, JavaType.BOOLEAN),
+ SINT64_TO_STRING_MAP(144, Collection.MAP, JavaType.LONG, JavaType.STRING),
+ SINT64_TO_ENUM_MAP(145, Collection.MAP, JavaType.LONG, JavaType.ENUM),
+ SINT64_TO_MESSAGE_MAP(146, Collection.MAP, JavaType.LONG, JavaType.MESSAGE),
+ SINT64_TO_BYTES_MAP(147, Collection.MAP, JavaType.LONG, JavaType.BYTE_STRING),
+ SINT64_TO_DOUBLE_MAP(148, Collection.MAP, JavaType.LONG, JavaType.DOUBLE),
+ SINT64_TO_FLOAT_MAP(149, Collection.MAP, JavaType.LONG, JavaType.FLOAT),
+ FIXED32_TO_INT32_MAP(150, Collection.MAP, JavaType.INT, JavaType.INT),
+ FIXED32_TO_INT64_MAP(151, Collection.MAP, JavaType.INT, JavaType.LONG),
+ FIXED32_TO_UINT32_MAP(152, Collection.MAP, JavaType.INT, JavaType.INT),
+ FIXED32_TO_UINT64_MAP(153, Collection.MAP, JavaType.INT, JavaType.LONG),
+ FIXED32_TO_SINT32_MAP(154, Collection.MAP, JavaType.INT, JavaType.INT),
+ FIXED32_TO_SINT64_MAP(155, Collection.MAP, JavaType.INT, JavaType.LONG),
+ FIXED32_TO_FIXED32_MAP(156, Collection.MAP, JavaType.INT, JavaType.INT),
+ FIXED32_TO_FIXED64_MAP(157, Collection.MAP, JavaType.INT, JavaType.LONG),
+ FIXED32_TO_SFIXED32_MAP(158, Collection.MAP, JavaType.INT, JavaType.INT),
+ FIXED32_TO_SFIXED64_MAP(159, Collection.MAP, JavaType.INT, JavaType.LONG),
+ FIXED32_TO_BOOL_MAP(160, Collection.MAP, JavaType.INT, JavaType.BOOLEAN),
+ FIXED32_TO_STRING_MAP(161, Collection.MAP, JavaType.INT, JavaType.STRING),
+ FIXED32_TO_ENUM_MAP(162, Collection.MAP, JavaType.INT, JavaType.ENUM),
+ FIXED32_TO_MESSAGE_MAP(163, Collection.MAP, JavaType.INT, JavaType.MESSAGE),
+ FIXED32_TO_BYTES_MAP(164, Collection.MAP, JavaType.INT, JavaType.BYTE_STRING),
+ FIXED32_TO_DOUBLE_MAP(165, Collection.MAP, JavaType.INT, JavaType.DOUBLE),
+ FIXED32_TO_FLOAT_MAP(166, Collection.MAP, JavaType.INT, JavaType.FLOAT),
+ FIXED64_TO_INT32_MAP(167, Collection.MAP, JavaType.LONG, JavaType.INT),
+ FIXED64_TO_INT64_MAP(168, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ FIXED64_TO_UINT32_MAP(169, Collection.MAP, JavaType.LONG, JavaType.INT),
+ FIXED64_TO_UINT64_MAP(170, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ FIXED64_TO_SINT32_MAP(171, Collection.MAP, JavaType.LONG, JavaType.INT),
+ FIXED64_TO_SINT64_MAP(172, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ FIXED64_TO_FIXED32_MAP(173, Collection.MAP, JavaType.LONG, JavaType.INT),
+ FIXED64_TO_FIXED64_MAP(174, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ FIXED64_TO_SFIXED32_MAP(175, Collection.MAP, JavaType.LONG, JavaType.INT),
+ FIXED64_TO_SFIXED64_MAP(176, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ FIXED64_TO_BOOL_MAP(177, Collection.MAP, JavaType.LONG, JavaType.BOOLEAN),
+ FIXED64_TO_STRING_MAP(178, Collection.MAP, JavaType.LONG, JavaType.STRING),
+ FIXED64_TO_ENUM_MAP(179, Collection.MAP, JavaType.LONG, JavaType.ENUM),
+ FIXED64_TO_MESSAGE_MAP(180, Collection.MAP, JavaType.LONG, JavaType.MESSAGE),
+ FIXED64_TO_BYTES_MAP(181, Collection.MAP, JavaType.LONG, JavaType.BYTE_STRING),
+ FIXED64_TO_DOUBLE_MAP(182, Collection.MAP, JavaType.LONG, JavaType.DOUBLE),
+ FIXED64_TO_FLOAT_MAP(183, Collection.MAP, JavaType.LONG, JavaType.FLOAT),
+ SFIXED32_TO_INT32_MAP(184, Collection.MAP, JavaType.INT, JavaType.INT),
+ SFIXED32_TO_INT64_MAP(185, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SFIXED32_TO_UINT32_MAP(186, Collection.MAP, JavaType.INT, JavaType.INT),
+ SFIXED32_TO_UINT64_MAP(187, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SFIXED32_TO_SINT32_MAP(188, Collection.MAP, JavaType.INT, JavaType.INT),
+ SFIXED32_TO_SINT64_MAP(189, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SFIXED32_TO_FIXED32_MAP(190, Collection.MAP, JavaType.INT, JavaType.INT),
+ SFIXED32_TO_FIXED64_MAP(191, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SFIXED32_TO_SFIXED32_MAP(192, Collection.MAP, JavaType.INT, JavaType.INT),
+ SFIXED32_TO_SFIXED64_MAP(193, Collection.MAP, JavaType.INT, JavaType.LONG),
+ SFIXED32_TO_BOOL_MAP(194, Collection.MAP, JavaType.INT, JavaType.BOOLEAN),
+ SFIXED32_TO_STRING_MAP(195, Collection.MAP, JavaType.INT, JavaType.STRING),
+ SFIXED32_TO_ENUM_MAP(196, Collection.MAP, JavaType.INT, JavaType.ENUM),
+ SFIXED32_TO_MESSAGE_MAP(197, Collection.MAP, JavaType.INT, JavaType.MESSAGE),
+ SFIXED32_TO_BYTES_MAP(198, Collection.MAP, JavaType.INT, JavaType.BYTE_STRING),
+ SFIXED32_TO_DOUBLE_MAP(199, Collection.MAP, JavaType.INT, JavaType.DOUBLE),
+ SFIXED32_TO_FLOAT_MAP(200, Collection.MAP, JavaType.INT, JavaType.FLOAT),
+ SFIXED64_TO_INT32_MAP(201, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SFIXED64_TO_INT64_MAP(202, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SFIXED64_TO_UINT32_MAP(203, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SFIXED64_TO_UINT64_MAP(204, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SFIXED64_TO_SINT32_MAP(205, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SFIXED64_TO_SINT64_MAP(206, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SFIXED64_TO_FIXED32_MAP(207, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SFIXED64_TO_FIXED64_MAP(208, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SFIXED64_TO_SFIXED32_MAP(209, Collection.MAP, JavaType.LONG, JavaType.INT),
+ SFIXED64_TO_SFIXED64_MAP(210, Collection.MAP, JavaType.LONG, JavaType.LONG),
+ SFIXED64_TO_BOOL_MAP(211, Collection.MAP, JavaType.LONG, JavaType.BOOLEAN),
+ SFIXED64_TO_STRING_MAP(212, Collection.MAP, JavaType.LONG, JavaType.STRING),
+ SFIXED64_TO_ENUM_MAP(213, Collection.MAP, JavaType.LONG, JavaType.ENUM),
+ SFIXED64_TO_MESSAGE_MAP(214, Collection.MAP, JavaType.LONG, JavaType.MESSAGE),
+ SFIXED64_TO_BYTES_MAP(215, Collection.MAP, JavaType.LONG, JavaType.BYTE_STRING),
+ SFIXED64_TO_DOUBLE_MAP(216, Collection.MAP, JavaType.LONG, JavaType.DOUBLE),
+ SFIXED64_TO_FLOAT_MAP(217, Collection.MAP, JavaType.LONG, JavaType.FLOAT),
+ BOOL_TO_INT32_MAP(218, Collection.MAP, JavaType.BOOLEAN, JavaType.INT),
+ BOOL_TO_INT64_MAP(219, Collection.MAP, JavaType.BOOLEAN, JavaType.LONG),
+ BOOL_TO_UINT32_MAP(220, Collection.MAP, JavaType.BOOLEAN, JavaType.INT),
+ BOOL_TO_UINT64_MAP(221, Collection.MAP, JavaType.BOOLEAN, JavaType.LONG),
+ BOOL_TO_SINT32_MAP(222, Collection.MAP, JavaType.BOOLEAN, JavaType.INT),
+ BOOL_TO_SINT64_MAP(223, Collection.MAP, JavaType.BOOLEAN, JavaType.LONG),
+ BOOL_TO_FIXED32_MAP(224, Collection.MAP, JavaType.BOOLEAN, JavaType.INT),
+ BOOL_TO_FIXED64_MAP(225, Collection.MAP, JavaType.BOOLEAN, JavaType.LONG),
+ BOOL_TO_SFIXED32_MAP(226, Collection.MAP, JavaType.BOOLEAN, JavaType.INT),
+ BOOL_TO_SFIXED64_MAP(227, Collection.MAP, JavaType.BOOLEAN, JavaType.LONG),
+ BOOL_TO_BOOL_MAP(228, Collection.MAP, JavaType.BOOLEAN, JavaType.BOOLEAN),
+ BOOL_TO_STRING_MAP(229, Collection.MAP, JavaType.BOOLEAN, JavaType.STRING),
+ BOOL_TO_ENUM_MAP(230, Collection.MAP, JavaType.BOOLEAN, JavaType.ENUM),
+ BOOL_TO_MESSAGE_MAP(231, Collection.MAP, JavaType.BOOLEAN, JavaType.MESSAGE),
+ BOOL_TO_BYTES_MAP(232, Collection.MAP, JavaType.BOOLEAN, JavaType.BYTE_STRING),
+ BOOL_TO_DOUBLE_MAP(233, Collection.MAP, JavaType.BOOLEAN, JavaType.DOUBLE),
+ BOOL_TO_FLOAT_MAP(234, Collection.MAP, JavaType.BOOLEAN, JavaType.FLOAT),
+ STRING_TO_INT32_MAP(235, Collection.MAP, JavaType.STRING, JavaType.INT),
+ STRING_TO_INT64_MAP(236, Collection.MAP, JavaType.STRING, JavaType.LONG),
+ STRING_TO_UINT32_MAP(237, Collection.MAP, JavaType.STRING, JavaType.INT),
+ STRING_TO_UINT64_MAP(238, Collection.MAP, JavaType.STRING, JavaType.LONG),
+ STRING_TO_SINT32_MAP(239, Collection.MAP, JavaType.STRING, JavaType.INT),
+ STRING_TO_SINT64_MAP(240, Collection.MAP, JavaType.STRING, JavaType.LONG),
+ STRING_TO_FIXED32_MAP(241, Collection.MAP, JavaType.STRING, JavaType.INT),
+ STRING_TO_FIXED64_MAP(242, Collection.MAP, JavaType.STRING, JavaType.LONG),
+ STRING_TO_SFIXED32_MAP(243, Collection.MAP, JavaType.STRING, JavaType.INT),
+ STRING_TO_SFIXED64_MAP(244, Collection.MAP, JavaType.STRING, JavaType.LONG),
+ STRING_TO_BOOL_MAP(245, Collection.MAP, JavaType.STRING, JavaType.BOOLEAN),
+ STRING_TO_STRING_MAP(246, Collection.MAP, JavaType.STRING, JavaType.STRING),
+ STRING_TO_ENUM_MAP(247, Collection.MAP, JavaType.STRING, JavaType.ENUM),
+ STRING_TO_MESSAGE_MAP(248, Collection.MAP, JavaType.STRING, JavaType.MESSAGE),
+ STRING_TO_BYTES_MAP(249, Collection.MAP, JavaType.STRING, JavaType.BYTE_STRING),
+ STRING_TO_DOUBLE_MAP(250, Collection.MAP, JavaType.STRING, JavaType.DOUBLE),
+ STRING_TO_FLOAT_MAP(251, Collection.MAP, JavaType.STRING, JavaType.FLOAT),
+ GROUP(252, Collection.SCALAR, JavaType.MESSAGE, JavaType.VOID),
+ GROUP_LIST(253, Collection.VECTOR, JavaType.MESSAGE, JavaType.VOID),
+ GROUP_LIST_PACKED(254, Collection.PACKED_VECTOR, JavaType.MESSAGE, JavaType.VOID);
+
+ private final JavaType javaType1;
+ private final JavaType javaType2;
+ private final int id;
+ private final Collection collection;
+ private final Class<?> elementType1;
+ private final Class<?> elementType2;
+
+ FieldType(int id, Collection collection, JavaType javaType1, JavaType javaType2) {
+ this.id = id;
+ this.collection = collection;
+ this.javaType1 = javaType1;
+ this.javaType2 = javaType2;
+
+ switch (collection) {
+ case MAP:
+ elementType1 = javaType1.getBoxedType();
+ elementType2 = javaType2.getBoxedType();
+ break;
+ case VECTOR:
+ elementType1 = javaType1.getBoxedType();
+ elementType2 = null;
+ break;
+ case SCALAR:
+ default:
+ elementType1 = null;
+ elementType2 = null;
+ break;
+ }
+ }
+
+ /**
+ * A reliable unique identifier for this type.
+ */
+ public int id() {
+ return id;
+ }
+
+ /**
+ * Gets the {@link JavaType} for this field. For lists, this identifies the type of the elements
+ * contained within the list.
+ */
+ public JavaType getJavaType() {
+ return javaType1;
+ }
+
+ /**
+ * Indicates whether a list field should be represented on the wire in packed form.
+ */
+ public boolean isPacked() {
+ return Collection.PACKED_VECTOR.equals(collection);
+ }
+
+ /**
+ * Indicates whether this field represents a list of values.
+ */
+ public boolean isList() {
+ return collection.isList();
+ }
+
+ /**
+ * Indicates whether or not this {@link FieldType} can be applied to the given {@link Field}.
+ */
+ public boolean isValidForField(Field field) {
+ if (Collection.VECTOR.equals(collection)) {
+ return isValidForList(field);
+ } else {
+ return javaType1.getType().isAssignableFrom(field.getType());
+ }
+ }
+
+ private boolean isValidForList(Field field) {
+ Class<?> clazz = field.getType();
+ if (!javaType1.getType().isAssignableFrom(clazz)) {
+ // The field isn't a List type.
+ return false;
+ }
+ Type[] types = EMPTY_TYPES;
+ Type genericType = field.getGenericType();
+ if (genericType instanceof ParameterizedType) {
+ types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
+ }
+ Type listParameter = getListParameter(clazz, types);
+ if (!(listParameter instanceof Class)) {
+ // It's a wildcard, we should allow anything in the list.
+ return true;
+ }
+ return elementType1.isAssignableFrom((Class<?>) listParameter);
+ }
+
+ /**
+ * Looks up the appropriate {@link FieldType} by it's identifier.
+ *
+ * @return the {@link FieldType} or {@code null} if not found.
+ */
+ /* @Nullable */
+ public static FieldType forId(byte id) {
+ if (id < 0 || id >= VALUES.length) {
+ return null;
+ }
+ return VALUES[id];
+ }
+
+ private static final FieldType[] VALUES;
+ private static final Type[] EMPTY_TYPES = new Type[0];
+
+ static {
+ FieldType[] values = values();
+ VALUES = new FieldType[values.length];
+ for (FieldType type : values) {
+ VALUES[type.id] = type;
+ }
+ }
+
+ /**
+ * Given a class, finds a generic super class or interface that extends {@link List}.
+ *
+ * @return the generic super class/interface, or {@code null} if not found.
+ */
+ /* @Nullable */
+ private static Type getGenericSuperList(Class<?> clazz) {
+ // First look at interfaces.
+ Type[] genericInterfaces = clazz.getGenericInterfaces();
+ for (Type genericInterface : genericInterfaces) {
+ if (genericInterface instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
+ Class<?> rawType = (Class<?>) parameterizedType.getRawType();
+ if (List.class.isAssignableFrom(rawType)) {
+ return genericInterface;
+ }
+ }
+ }
+
+ // Try the subclass
+ Type type = clazz.getGenericSuperclass();
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Class<?> rawType = (Class<?>) parameterizedType.getRawType();
+ if (List.class.isAssignableFrom(rawType)) {
+ return type;
+ }
+ }
+
+ // No super class/interface extends List.
+ return null;
+ }
+
+ /**
+ * Inspects the inheritance hierarchy for the given class and finds the generic type parameter
+ * for {@link List}.
+ *
+ * @param clazz the class to begin the search.
+ * @param realTypes the array of actual type parameters for {@code clazz}. These will be used to
+ * substitute generic parameters up the inheritance hierarchy. If {@code clazz} does not have any
+ * generic parameters, this list should be empty.
+ * @return the {@link List} parameter.
+ */
+ private static Type getListParameter(Class<?> clazz, Type[] realTypes) {
+ top:
+ while (clazz != List.class) {
+ // First look at generic subclass and interfaces.
+ Type genericType = getGenericSuperList(clazz);
+ if (genericType instanceof ParameterizedType) {
+ // Replace any generic parameters with the real values.
+ ParameterizedType parameterizedType = (ParameterizedType) genericType;
+ Type[] superArgs = parameterizedType.getActualTypeArguments();
+ for (int i = 0; i < superArgs.length; ++i) {
+ Type superArg = superArgs[i];
+ if (superArg instanceof TypeVariable) {
+ // Get the type variables for this class so that we can match them to the variables
+ // used on the super class.
+ TypeVariable<?>[] clazzParams = clazz.getTypeParameters();
+ if (realTypes.length != clazzParams.length) {
+ throw new RuntimeException("Type array mismatch");
+ }
+
+ // Replace the variable parameter with the real type.
+ boolean foundReplacement = false;
+ for (int j = 0; j < clazzParams.length; ++j) {
+ if (superArg == clazzParams[j]) {
+ Type realType = realTypes[j];
+ superArgs[i] = realType;
+ foundReplacement = true;
+ break;
+ }
+ }
+ if (!foundReplacement) {
+ throw new RuntimeException("Unable to find replacement for " + superArg);
+ }
+ }
+ }
+
+ Class<?> parent = (Class<?>) parameterizedType.getRawType();
+
+ realTypes = superArgs;
+ clazz = parent;
+ continue;
+ }
+
+ // None of the parameterized types inherit List. Just continue up the inheritance hierarchy
+ // toward the List interface until we can identify the parameters.
+ realTypes = EMPTY_TYPES;
+ for (Class<?> iface : clazz.getInterfaces()) {
+ if (List.class.isAssignableFrom(iface)) {
+ clazz = iface;
+ continue top;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+
+ if (realTypes.length != 1) {
+ throw new RuntimeException("Unable to identify parameter type for List<T>");
+ }
+ return realTypes[0];
+ }
+
+ enum Collection {
+ SCALAR(false),
+ VECTOR(true),
+ PACKED_VECTOR(true),
+ MAP(false);
+
+ private final boolean isList;
+
+ Collection(boolean isList) {
+ this.isList = isList;
+ }
+
+ /**
+ * @return the isList
+ */
+ public boolean isList() {
+ return isList;
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Int2ObjectHashMap.java b/java/core/src/main/java/com/google/protobuf/Int2ObjectHashMap.java
new file mode 100644
index 00000000..7909ab28
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Int2ObjectHashMap.java
@@ -0,0 +1,754 @@
+// 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.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A hash map that uses primitive integers as keys with open addressing. To minimize the memory
+ * footprint, this class uses open addressing rather than chaining. Collisions are resolved using
+ * linear probing. Deletions implement compaction, so cost of remove can approach O(N) for full
+ * maps, which makes a small loadFactor recommended.
+ *
+ * @param <V> The value type stored in the map.
+ */
+@ExperimentalApi
+public final class Int2ObjectHashMap<V> implements Map<Integer, V> {
+ /**
+ * A primitive entry in the map, provided by the iterator from {@link #entries()}
+ *
+ * @param <V> the value type stored in the map.
+ */
+ public interface PrimitiveEntry<V> {
+ /** Gets the key for this entry. */
+ int key();
+
+ /** Gets the value for this entry. */
+ V value();
+
+ /** Sets the value for this entry. */
+ void setValue(V value);
+ }
+
+ /** Default initial capacity. Used if not specified in the constructor */
+ public static final int DEFAULT_CAPACITY = 8;
+
+ /** Default load factor. Used if not specified in the constructor */
+ public static final float DEFAULT_LOAD_FACTOR = 0.5f;
+
+ /**
+ * Placeholder for null values, so we can use the actual null to mean available. (Better than
+ * using a placeholder for available: less references for GC processing.)
+ */
+ private static final Object NULL_VALUE = new Object();
+
+ /** The maximum number of elements allowed without allocating more space. */
+ private int maxSize;
+
+ /** The load factor for the map. Used to calculate {@link #maxSize}. */
+ private final float loadFactor;
+
+ private int[] keys;
+ private V[] values;
+ private int size;
+ private int mask;
+
+ private final Set<Integer> keySet = new KeySet();
+ private final Set<Entry<Integer, V>> entrySet = new EntrySet();
+ private final Iterable<PrimitiveEntry<V>> entries =
+ new Iterable<PrimitiveEntry<V>>() {
+ @Override
+ public Iterator<PrimitiveEntry<V>> iterator() {
+ return new PrimitiveIterator();
+ }
+ };
+
+ public Int2ObjectHashMap() {
+ this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ public Int2ObjectHashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ public Int2ObjectHashMap(int initialCapacity, float loadFactor) {
+ if (initialCapacity < 1) {
+ throw new IllegalArgumentException("initialCapacity must be >= 1");
+ }
+ if (loadFactor <= 0.0f || loadFactor > 1.0f) {
+ // Cannot exceed 1 because we can never store more than capacity elements;
+ // using a bigger loadFactor would trigger rehashing before the desired load is reached.
+ throw new IllegalArgumentException("loadFactor must be > 0 and <= 1");
+ }
+
+ this.loadFactor = loadFactor;
+
+ // Adjust the initial capacity if necessary.
+ int capacity = findNextPositivePowerOfTwo(initialCapacity);
+ mask = capacity - 1;
+
+ // Allocate the arrays.
+ keys = new int[capacity];
+ @SuppressWarnings({"unchecked", "SuspiciousArrayCast"})
+ V[] temp = (V[]) new Object[capacity];
+ values = temp;
+
+ // Initialize the maximum size value.
+ maxSize = calcMaxSize(capacity);
+ }
+
+ private static <T> T toExternal(T value) {
+ return value == NULL_VALUE ? null : value;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T toInternal(T value) {
+ return value == null ? (T) NULL_VALUE : value;
+ }
+
+ public V get(int key) {
+ int index = indexOf(key);
+ return index == -1 ? null : toExternal(values[index]);
+ }
+
+ public V put(int key, V value) {
+ int startIndex = hashIndex(key);
+ int index = startIndex;
+
+ for (; ; ) {
+ if (values[index] == null) {
+ // Found empty slot, use it.
+ keys[index] = key;
+ values[index] = toInternal(value);
+ growSize();
+ return null;
+ }
+ if (keys[index] == key) {
+ // Found existing entry with this key, just replace the value.
+ V previousValue = values[index];
+ values[index] = toInternal(value);
+ return toExternal(previousValue);
+ }
+
+ // Conflict, keep probing ...
+ if ((index = probeNext(index)) == startIndex) {
+ // Can only happen if the map was full at MAX_ARRAY_SIZE and couldn't grow.
+ throw new IllegalStateException("Unable to insert");
+ }
+ }
+ }
+
+ @Override
+ public void putAll(Map<? extends Integer, ? extends V> sourceMap) {
+ if (sourceMap instanceof Int2ObjectHashMap) {
+ // Optimization - iterate through the arrays.
+ @SuppressWarnings("unchecked")
+ Int2ObjectHashMap<V> source = (Int2ObjectHashMap<V>) sourceMap;
+ for (int i = 0; i < source.values.length; ++i) {
+ V sourceValue = source.values[i];
+ if (sourceValue != null) {
+ put(source.keys[i], sourceValue);
+ }
+ }
+ return;
+ }
+
+ // Otherwise, just add each entry.
+ for (Entry<? extends Integer, ? extends V> entry : sourceMap.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ public V remove(int key) {
+ int index = indexOf(key);
+ if (index == -1) {
+ return null;
+ }
+
+ V prev = values[index];
+ removeAt(index);
+ return toExternal(prev);
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ @Override
+ public void clear() {
+ Arrays.fill(keys, 0);
+ Arrays.fill(values, null);
+ size = 0;
+ }
+
+ public boolean containsKey(int key) {
+ return indexOf(key) >= 0;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ @SuppressWarnings("unchecked")
+ V v1 = toInternal((V) value);
+ for (V v2 : values) {
+ // The map supports null values; this will be matched as NULL_VALUE.equals(NULL_VALUE).
+ if (v2 != null && v2.equals(v1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Iterable<PrimitiveEntry<V>> entries() {
+ return entries;
+ }
+
+ @Override
+ public Collection<V> values() {
+ return new AbstractCollection<V>() {
+ @Override
+ public Iterator<V> iterator() {
+ return new Iterator<V>() {
+ final PrimitiveIterator iter = new PrimitiveIterator();
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public V next() {
+ return iter.next().value();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ @Override
+ public int hashCode() {
+ // Hashcode is based on all non-zero, valid keys. We have to scan the whole keys
+ // array, which may have different lengths for two maps of same size(), so the
+ // capacity cannot be used as input for hashing but the size can.
+ int hash = size;
+ for (int key : keys) {
+ // 0 can be a valid key or unused slot, but won't impact the hashcode in either case.
+ // This way we can use a cheap loop without conditionals, or hard-to-unroll operations,
+ // or the devastatingly bad memory locality of visiting value objects.
+ // Also, it's important to use a hash function that does not depend on the ordering
+ // of terms, only their values; since the map is an unordered collection and
+ // entries can end up in different positions in different maps that have the same
+ // elements, but with different history of puts/removes, due to conflicts.
+ hash ^= hashCode(key);
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Int2ObjectHashMap)) {
+ return false;
+ }
+ @SuppressWarnings("rawtypes")
+ Int2ObjectHashMap other = (Int2ObjectHashMap) obj;
+ if (size != other.size()) {
+ return false;
+ }
+ for (int i = 0; i < values.length; ++i) {
+ V value = values[i];
+ if (value != null) {
+ int key = keys[i];
+ Object otherValue = other.get(key);
+ if (value == NULL_VALUE) {
+ if (otherValue != null) {
+ return false;
+ }
+ } else if (!value.equals(otherValue)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return containsKey(objectToKey(key));
+ }
+
+ @Override
+ public V get(Object key) {
+ return get(objectToKey(key));
+ }
+
+ @Override
+ public V put(Integer key, V value) {
+ return put(objectToKey(key), value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return remove(objectToKey(key));
+ }
+
+ @Override
+ public Set<Integer> keySet() {
+ return keySet;
+ }
+
+ @Override
+ public Set<Entry<Integer, V>> entrySet() {
+ return entrySet;
+ }
+
+ private int objectToKey(Object key) {
+ return ((Integer) key).intValue();
+ }
+
+ /**
+ * Locates the index for the given key. This method probes using double hashing.
+ *
+ * @param key the key for an entry in the map.
+ * @return the index where the key was found, or {@code -1} if no entry is found for that key.
+ */
+ private int indexOf(int key) {
+ int startIndex = hashIndex(key);
+ int index = startIndex;
+
+ for (; ; ) {
+ if (values[index] == null) {
+ // It's available, so no chance that this value exists anywhere in the map.
+ return -1;
+ }
+ if (key == keys[index]) {
+ return index;
+ }
+
+ // Conflict, keep probing ...
+ if ((index = probeNext(index)) == startIndex) {
+ return -1;
+ }
+ }
+ }
+
+ /** Returns the hashed index for the given key. */
+ private int hashIndex(int key) {
+ // The array lengths are always a power of two, so we can use a bitmask to stay inside the
+ // array bounds.
+ return hashCode(key) & mask;
+ }
+
+ /** Returns the hash code for the key. */
+ private static int hashCode(int key) {
+ return key;
+ }
+
+ /** Get the next sequential index after {@code index} and wraps if necessary. */
+ private int probeNext(int index) {
+ // The array lengths are always a power of two, so we can use a bitmask to stay inside the
+ // array bounds.
+ return (index + 1) & mask;
+ }
+
+ /** Grows the map size after an insertion. If necessary, performs a rehash of the map. */
+ private void growSize() {
+ size++;
+
+ if (size > maxSize) {
+ if (keys.length == Integer.MAX_VALUE) {
+ throw new IllegalStateException("Max capacity reached at size=" + size);
+ }
+
+ // Double the capacity.
+ rehash(keys.length << 1);
+ }
+ }
+
+ /**
+ * Removes entry at the given index position. Also performs opportunistic, incremental rehashing
+ * if necessary to not break conflict chains.
+ *
+ * @param index the index position of the element to remove.
+ * @return {@code true} if the next item was moved back. {@code false} otherwise.
+ */
+ private boolean removeAt(final int index) {
+ --size;
+ // Clearing the key is not strictly necessary (for GC like in a regular collection),
+ // but recommended for security. The memory location is still fresh in the cache anyway.
+ keys[index] = 0;
+ values[index] = null;
+
+ // In the interval from index to the next available entry, the arrays may have entries
+ // that are displaced from their base position due to prior conflicts. Iterate these
+ // entries and move them back if possible, optimizing future lookups.
+ // Knuth Section 6.4 Algorithm R, also used by the JDK's IdentityHashMap.
+
+ boolean movedBack = false;
+ int nextFree = index;
+ for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
+ int bucket = hashIndex(keys[i]);
+ if ((i < bucket && (bucket <= nextFree || nextFree <= i))
+ || (bucket <= nextFree && nextFree <= i)) {
+ // Move the displaced entry "back" to the first available position.
+ keys[nextFree] = keys[i];
+ values[nextFree] = values[i];
+ movedBack = true;
+ // Put the first entry after the displaced entry
+ keys[i] = 0;
+ values[i] = null;
+ nextFree = i;
+ }
+ }
+ return movedBack;
+ }
+
+ /** Calculates the maximum size allowed before rehashing. */
+ private int calcMaxSize(int capacity) {
+ // Clip the upper bound so that there will always be at least one available slot.
+ int upperBound = capacity - 1;
+ return Math.min(upperBound, (int) (capacity * loadFactor));
+ }
+
+ /**
+ * Rehashes the map for the given capacity.
+ *
+ * @param newCapacity the new capacity for the map.
+ */
+ private void rehash(int newCapacity) {
+ int[] oldKeys = keys;
+ V[] oldVals = values;
+
+ keys = new int[newCapacity];
+ @SuppressWarnings({"unchecked", "SuspiciousArrayCast"})
+ V[] temp = (V[]) new Object[newCapacity];
+ values = temp;
+
+ maxSize = calcMaxSize(newCapacity);
+ mask = newCapacity - 1;
+
+ // Insert to the new arrays.
+ for (int i = 0; i < oldVals.length; ++i) {
+ V oldVal = oldVals[i];
+ if (oldVal != null) {
+ // Inlined put(), but much simpler: we don't need to worry about
+ // duplicated keys, growing/rehashing, or failing to insert.
+ int oldKey = oldKeys[i];
+ int index = hashIndex(oldKey);
+
+ for (; ; ) {
+ if (values[index] == null) {
+ keys[index] = oldKey;
+ values[index] = oldVal;
+ break;
+ }
+
+ // Conflict, keep probing. Can wrap around, but never reaches startIndex again.
+ index = probeNext(index);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (isEmpty()) {
+ return "{}";
+ }
+ StringBuilder sb = new StringBuilder(4 * size);
+ sb.append('{');
+ boolean first = true;
+ for (int i = 0; i < values.length; ++i) {
+ V value = values[i];
+ if (value != null) {
+ if (!first) {
+ sb.append(", ");
+ }
+ sb.append(keyToString(keys[i]))
+ .append('=')
+ .append(value == this ? "(this Map)" : toExternal(value));
+ first = false;
+ }
+ }
+ return sb.append('}').toString();
+ }
+
+ /**
+ * Helper method called by {@link #toString()} in order to convert a single map key into a string.
+ * This is protected to allow subclasses to override the appearance of a given key.
+ */
+ protected String keyToString(int key) {
+ return Integer.toString(key);
+ }
+
+ /** Set implementation for iterating over the entries of the map. */
+ private final class EntrySet extends AbstractSet<Entry<Integer, V>> {
+ @Override
+ public Iterator<Entry<Integer, V>> iterator() {
+ return new MapIterator();
+ }
+
+ @Override
+ public int size() {
+ return Int2ObjectHashMap.this.size();
+ }
+ }
+
+ /** Set implementation for iterating over the keys. */
+ private final class KeySet extends AbstractSet<Integer> {
+ @Override
+ public int size() {
+ return Int2ObjectHashMap.this.size();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return Int2ObjectHashMap.this.containsKey(o);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return Int2ObjectHashMap.this.remove(o) != null;
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> retainedKeys) {
+ boolean changed = false;
+ for (Iterator<PrimitiveEntry<V>> iter = entries().iterator(); iter.hasNext(); ) {
+ PrimitiveEntry<V> entry = iter.next();
+ if (!retainedKeys.contains(entry.key())) {
+ changed = true;
+ iter.remove();
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public void clear() {
+ Int2ObjectHashMap.this.clear();
+ }
+
+ @Override
+ public Iterator<Integer> iterator() {
+ return new Iterator<Integer>() {
+ private final Iterator<Entry<Integer, V>> iter = entrySet.iterator();
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public Integer next() {
+ return iter.next().getKey();
+ }
+
+ @Override
+ public void remove() {
+ iter.remove();
+ }
+ };
+ }
+ }
+
+ /**
+ * Iterator over primitive entries. Entry key/values are overwritten by each call to {@link
+ * #next()}.
+ */
+ private final class PrimitiveIterator implements Iterator<PrimitiveEntry<V>>, PrimitiveEntry<V> {
+ private int prevIndex = -1;
+ private int nextIndex = -1;
+ private int entryIndex = -1;
+
+ private void scanNext() {
+ for (; ; ) {
+ if (++nextIndex == values.length || values[nextIndex] != null) {
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (nextIndex == -1) {
+ scanNext();
+ }
+ return nextIndex < keys.length;
+ }
+
+ @Override
+ public PrimitiveEntry<V> next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ prevIndex = nextIndex;
+ scanNext();
+
+ // Always return the same Entry object, just change its index each time.
+ entryIndex = prevIndex;
+ return this;
+ }
+
+ @Override
+ public void remove() {
+ if (prevIndex < 0) {
+ throw new IllegalStateException("next must be called before each remove.");
+ }
+ if (removeAt(prevIndex)) {
+ // removeAt may move elements "back" in the array if they have been displaced because
+ // their spot in the array was occupied when they were inserted. If this occurs then the
+ // nextIndex is now invalid and should instead point to the prevIndex which now holds an
+ // element which was "moved back".
+ nextIndex = prevIndex;
+ }
+ prevIndex = -1;
+ }
+
+ // Entry implementation. Since this implementation uses a single Entry, we coalesce that
+ // into the Iterator object (potentially making loop optimization much easier).
+
+ @Override
+ public int key() {
+ return keys[entryIndex];
+ }
+
+ @Override
+ public V value() {
+ return toExternal(values[entryIndex]);
+ }
+
+ @Override
+ public void setValue(V value) {
+ values[entryIndex] = toInternal(value);
+ }
+ }
+
+ /** Iterator used by the {@link Map} interface. */
+ private final class MapIterator implements Iterator<Entry<Integer, V>> {
+ private final PrimitiveIterator iter = new PrimitiveIterator();
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public Entry<Integer, V> next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ iter.next();
+
+ return new MapEntry(iter.entryIndex);
+ }
+
+ @Override
+ public void remove() {
+ iter.remove();
+ }
+ }
+
+ /** A single entry in the map. */
+ final class MapEntry implements Entry<Integer, V> {
+ private final int entryIndex;
+
+ MapEntry(int entryIndex) {
+ this.entryIndex = entryIndex;
+ }
+
+ @Override
+ public Integer getKey() {
+ verifyExists();
+ return keys[entryIndex];
+ }
+
+ @Override
+ public V getValue() {
+ verifyExists();
+ return toExternal(values[entryIndex]);
+ }
+
+ @Override
+ public V setValue(V value) {
+ verifyExists();
+ V prevValue = toExternal(values[entryIndex]);
+ values[entryIndex] = toInternal(value);
+ return prevValue;
+ }
+
+ private void verifyExists() {
+ if (values[entryIndex] == null) {
+ throw new IllegalStateException("The map entry has been removed");
+ }
+ }
+ }
+
+ /**
+ * Fast method of finding the next power of 2 greater than or equal to the supplied value.
+ *
+ * <p>If the value is {@code <= 0} then 1 will be returned. This method is not suitable for {@link
+ * Integer#MIN_VALUE} or numbers greater than 2^30.
+ *
+ * @param value from which to search for next power of 2
+ * @return The next power of 2 or the value itself if it is a power of 2
+ */
+ private static int findNextPositivePowerOfTwo(final int value) {
+ assert value > Integer.MIN_VALUE && value < 0x40000000;
+ return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/JavaType.java b/java/core/src/main/java/com/google/protobuf/JavaType.java
new file mode 100644
index 00000000..0e4c5c41
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/JavaType.java
@@ -0,0 +1,87 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+/**
+ * Enum that identifies the Java types required to store protobuf fields.
+ */
+@ExperimentalApi
+public enum JavaType {
+ VOID(Void.class, Void.class, null),
+ INT(int.class, Integer.class, 0),
+ LONG(long.class, Long.class, 0L),
+ FLOAT(float.class, Float.class, 0F),
+ DOUBLE(double.class, Double.class, 0D),
+ BOOLEAN(boolean.class, Boolean.class, false),
+ STRING(String.class, String.class, ""),
+ BYTE_STRING(ByteString.class, ByteString.class, ByteString.EMPTY),
+ ENUM(int.class, Integer.class, null),
+ MESSAGE(Object.class, Object.class, null);
+
+ private final Class<?> type;
+ private final Class<?> boxedType;
+ private final Object defaultDefault;
+
+ JavaType(Class<?> type, Class<?> boxedType, Object defaultDefault) {
+ this.type = type;
+ this.boxedType = boxedType;
+ this.defaultDefault = defaultDefault;
+ }
+
+ /**
+ * The default default value for fields of this type, if it's a primitive
+ * type.
+ */
+ public Object getDefaultDefault() {
+ return defaultDefault;
+ }
+
+ /**
+ * Gets the required type for a field that would hold a value of this type.
+ */
+ public Class<?> getType() {
+ return type;
+ }
+
+ /**
+ * @return the boxedType
+ */
+ public Class<?> getBoxedType() {
+ return boxedType;
+ }
+
+ /**
+ * Indicates whether or not this {@link JavaType} can be applied to a field of the given type.
+ */
+ public boolean isValidType(Class<?> t) {
+ return type.isAssignableFrom(t);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/MessageInfo.java b/java/core/src/main/java/com/google/protobuf/MessageInfo.java
new file mode 100644
index 00000000..fdcfc2d8
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/MessageInfo.java
@@ -0,0 +1,158 @@
+// 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.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Information for the layout of a protobuf message class. This describes all of the fields
+ * contained within a message.
+ */
+@ExperimentalApi
+public final class MessageInfo {
+ private final ProtoSyntax syntax;
+ private final boolean messageSetWireFormat;
+ private final List<FieldInfo> fields;
+
+ /**
+ * Constructor.
+ *
+ * @param fields the set of fields for the message.
+ */
+ private MessageInfo(ProtoSyntax syntax, boolean messageSetWireFormat, List<FieldInfo> fields) {
+ this.syntax = syntax;
+ this.messageSetWireFormat = messageSetWireFormat;
+ this.fields = fields;
+ }
+
+ /** Gets the syntax for the message (e.g. PROTO2, PROTO3). */
+ public ProtoSyntax getSyntax() {
+ return syntax;
+ }
+
+ /** Indicates whether or not the message should be represented with message set wire format. */
+ public boolean isMessageSetWireFormat() {
+ return messageSetWireFormat;
+ }
+
+ /**
+ * Gets the information for all fields within this message, sorted in ascending order by their
+ * field number.
+ */
+ public List<FieldInfo> getFields() {
+ return fields;
+ }
+
+ /** Creates a new map of field number to message class for message fields. */
+ public Int2ObjectHashMap<Class<?>> messageFieldClassMap() {
+ Int2ObjectHashMap<Class<?>> classMap = new Int2ObjectHashMap<Class<?>>();
+ for (int i = 0; i < fields.size(); ++i) {
+ FieldInfo fd = fields.get(i);
+ int fieldNumber = fd.getFieldNumber();
+
+ // Configure messages
+ switch (fd.getType()) {
+ case MESSAGE:
+ classMap.put(fieldNumber, fd.getField().getType());
+ break;
+ case MESSAGE_LIST:
+ classMap.put(fieldNumber, fd.getListElementType());
+ break;
+ case GROUP:
+ classMap.put(fieldNumber, fd.getField().getType());
+ break;
+ case GROUP_LIST:
+ classMap.put(fieldNumber, fd.getListElementType());
+ break;
+ default:
+ break;
+ }
+ }
+ return classMap;
+ }
+
+ /** Helper method for creating a new builder for {@link MessageInfo}. */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /** Helper method for creating a new builder for {@link MessageInfo}. */
+ public static Builder newBuilder(int numFields) {
+ return new Builder(numFields);
+ }
+
+ /** A builder of {@link MessageInfo} instances. */
+ public static final class Builder {
+ private final ArrayList<FieldInfo> fields;
+ private ProtoSyntax syntax;
+ private boolean wasBuilt;
+ private boolean messageSetWireFormat;
+
+ public Builder() {
+ fields = new ArrayList<FieldInfo>();
+ }
+
+ public Builder(int numFields) {
+ fields = new ArrayList<FieldInfo>(numFields);
+ }
+
+ public void withSyntax(ProtoSyntax syntax) {
+ this.syntax = checkNotNull(syntax, "syntax");
+ }
+
+ public void withMessageSetWireFormat(boolean messageSetWireFormat) {
+ this.messageSetWireFormat = messageSetWireFormat;
+ }
+
+ public void add(FieldInfo field) {
+ if (wasBuilt) {
+ throw new IllegalStateException("Builder can only build once");
+ }
+ fields.add(field);
+ }
+
+ public MessageInfo build() {
+ if (wasBuilt) {
+ throw new IllegalStateException("Builder can only build once");
+ }
+ if (syntax == null) {
+ throw new IllegalStateException("Must specify a proto syntax");
+ }
+ wasBuilt = true;
+ Collections.sort(fields);
+ return new MessageInfo(syntax, messageSetWireFormat, Collections.unmodifiableList(fields));
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/MessageInfoFactory.java b/java/core/src/main/java/com/google/protobuf/MessageInfoFactory.java
new file mode 100644
index 00000000..c17f8ec4
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/MessageInfoFactory.java
@@ -0,0 +1,38 @@
+// 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;
+
+/** A factory that creates {@link MessageInfo} instances for message types. */
+@ExperimentalApi
+public interface MessageInfoFactory {
+ /** Returns a information of the message class. */
+ MessageInfo messageInfoFor(Class<?> clazz);
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2LiteLookupSchema.java b/java/core/src/main/java/com/google/protobuf/Proto2LiteLookupSchema.java
new file mode 100644
index 00000000..959abc5f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2LiteLookupSchema.java
@@ -0,0 +1,281 @@
+// 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.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, lookup-based schema that can be used with any proto3-lite message class. The message
+ * class must extend {@link GeneratedMessage}.
+ */
+final class Proto2LiteLookupSchema<T> extends AbstractProto2LiteSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto2LiteLookupSchema(Class<T> messageClass, MessageInfo msgInfo) {
+ super(messageClass, Proto2Manifest.newLookupManifest(msgInfo));
+ this.messageFieldClassMap = msgInfo.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.lookupPositionForFieldNumber(fieldNumber);
+ if (pos >= 0L) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 19: //INT64_LIST:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 21: //INT32_LIST:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 25: //STRING_LIST:
+ reader.readStringList(
+ SchemaUtil.<String>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readProtobufMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ continue;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case -4: //GROUP (actually should be 252, but byte is [-128, 127])
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readGroup(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ break;
+ case -3: //GROUP_LIST (actually should be 253, but byte is [-128, 127])
+ SchemaUtil.readGroupList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ default:
+ // Unknown field type - break out of loop and skip the field.
+ break;
+ }
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat it as an unknown field - same as the default case.
+ }
+ }
+
+ // Unknown field.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2LiteTableSchema.java b/java/core/src/main/java/com/google/protobuf/Proto2LiteTableSchema.java
new file mode 100644
index 00000000..5d60986a
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2LiteTableSchema.java
@@ -0,0 +1,291 @@
+// 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.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, table-based schema that can be used with any proto3 lite message class. The message
+ * class must extend {@link GeneratedMessage}.
+ */
+final class Proto2LiteTableSchema<T> extends AbstractProto2LiteSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto2LiteTableSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto2Manifest.newTableManfiest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.tablePositionForFieldNumber(fieldNumber);
+ if (pos < 0) {
+ // Unknown field.
+ if (reader.skipField()) {
+ continue;
+ }
+ // Done reading.
+ return;
+ }
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+
+ // Benchmarks have shown that switching on a byte is faster than an enum.
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 19: //INT64_LIST:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 21: //INT32_LIST:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 25: //STRING_LIST:
+ reader.readStringList(
+ SchemaUtil.<String>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readProtobufMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case -4: //GROUP (actually should be 252, but byte is [-128, 127])
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readGroup(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ break;
+ case -3: //GROUP_LIST (actually should be 253, but byte is [-128, 127])
+ SchemaUtil.readGroupList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ default:
+ // Assume we've landed on an empty entry. Treat it as an unknown field - just skip it.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ break;
+ }
+ // TODO(nathanmittler): Do we need to make lists immutable?
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat fields with an invalid wire type as unknown fields (i.e. same as the default case).
+ if (!reader.skipField()) {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2LookupSchema.java b/java/core/src/main/java/com/google/protobuf/Proto2LookupSchema.java
new file mode 100644
index 00000000..adab1c2c
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2LookupSchema.java
@@ -0,0 +1,273 @@
+// 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.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, lookup-based schema that can be used with any standard (i.e. non-lite) proto3 message
+ * class. The message class must extend {@link GeneratedMessage}.
+ */
+final class Proto2LookupSchema<T> extends AbstractProto2StandardSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto2LookupSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto2Manifest.newLookupManifest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.lookupPositionForFieldNumber(fieldNumber);
+ if (pos >= 0L) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 19: //INT64_LIST:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 21: //INT32_LIST:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 25: //STRING_LIST:
+ reader.readStringList(
+ SchemaUtil.<String>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ continue;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case -4: //GROUP (actually should be 252, but byte is [-128, 127])
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readGroup(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ continue;
+ case -3: //GROUP_LIST (actually should be 253, but byte is [-128, 127])
+ SchemaUtil.readGroupList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ continue;
+ default:
+ // Unknown field type - break out of loop and skip the field.
+ break;
+ }
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat it as an unknown field - same as the default case.
+ }
+ }
+
+ // Unknown field.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java b/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java
new file mode 100644
index 00000000..b6eed904
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2Manifest.java
@@ -0,0 +1,261 @@
+// 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;
+import java.util.List;
+
+/** Container for the field metadata of a single proto2 schema. */
+final class Proto2Manifest {
+ static final int INT_LENGTH = 4;
+ static final int LONG_LENGTH = INT_LENGTH * 2;
+ static final int LONGS_PER_FIELD = 2;
+ /**
+ * Note that field length is always a power of two so that we can use bit shifting (rather than
+ * division) to find the location of a field when parsing.
+ */
+ static final int FIELD_LENGTH = LONGS_PER_FIELD * LONG_LENGTH;
+
+ static final int FIELD_SHIFT = 4 /* 2^4 = 16 */;
+ static final int OFFSET_BITS = 20;
+ static final int OFFSET_MASK = 0XFFFFF;
+ static final long EMPTY_LONG = 0xFFFFFFFFFFFFFFFFL;
+
+ /**
+ * Holds all information for accessing the message fields. The layout is as follows (field
+ * positions are relative to the offset of the start of the field in the buffer):
+ *
+ * <p>
+ *
+ * <pre>
+ * [ 0 - 3] unused
+ * [ 4 - 31] field number
+ * [32 - 37] unused
+ * [38 - 43] field type
+ * [44 - 63] field offset
+ * [64 - 69] unused
+ * [70 - 75] field presence mask shift
+ * [76 - 95] presence field offset
+ * [96 - 127] unused
+ * </pre>
+ */
+ final ByteBuffer buffer;
+
+ final long address;
+ final long limit;
+ final int numFields;
+
+ final int minFieldNumber;
+ final int maxFieldNumber;
+
+ private Proto2Manifest(
+ ByteBuffer buffer,
+ long address,
+ long limit,
+ int numFields,
+ int minFieldNumber,
+ int maxFieldNumber) {
+ this.buffer = buffer;
+ this.address = address;
+ this.limit = limit;
+ this.numFields = numFields;
+ this.minFieldNumber = minFieldNumber;
+ this.maxFieldNumber = maxFieldNumber;
+ }
+
+ boolean isFieldInRange(int fieldNumber) {
+ return fieldNumber >= minFieldNumber && fieldNumber <= maxFieldNumber;
+ }
+
+ long tablePositionForFieldNumber(int fieldNumber) {
+ if (fieldNumber < minFieldNumber || fieldNumber > maxFieldNumber) {
+ return -1;
+ }
+
+ return indexToAddress(fieldNumber - minFieldNumber);
+ }
+
+ <T> boolean isFieldPresent(T message, long pos) {
+ int maskShiftAndOffset = UnsafeUtil.getInt(pos + LONG_LENGTH);
+ long offset = maskShiftAndOffset & OFFSET_MASK;
+ int mask = 1 << (maskShiftAndOffset >>> OFFSET_BITS);
+ return (UnsafeUtil.getInt(message, offset) & mask) != 0;
+ }
+
+ <T> void setFieldPresent(T message, long pos) {
+ int maskShiftAndOffset = UnsafeUtil.getInt(pos + LONG_LENGTH);
+ long offset = maskShiftAndOffset & OFFSET_MASK;
+ int mask = 1 << (maskShiftAndOffset >>> OFFSET_BITS);
+ UnsafeUtil.putInt(message, offset, UnsafeUtil.getInt(message, offset) | mask);
+ }
+
+ long lookupPositionForFieldNumber(int fieldNumber) {
+ int min = 0;
+ int max = numFields - 1;
+ while (min <= max) {
+ // Find the midpoint address.
+ int mid = (max + min) >>> 1;
+ long midAddress = indexToAddress(mid);
+ int midFieldNumber = numberAt(midAddress);
+ if (fieldNumber == midFieldNumber) {
+ // Found the field.
+ return midAddress;
+ }
+ if (fieldNumber < midFieldNumber) {
+ // Search the lower half.
+ max = mid - 1;
+ } else {
+ // Search the upper half.
+ min = mid + 1;
+ }
+ }
+ return -1;
+ }
+
+ int numberAt(long pos) {
+ return UnsafeUtil.getInt(pos);
+ }
+
+ int typeAndOffsetAt(long pos) {
+ return UnsafeUtil.getInt(pos + INT_LENGTH);
+ }
+
+ private long indexToAddress(int index) {
+ return address + (index << FIELD_SHIFT);
+ }
+
+ static byte type(int value) {
+ return (byte) (value >>> OFFSET_BITS);
+ }
+
+ static long offset(int value) {
+ return value & OFFSET_MASK;
+ }
+
+ static Proto2Manifest newTableManfiest(MessageInfo descriptor) {
+ List<FieldInfo> fds = descriptor.getFields();
+ if (fds.isEmpty()) {
+ throw new IllegalArgumentException("Table-based schema requires at least one field");
+ }
+
+ // Set up the buffer for direct indexing by field number.
+ final int minFieldNumber = fds.get(0).getFieldNumber();
+ final int maxFieldNumber = fds.get(fds.size() - 1).getFieldNumber();
+ final int numEntries = (maxFieldNumber - minFieldNumber) + 1;
+
+ int bufferLength = numEntries * FIELD_LENGTH;
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bufferLength + LONG_LENGTH);
+ long tempAddress = UnsafeUtil.addressOffset(buffer);
+ if ((tempAddress & 7L) != 0) {
+ // Make sure that the memory address is 8-byte aligned.
+ tempAddress = (tempAddress & ~7L) + LONG_LENGTH;
+ }
+ final long address = tempAddress;
+ final long limit = address + bufferLength;
+
+ // Fill in the manifest data from the descriptors.
+ int fieldIndex = 0;
+ FieldInfo fd = fds.get(fieldIndex++);
+ for (int bufferIndex = 0; bufferIndex < bufferLength; bufferIndex += FIELD_LENGTH) {
+ final int fieldNumber = fd.getFieldNumber();
+ if (bufferIndex < ((fieldNumber - minFieldNumber) << FIELD_SHIFT)) {
+ // Mark this entry as "empty".
+ long skipLimit = address + bufferIndex + FIELD_LENGTH;
+ for (long skipPos = address + bufferIndex; skipPos < skipLimit; skipPos += LONG_LENGTH) {
+ UnsafeUtil.putLong(skipPos, EMPTY_LONG);
+ }
+ continue;
+ }
+
+ // We found the entry for the next field. Store the entry in the manifest for
+ // this field and increment the field index.
+ FieldType type = fd.getType();
+ long pos = address + bufferIndex;
+ UnsafeUtil.putInt(pos, fieldNumber);
+ UnsafeUtil.putInt(
+ pos + INT_LENGTH,
+ (type.id() << OFFSET_BITS) | (int) UnsafeUtil.objectFieldOffset(fd.getField()));
+ if (!type.isList()) {
+ int presenceOffset = (int) UnsafeUtil.objectFieldOffset(fd.getPresenceField());
+ int maskShift = Integer.numberOfTrailingZeros(fd.getPresenceMask());
+ UnsafeUtil.putInt(pos + LONG_LENGTH, maskShift << OFFSET_BITS | presenceOffset);
+ }
+
+ // Advance to the next field, unless we're at the end.
+ if (fieldIndex < fds.size()) {
+ fd = fds.get(fieldIndex++);
+ }
+ }
+
+ return new Proto2Manifest(buffer, address, limit, fds.size(), minFieldNumber, maxFieldNumber);
+ }
+
+ static Proto2Manifest newLookupManifest(MessageInfo descriptor) {
+ List<FieldInfo> fds = descriptor.getFields();
+
+ final int numFields = fds.size();
+ int bufferLength = numFields * FIELD_LENGTH;
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferLength + LONG_LENGTH);
+ long tempAddress = UnsafeUtil.addressOffset(buffer);
+ if ((tempAddress & 7L) != 0) {
+ // Make sure that the memory address is 8-byte aligned.
+ tempAddress = (tempAddress & ~7L) + LONG_LENGTH;
+ }
+ final long address = tempAddress;
+ final long limit = address + bufferLength;
+
+ // Allocate and populate the data buffer.
+ long pos = address;
+ for (int i = 0; i < fds.size(); ++i, pos += FIELD_LENGTH) {
+ FieldInfo fd = fds.get(i);
+ UnsafeUtil.putInt(pos, fd.getFieldNumber());
+ UnsafeUtil.putInt(
+ pos + INT_LENGTH,
+ (fd.getType().id() << OFFSET_BITS) | (int) UnsafeUtil.objectFieldOffset(fd.getField()));
+ if (!fd.getType().isList()) {
+ int presenceOffset = (int) UnsafeUtil.objectFieldOffset(fd.getPresenceField());
+ int maskShift = Integer.numberOfTrailingZeros(fd.getPresenceMask());
+ UnsafeUtil.putInt(pos + LONG_LENGTH, maskShift << OFFSET_BITS | presenceOffset);
+ }
+ }
+
+ if (numFields > 0) {
+ return new Proto2Manifest(
+ buffer,
+ address,
+ limit,
+ numFields,
+ fds.get(0).getFieldNumber(),
+ fds.get(numFields - 1).getFieldNumber());
+ }
+ return new Proto2Manifest(buffer, address, limit, numFields, -1, -1);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2SchemaFactory.java b/java/core/src/main/java/com/google/protobuf/Proto2SchemaFactory.java
new file mode 100644
index 00000000..e686cbd7
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2SchemaFactory.java
@@ -0,0 +1,112 @@
+// 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.checkNotNull;
+
+/** Manufactures instances of {@link Proto3TableSchema}. */
+@ExperimentalApi
+public final class Proto2SchemaFactory implements SchemaFactory {
+ /**
+ * The mode with which to generate schemas.
+ *
+ * <p>For testing purposes only.
+ */
+ public enum Mode {
+ /** Always use a table-based indexing of fields. */
+ TABLE,
+
+ /** Always used lookup-based (i.e. binary search) indexing of fields. */
+ LOOKUP,
+
+ /**
+ * Default. Determine the appropriate field indexing mode based on how sparse the field numbers
+ * are for the message.
+ */
+ DYNAMIC
+ }
+
+ private final MessageInfoFactory messageDescriptorFactory;
+ private final Mode mode;
+
+ public Proto2SchemaFactory() {
+ this(DescriptorMessageInfoFactory.getInstance());
+ }
+
+ public Proto2SchemaFactory(MessageInfoFactory messageDescriptorFactory) {
+ this(messageDescriptorFactory, Mode.DYNAMIC);
+ }
+
+ /** For testing purposes only. Allows specification of {@link Mode}. */
+ public Proto2SchemaFactory(MessageInfoFactory messageDescriptorFactory, Mode mode) {
+ if (!isSupported()) {
+ throw new IllegalStateException("Schema factory is unsupported on this platform");
+ }
+ this.messageDescriptorFactory =
+ checkNotNull(messageDescriptorFactory, "messageDescriptorFactory");
+ this.mode = checkNotNull(mode, "mode");
+ }
+
+ public static boolean isSupported() {
+ return UnsafeUtil.hasUnsafeArrayOperations() && UnsafeUtil.hasUnsafeByteBufferOperations();
+ }
+
+ @Override
+ public <T> Schema<T> createSchema(Class<T> messageType) {
+ SchemaUtil.requireGeneratedMessage(messageType);
+
+ MessageInfo descriptor = messageDescriptorFactory.messageInfoFor(messageType);
+ switch (mode) {
+ case TABLE:
+ return newTableSchema(messageType, descriptor);
+ case LOOKUP:
+ return newLookupSchema(messageType, descriptor);
+ default:
+ return SchemaUtil.shouldUseTableSwitch(descriptor.getFields())
+ ? newTableSchema(messageType, descriptor)
+ : newLookupSchema(messageType, descriptor);
+ }
+ }
+
+ private <T> Schema<T> newTableSchema(Class<T> messageType, MessageInfo descriptor) {
+ if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
+ return new Proto2LiteTableSchema<T>(messageType, descriptor);
+ }
+ return new Proto2TableSchema<T>(messageType, descriptor);
+ }
+
+ private <T> Schema<T> newLookupSchema(Class<T> messageType, MessageInfo descriptor) {
+ if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
+ return new Proto2LiteLookupSchema<T>(messageType, descriptor);
+ }
+ return new Proto2LookupSchema<T>(messageType, descriptor);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto2TableSchema.java b/java/core/src/main/java/com/google/protobuf/Proto2TableSchema.java
new file mode 100644
index 00000000..24571462
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto2TableSchema.java
@@ -0,0 +1,270 @@
+// 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.Proto2Manifest.offset;
+import static com.google.protobuf.Proto2Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, table-based schema that can be used with any proto2 message class. The message class
+ * must extend {@link GeneratedMessage}.
+ */
+final class Proto2TableSchema<T> extends AbstractProto2StandardSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto2TableSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto2Manifest.newTableManfiest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.tablePositionForFieldNumber(fieldNumber);
+ if (pos < 0) {
+ // Unknown field.
+ if (reader.skipField()) {
+ continue;
+ }
+ // Done reading.
+ return;
+ }
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+
+ // Benchmarks have shown that switching on a byte is faster than an enum.
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ manifest.setFieldPresent(message, pos);
+ break;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 19: //INT64_LIST:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 21: //INT32_LIST:
+ reader.readInt32List(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 25: //STRING_LIST:
+ reader.readStringList(SchemaUtil.<String>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case -4: //GROUP (actually should be 252, but byte is [-128, 127])
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readGroup(messageFieldClassMap.get(fieldNumber)));
+ manifest.setFieldPresent(message, pos);
+ break;
+ case -3: //GROUP_LIST (actually should be 253, but byte is [-128, 127])
+ SchemaUtil.readGroupList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ default:
+ // Assume we've landed on an empty entry. Treat it as an unknown field - just skip it.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ break;
+ }
+ // TODO(nathanmittler): Do we need to make lists immutable?
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat fields with an invalid wire type as unknown fields (i.e. same as the default case).
+ if (!reader.skipField()) {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto3LiteLookupSchema.java b/java/core/src/main/java/com/google/protobuf/Proto3LiteLookupSchema.java
new file mode 100644
index 00000000..b25c3f0d
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto3LiteLookupSchema.java
@@ -0,0 +1,253 @@
+// 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.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, lookup-based schema that can be used with any proto3-lite message class. The message
+ * class must extend {@link GeneratedMessage}.
+ */
+final class Proto3LiteLookupSchema<T> extends AbstractProto3LiteSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto3LiteLookupSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto3Manifest.newLookupManifest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.lookupPositionForFieldNumber(fieldNumber);
+ if (pos >= 0L) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ continue;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ continue;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ continue;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ continue;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ continue;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ continue;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ continue;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ continue;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ continue;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ continue;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ continue;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ continue;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ continue;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ continue;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ continue;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ continue;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ continue;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 19: //INT64_LIST:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 21: //INT32_LIST:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 25: //STRING_LIST:
+ reader.readStringList(
+ SchemaUtil.<String>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readProtobufMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ continue;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ continue;
+ default:
+ // Unknown field type - break out of loop and skip the field.
+ break;
+ }
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat it as an unknown field - same as the default case.
+ }
+ }
+
+ // Unknown field.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto3LiteTableSchema.java b/java/core/src/main/java/com/google/protobuf/Proto3LiteTableSchema.java
new file mode 100644
index 00000000..46abeb71
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto3LiteTableSchema.java
@@ -0,0 +1,263 @@
+// 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.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, table-based schema that can be used with any proto3 lite message class. The message
+ * class must extend {@link GeneratedMessage}.
+ */
+final class Proto3LiteTableSchema<T> extends AbstractProto3LiteSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto3LiteTableSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto3Manifest.newTableManfiest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.tablePositionForFieldNumber(fieldNumber);
+ if (pos < 0) {
+ // Unknown field.
+ if (reader.skipField()) {
+ continue;
+ }
+ // Done reading.
+ return;
+ }
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+
+ // Benchmarks have shown that switching on a byte is faster than an enum.
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ break;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ break;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ break;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ break;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ break;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ break;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ break;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ break;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ break;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ break;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ break;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ break;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ break;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ break;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ break;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 19: //INT64_LIST:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 21: //INT32_LIST:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 25: //STRING_LIST:
+ reader.readStringList(
+ SchemaUtil.<String>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readProtobufMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(
+ SchemaUtil.<Float>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(
+ SchemaUtil.<Long>mutableProtobufListAt(message, offset(typeAndOffset)));
+ break;
+ default:
+ // Assume we've landed on an empty entry. Treat it as an unknown field - just skip it.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ break;
+ }
+ // TODO(nathanmittler): Do we need to make lists immutable?
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat fields with an invalid wire type as unknown fields (i.e. same as the default case).
+ if (!reader.skipField()) {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto3LookupSchema.java b/java/core/src/main/java/com/google/protobuf/Proto3LookupSchema.java
new file mode 100644
index 00000000..5e0f9c51
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto3LookupSchema.java
@@ -0,0 +1,245 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static com.google.protobuf.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, lookup-based schema that can be used with any standard (i.e. non-lite) proto3 message
+ * class. The message class must extend {@link GeneratedMessage}.
+ */
+final class Proto3LookupSchema<T> extends AbstractProto3StandardSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto3LookupSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto3Manifest.newTableManfiest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.lookupPositionForFieldNumber(fieldNumber);
+ if (pos >= 0L) {
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ continue;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ continue;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ continue;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ continue;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ continue;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ continue;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ continue;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ continue;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ continue;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ continue;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ continue;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ continue;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ continue;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ continue;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ continue;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ continue;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ continue;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 19: //INT64_LIST:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 21: //INT32_LIST:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 25: //STRING_LIST:
+ reader.readStringList(
+ SchemaUtil.<String>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ continue;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(
+ SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(
+ SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(
+ SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ continue;
+ default:
+ // Unknown field type - break out of loop and skip the field.
+ break;
+ }
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat it as an unknown field - same as the default case.
+ }
+ }
+
+ // Unknown field.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto3Manifest.java b/java/core/src/main/java/com/google/protobuf/Proto3Manifest.java
new file mode 100644
index 00000000..6700a0b4
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto3Manifest.java
@@ -0,0 +1,233 @@
+// 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;
+import java.util.List;
+
+/** Container for the field metadata of a single proto3 schema. */
+final class Proto3Manifest {
+ static final int INT_LENGTH = 4;
+ static final int LONG_LENGTH = INT_LENGTH * 2;
+ static final int LONGS_PER_FIELD = 2;
+ /**
+ * Note that field length is always a power of two so that we can use bit shifting (rather than
+ * division) to find the location of a field when parsing.
+ */
+ static final int FIELD_LENGTH = LONGS_PER_FIELD * LONG_LENGTH;
+
+ static final int FIELD_SHIFT = 4 /* 2^4 = 16 */;
+ static final int OFFSET_BITS = 20;
+ static final int OFFSET_MASK = 0XFFFFF;
+ static final long EMPTY_LONG = 0xFFFFFFFFFFFFFFFFL;
+
+ /**
+ * Holds all information for accessing the message fields. The layout is as follows (field
+ * positions are relative to the offset of the start of the field in the buffer):
+ *
+ * <p>
+ *
+ * <pre>
+ * [ 0 - 3] unused
+ * [ 4 - 31] field number
+ * [32 - 37] unused
+ * [38 - 43] field type
+ * [44 - 63] field offset
+ * [64 - 127] unused
+ * </pre>
+ */
+ final ByteBuffer buffer;
+
+ final long address;
+ final long limit;
+ final int numFields;
+
+ final int minFieldNumber;
+ final int maxFieldNumber;
+
+ private Proto3Manifest(
+ ByteBuffer buffer,
+ long address,
+ long limit,
+ int numFields,
+ int minFieldNumber,
+ int maxFieldNumber) {
+ this.buffer = buffer;
+ this.address = address;
+ this.limit = limit;
+ this.numFields = numFields;
+ this.minFieldNumber = minFieldNumber;
+ this.maxFieldNumber = maxFieldNumber;
+ }
+
+ boolean isFieldInRange(int fieldNumber) {
+ return fieldNumber >= minFieldNumber && fieldNumber <= maxFieldNumber;
+ }
+
+ long tablePositionForFieldNumber(int fieldNumber) {
+ if (fieldNumber < minFieldNumber || fieldNumber > maxFieldNumber) {
+ return -1;
+ }
+
+ return indexToAddress(fieldNumber - minFieldNumber);
+ }
+
+ long lookupPositionForFieldNumber(int fieldNumber) {
+ int min = 0;
+ int max = numFields - 1;
+ while (min <= max) {
+ // Find the midpoint address.
+ int mid = (max + min) >>> 1;
+ long midAddress = indexToAddress(mid);
+ int midFieldNumber = numberAt(midAddress);
+ if (fieldNumber == midFieldNumber) {
+ // Found the field.
+ return midAddress;
+ }
+ if (fieldNumber < midFieldNumber) {
+ // Search the lower half.
+ max = mid - 1;
+ } else {
+ // Search the upper half.
+ min = mid + 1;
+ }
+ }
+ return -1;
+ }
+
+ int numberAt(long pos) {
+ return UnsafeUtil.getInt(pos);
+ }
+
+ int typeAndOffsetAt(long pos) {
+ return UnsafeUtil.getInt(pos + INT_LENGTH);
+ }
+
+ private long indexToAddress(int index) {
+ return address + (index << FIELD_SHIFT);
+ }
+
+ static byte type(int value) {
+ return (byte) (value >>> OFFSET_BITS);
+ }
+
+ static long offset(int value) {
+ return value & OFFSET_MASK;
+ }
+
+ static Proto3Manifest newTableManfiest(MessageInfo descriptor) {
+ List<FieldInfo> fds = descriptor.getFields();
+ if (fds.isEmpty()) {
+ throw new IllegalArgumentException("Table-based schema requires at least one field");
+ }
+
+ // Set up the buffer for direct indexing by field number.
+ final int minFieldNumber = fds.get(0).getFieldNumber();
+ final int maxFieldNumber = fds.get(fds.size() - 1).getFieldNumber();
+ final int numEntries = (maxFieldNumber - minFieldNumber) + 1;
+
+ int bufferLength = numEntries * FIELD_LENGTH;
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bufferLength + LONG_LENGTH);
+ long tempAddress = UnsafeUtil.addressOffset(buffer);
+ if ((tempAddress & 7L) != 0) {
+ // Make sure that the memory address is 8-byte aligned.
+ tempAddress = (tempAddress & ~7L) + LONG_LENGTH;
+ }
+ final long address = tempAddress;
+ final long limit = address + bufferLength;
+
+ // Fill in the manifest data from the descriptors.
+ int fieldIndex = 0;
+ FieldInfo fd = fds.get(fieldIndex++);
+ for (int bufferIndex = 0; bufferIndex < bufferLength; bufferIndex += FIELD_LENGTH) {
+ final int fieldNumber = fd.getFieldNumber();
+ if (bufferIndex < ((fieldNumber - minFieldNumber) << FIELD_SHIFT)) {
+ // Mark this entry as "empty".
+ long skipLimit = address + bufferIndex + FIELD_LENGTH;
+ for (long skipPos = address + bufferIndex; skipPos < skipLimit; skipPos += LONG_LENGTH) {
+ UnsafeUtil.putLong(skipPos, EMPTY_LONG);
+ }
+ continue;
+ }
+
+ // We found the entry for the next field. Store the entry in the manifest for
+ // this field and increment the field index.
+ FieldType type = fd.getType();
+ UnsafeUtil.putInt(address + bufferIndex, fieldNumber);
+ UnsafeUtil.putInt(
+ address + bufferIndex + INT_LENGTH,
+ (type.id() << OFFSET_BITS) | (int) UnsafeUtil.objectFieldOffset(fd.getField()));
+
+ // Advance to the next field, unless we're at the end.
+ if (fieldIndex < fds.size()) {
+ fd = fds.get(fieldIndex++);
+ }
+ }
+
+ return new Proto3Manifest(buffer, address, limit, fds.size(), minFieldNumber, maxFieldNumber);
+ }
+
+ static Proto3Manifest newLookupManifest(MessageInfo descriptor) {
+ List<FieldInfo> fds = descriptor.getFields();
+
+ final int numFields = fds.size();
+ int bufferLength = numFields * FIELD_LENGTH;
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferLength + LONG_LENGTH);
+ long tempAddress = UnsafeUtil.addressOffset(buffer);
+ if ((tempAddress & 7L) != 0) {
+ // Make sure that the memory address is 8-byte aligned.
+ tempAddress = (tempAddress & ~7L) + LONG_LENGTH;
+ }
+ final long address = tempAddress;
+ final long limit = address + bufferLength;
+
+ // Allocate and populate the data buffer.
+ long pos = address;
+ for (int i = 0; i < fds.size(); ++i, pos += FIELD_LENGTH) {
+ FieldInfo fd = fds.get(i);
+ UnsafeUtil.putInt(pos, fd.getFieldNumber());
+ UnsafeUtil.putInt(
+ pos + INT_LENGTH,
+ (fd.getType().id() << OFFSET_BITS) | (int) UnsafeUtil.objectFieldOffset(fd.getField()));
+ }
+
+ if (numFields > 0) {
+ return new Proto3Manifest(
+ buffer,
+ address,
+ limit,
+ numFields,
+ fds.get(0).getFieldNumber(),
+ fds.get(numFields - 1).getFieldNumber());
+ }
+ return new Proto3Manifest(buffer, address, limit, numFields, -1, -1);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto3SchemaFactory.java b/java/core/src/main/java/com/google/protobuf/Proto3SchemaFactory.java
new file mode 100644
index 00000000..c991dd12
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto3SchemaFactory.java
@@ -0,0 +1,115 @@
+// 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.checkNotNull;
+
+/**
+ * Manufactures schemas for proto3 messages. Message classes must extend {@link
+ * com.google.protobuf.GeneratedMessage} or {@link com.google.protobuf.GeneratedMessageLite}.
+ */
+@ExperimentalApi
+public final class Proto3SchemaFactory implements SchemaFactory {
+ /**
+ * The mode with which to generate schemas.
+ *
+ * <p>For testing purposes only.
+ */
+ public enum Mode {
+ /** Always use a table-based indexing of fields. */
+ TABLE,
+
+ /** Always used lookup-based (i.e. binary search) indexing of fields. */
+ LOOKUP,
+
+ /**
+ * Default. Determine the appropriate field indexing mode based on how sparse the field numbers
+ * are for the message.
+ */
+ DYNAMIC
+ }
+
+ private final MessageInfoFactory messageDescriptorFactory;
+ private final Mode mode;
+
+ public Proto3SchemaFactory() {
+ this(DescriptorMessageInfoFactory.getInstance());
+ }
+
+ public Proto3SchemaFactory(MessageInfoFactory messageDescriptorFactory) {
+ this(messageDescriptorFactory, Mode.DYNAMIC);
+ }
+
+ /** For testing purposes only. Allows specification of {@link Mode}. */
+ public Proto3SchemaFactory(MessageInfoFactory messageDescriptorFactory, Mode mode) {
+ if (!isSupported()) {
+ throw new IllegalStateException("Schema factory is unsupported on this platform");
+ }
+ this.messageDescriptorFactory =
+ checkNotNull(messageDescriptorFactory, "messageDescriptorFactory");
+ this.mode = checkNotNull(mode, "mode");
+ }
+
+ public static boolean isSupported() {
+ return UnsafeUtil.hasUnsafeArrayOperations() && UnsafeUtil.hasUnsafeByteBufferOperations();
+ }
+
+ @Override
+ public <T> Schema<T> createSchema(Class<T> messageType) {
+ SchemaUtil.requireGeneratedMessage(messageType);
+
+ MessageInfo descriptor = messageDescriptorFactory.messageInfoFor(messageType);
+ switch (mode) {
+ case TABLE:
+ return newTableSchema(messageType, descriptor);
+ case LOOKUP:
+ return newLookupSchema(messageType, descriptor);
+ default:
+ return SchemaUtil.shouldUseTableSwitch(descriptor.getFields())
+ ? newTableSchema(messageType, descriptor)
+ : newLookupSchema(messageType, descriptor);
+ }
+ }
+
+ private <T> Schema<T> newTableSchema(Class<T> messageType, MessageInfo descriptor) {
+ if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
+ return new Proto3LiteTableSchema<T>(messageType, descriptor);
+ }
+ return new Proto3TableSchema<T>(messageType, descriptor);
+ }
+
+ private <T> Schema<T> newLookupSchema(Class<T> messageType, MessageInfo descriptor) {
+ if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
+ return new Proto3LiteLookupSchema<T>(messageType, descriptor);
+ }
+ return new Proto3LookupSchema<T>(messageType, descriptor);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Proto3TableSchema.java b/java/core/src/main/java/com/google/protobuf/Proto3TableSchema.java
new file mode 100644
index 00000000..3a4f1018
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Proto3TableSchema.java
@@ -0,0 +1,242 @@
+// 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.Proto3Manifest.offset;
+import static com.google.protobuf.Proto3Manifest.type;
+
+import java.io.IOException;
+
+/**
+ * A generic, table-based schema that can be used with any proto3 message class. The message class
+ * must extend {@link GeneratedMessage}.
+ */
+final class Proto3TableSchema<T> extends AbstractProto3StandardSchema<T> {
+ private final Int2ObjectHashMap<Class<?>> messageFieldClassMap;
+
+ Proto3TableSchema(Class<T> messageClass, MessageInfo descriptor) {
+ super(messageClass, Proto3Manifest.newTableManfiest(descriptor));
+ this.messageFieldClassMap = descriptor.messageFieldClassMap();
+ }
+
+ @Override
+ public void mergeFrom(T message, Reader reader) throws IOException {
+ while (true) {
+ final int fieldNumber = reader.getFieldNumber();
+ final long pos = manifest.tablePositionForFieldNumber(fieldNumber);
+ if (pos < 0) {
+ // Unknown field.
+ if (reader.skipField()) {
+ continue;
+ }
+ // Done reading.
+ return;
+ }
+ final int typeAndOffset = manifest.typeAndOffsetAt(pos);
+
+ // Benchmarks have shown that switching on a byte is faster than an enum.
+ try {
+ switch (type(typeAndOffset)) {
+ case 0: //DOUBLE:
+ UnsafeUtil.putDouble(message, offset(typeAndOffset), reader.readDouble());
+ break;
+ case 1: //FLOAT:
+ UnsafeUtil.putFloat(message, offset(typeAndOffset), reader.readFloat());
+ break;
+ case 2: //INT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readInt64());
+ break;
+ case 3: //UINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readUInt64());
+ break;
+ case 4: //INT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readInt32());
+ break;
+ case 5: //FIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readFixed64());
+ break;
+ case 6: //FIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readFixed32());
+ break;
+ case 7: //BOOL:
+ UnsafeUtil.putBoolean(message, offset(typeAndOffset), reader.readBool());
+ break;
+ case 8: //STRING:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readString());
+ break;
+ case 9: //MESSAGE:
+ UnsafeUtil.putObject(
+ message,
+ offset(typeAndOffset),
+ reader.readMessage(messageFieldClassMap.get(fieldNumber)));
+ break;
+ case 10: //BYTES:
+ UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes());
+ break;
+ case 11: //UINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readUInt32());
+ break;
+ case 12: //ENUM:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readEnum());
+ break;
+ case 13: //SFIXED32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSFixed32());
+ break;
+ case 14: //SFIXED64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSFixed64());
+ break;
+ case 15: //SINT32:
+ UnsafeUtil.putInt(message, offset(typeAndOffset), reader.readSInt32());
+ break;
+ case 16: //SINT64:
+ UnsafeUtil.putLong(message, offset(typeAndOffset), reader.readSInt64());
+ break;
+ case 17: //DOUBLE_LIST:
+ reader.readDoubleList(SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 18: //FLOAT_LIST:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 19: //INT64_LIST:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 20: //UINT64_LIST:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 21: //INT32_LIST:
+ reader.readInt32List(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 22: //FIXED64_LIST:
+ reader.readFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 23: //FIXED32_LIST:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 24: //BOOL_LIST:
+ reader.readBoolList(SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 25: //STRING_LIST:
+ reader.readStringList(SchemaUtil.<String>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 26: //MESSAGE_LIST:
+ SchemaUtil.readMessageList(
+ message, offset(typeAndOffset), reader, messageFieldClassMap.get(fieldNumber));
+ break;
+ case 27: //BYTES_LIST:
+ reader.readBytesList(
+ SchemaUtil.<ByteString>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 28: //UINT32_LIST:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 29: //ENUM_LIST:
+ reader.readEnumList(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 30: //SFIXED32_LIST:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 31: //SFIXED64_LIST:
+ reader.readSFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 32: //SINT32_LIST:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 33: //SINT64_LIST:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 34: //DOUBLE_LIST_PACKED:
+ reader.readDoubleList(SchemaUtil.<Double>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 35: //FLOAT_LIST_PACKED:
+ reader.readFloatList(SchemaUtil.<Float>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 36: //INT64_LIST_PACKED:
+ reader.readInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 37: //UINT64_LIST_PACKED:
+ reader.readUInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 38: //INT32_LIST_PACKED:
+ reader.readInt32List(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 39: //FIXED64_LIST_PACKED:
+ reader.readFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 40: //FIXED32_LIST_PACKED:
+ reader.readFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 41: //BOOL_LIST_PACKED:
+ reader.readBoolList(SchemaUtil.<Boolean>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 42: //UINT32_LIST_PACKED:
+ reader.readUInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 43: //ENUM_LIST_PACKED:
+ reader.readEnumList(SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 44: //SFIXED32_LIST_PACKED:
+ reader.readSFixed32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 45: //SFIXED64_LIST_PACKED:
+ reader.readSFixed64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 46: //SINT32_LIST_PACKED:
+ reader.readSInt32List(
+ SchemaUtil.<Integer>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ case 47: //SINT64_LIST_PACKED:
+ reader.readSInt64List(SchemaUtil.<Long>mutableListAt(message, offset(typeAndOffset)));
+ break;
+ default:
+ // Assume we've landed on an empty entry. Treat it as an unknown field - just skip it.
+ if (!reader.skipField()) {
+ // Done reading.
+ return;
+ }
+ break;
+ }
+ // TODO(nathanmittler): Do we need to make lists immutable?
+ } catch (InvalidProtocolBufferException.InvalidWireTypeException e) {
+ // Treat fields with an invalid wire type as unknown fields (i.e. same as the default case).
+ if (!reader.skipField()) {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/ProtoSyntax.java b/java/core/src/main/java/com/google/protobuf/ProtoSyntax.java
new file mode 100644
index 00000000..851b839f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ProtoSyntax.java
@@ -0,0 +1,38 @@
+// 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;
+
+/** Represents the syntax version of the message. */
+@ExperimentalApi
+public enum ProtoSyntax {
+ PROTO2,
+ PROTO3;
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Protobuf.java b/java/core/src/main/java/com/google/protobuf/Protobuf.java
new file mode 100644
index 00000000..6c6aad24
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Protobuf.java
@@ -0,0 +1,148 @@
+// 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.checkNotNull;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Main runtime interface for protobuf. Applications should interact with this interface (rather
+ * than directly accessing internal APIs) in order to perform operations on protobuf messages.
+ */
+@ExperimentalApi
+public final class Protobuf {
+ private static final Protobuf INSTANCE = new Protobuf();
+
+ private final SchemaFactory schemaFactory;
+
+ // TODO(nathanmittler): Consider using ClassValue instead.
+ private final ConcurrentMap<Class<?>, Schema<?>> schemaCache =
+ new ConcurrentHashMap<Class<?>, Schema<?>>();
+
+ /** Gets the singleton instance of the Protobuf runtime. */
+ public static Protobuf getInstance() {
+ return INSTANCE;
+ }
+
+ /** Writes the given message to the target {@link Writer}. */
+ public <T> void writeTo(T message, Writer writer) {
+ schemaFor(message).writeTo(message, writer);
+ }
+
+ /** Reads fields from the given {@link Reader} and merges them into the message. */
+ public <T> void mergeFrom(T message, Reader reader) throws IOException {
+ schemaFor(message).mergeFrom(message, reader);
+ }
+
+ /** Gets the schema for the given message type. */
+ public <T> Schema<T> schemaFor(Class<T> messageType) {
+ checkNotNull(messageType, "messageType");
+ @SuppressWarnings("unchecked")
+ Schema<T> schema = (Schema<T>) schemaCache.get(messageType);
+ if (schema == null) {
+ schema = schemaFactory.createSchema(messageType);
+ @SuppressWarnings("unchecked")
+ Schema<T> previous = (Schema<T>) registerSchema(messageType, schema);
+ if (previous != null) {
+ // A new schema was registered by another thread.
+ schema = previous;
+ }
+ }
+ return schema;
+ }
+
+ /** Gets the schema for the given message. */
+ @SuppressWarnings("unchecked")
+ public <T> Schema<T> schemaFor(T message) {
+ return schemaFor((Class<T>) message.getClass());
+ }
+
+ /**
+ * Registers the given schema for the message type only if a schema was not already registered.
+ *
+ * @param messageType the type of message on which the schema operates.
+ * @param schema the schema for the message type.
+ * @return the previously registered schema, or {@code null} if the given schema was successfully
+ * registered.
+ */
+ public Schema<?> registerSchema(Class<?> messageType, Schema<?> schema) {
+ checkNotNull(messageType, "messageType");
+ checkNotNull(schema, "schema");
+ return schemaCache.putIfAbsent(messageType, schema);
+ }
+
+ /**
+ * Visible for testing only. Registers the given schema for the message type. If a schema was
+ * previously registered, it will be replaced by the provided schema.
+ *
+ * @param messageType the type of message on which the schema operates.
+ * @param schema the schema for the message type.
+ * @return the previously registered schema, or {@code null} if no schema was registered
+ * previously.
+ */
+ public Schema<?> registerSchemaOverride(Class<?> messageType, Schema<?> schema) {
+ checkNotNull(messageType, "messageType");
+ checkNotNull(schema, "schema");
+ return schemaCache.put(messageType, schema);
+ }
+
+ private Protobuf() {
+ // TODO(nathanmittler): Detect the proper factory for the platform.
+ SchemaFactory factory = null;
+ for (String className :
+ new String[] {
+ "com.google.frameworks.protobuf.experimental.android.schema.AndroidProto3SchemaFactory",
+ // TODO(nathanmittler): Remove this once the code has been completely ported over.
+ "com.google.frameworks.protobuf.experimental.schema.generic.Proto3SchemaFactory",
+ "com.google.protobuf.Proto3SchemaFactory"
+ }) {
+ factory = newSchemaFactory(className);
+ if (factory != null) {
+ break;
+ }
+ }
+ if (factory == null) {
+ throw new IllegalStateException("Unable to locate a default SchemaFactory. Check classpath.");
+ }
+ schemaFactory = factory;
+ }
+
+ private static SchemaFactory newSchemaFactory(String className) {
+ try {
+ return (SchemaFactory) Class.forName(className).getConstructor().newInstance();
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/ProtobufLists.java b/java/core/src/main/java/com/google/protobuf/ProtobufLists.java
new file mode 100644
index 00000000..e01f21ec
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ProtobufLists.java
@@ -0,0 +1,96 @@
+// 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 com.google.protobuf.Internal.BooleanList;
+import com.google.protobuf.Internal.DoubleList;
+import com.google.protobuf.Internal.FloatList;
+import com.google.protobuf.Internal.IntList;
+import com.google.protobuf.Internal.LongList;
+import com.google.protobuf.Internal.ProtobufList;
+
+/**
+ * Utility class for construction of lists that extend {@link ProtobufList}.
+ */
+@ExperimentalApi
+public final class ProtobufLists {
+ private ProtobufLists() {}
+
+ public static <E> ProtobufList<E> emptyProtobufList() {
+ return ProtobufArrayList.emptyList();
+ }
+
+ public static <E> ProtobufList<E> mutableCopy(ProtobufList<E> list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
+ public static BooleanList emptyBooleanList() {
+ return BooleanArrayList.emptyList();
+ }
+
+ public static BooleanList newBooleanList() {
+ return new BooleanArrayList();
+ }
+
+ public static IntList emptyIntList() {
+ return IntArrayList.emptyList();
+ }
+
+ public static IntList newIntList() {
+ return new IntArrayList();
+ }
+
+ public static LongList emptyLongList() {
+ return LongArrayList.emptyList();
+ }
+
+ public static LongList newLongList() {
+ return new LongArrayList();
+ }
+
+ public static FloatList emptyFloatList() {
+ return FloatArrayList.emptyList();
+ }
+
+ public static FloatList newFloatList() {
+ return new FloatArrayList();
+ }
+
+ public static DoubleList emptyDoubleList() {
+ return DoubleArrayList.emptyList();
+ }
+
+ public static DoubleList newDoubleList() {
+ return new DoubleArrayList();
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Reader.java b/java/core/src/main/java/com/google/protobuf/Reader.java
new file mode 100644
index 00000000..aa5b330f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Reader.java
@@ -0,0 +1,312 @@
+// 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.util.List;
+
+/** A reader of fields from a serialized protobuf message. */
+// TODO(nathanmittler): Refactor to allow the reader to allocate properly sized lists.
+@ExperimentalApi
+public interface Reader {
+ /** Value used to indicate that the end of input has been reached. */
+ int READ_DONE = Integer.MAX_VALUE;
+
+ /**
+ * Gets the field number for the current field being read.
+ *
+ * @return the current field number or {@link #READ_DONE} if the end of input has been reached.
+ */
+ int getFieldNumber() throws IOException;
+
+ /**
+ * Skips the current field and advances the reader to the next field.
+ *
+ * @return {@code true} if there are more fields or {@link #READ_DONE} if the end of input has
+ * been reached.
+ */
+ boolean skipField() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code DOUBLE} and advances the reader to the next
+ * field.
+ */
+ double readDouble() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code FLOAT} and advances the reader to the next
+ * field.
+ */
+ float readFloat() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code UINT64} and advances the reader to the next
+ * field.
+ */
+ long readUInt64() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code INT64} and advances the reader to the next
+ * field.
+ */
+ long readInt64() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code INT32} and advances the reader to the next
+ * field.
+ */
+ int readInt32() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code FIXED64} and advances the reader to the next
+ * field.
+ */
+ long readFixed64() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code FIXED32} and advances the reader to the next
+ * field.
+ */
+ int readFixed32() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code BOOL} and advances the reader to the next
+ * field.
+ */
+ boolean readBool() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code STRING} and advances the reader to the next
+ * field.
+ */
+ String readString() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code MESSAGE} and advances the reader to the next
+ * field.
+ */
+ <T> T readMessage(Class<T> clazz) throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code GROUP} and advances the reader to the next
+ * field.
+ *
+ * @deprecated groups fields are deprecated.
+ */
+ @Deprecated
+ <T> T readGroup(Class<T> clazz) throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code BYTES} and advances the reader to the next
+ * field.
+ */
+ ByteString readBytes() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code UINT32} and advances the reader to the next
+ * field.
+ */
+ int readUInt32() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code ENUM} and advances the reader to the next
+ * field.
+ */
+ int readEnum() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code SFIXED32} and advances the reader to the next
+ * field.
+ */
+ int readSFixed32() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code SFIXED64} and advances the reader to the next
+ * field.
+ */
+ long readSFixed64() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code SINT32} and advances the reader to the next
+ * field.
+ */
+ int readSInt32() throws IOException;
+
+ /**
+ * Reads and returns the next field of type {@code SINT64} and advances the reader to the next
+ * field.
+ */
+ long readSInt64() throws IOException;
+
+ /**
+ * Reads the next field of type {@code DOUBLE_LIST} or {@code DOUBLE_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readDoubleList(List<Double> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code FLOAT_LIST} or {@code FLOAT_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readFloatList(List<Float> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code UINT64_LIST} or {@code UINT64_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readUInt64List(List<Long> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code INT64_LIST} or {@code INT64_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readInt64List(List<Long> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code INT32_LIST} or {@code INT32_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readInt32List(List<Integer> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code FIXED64_LIST} or {@code FIXED64_LIST_PACKED} and advances
+ * the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readFixed64List(List<Long> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code FIXED32_LIST} or {@code FIXED32_LIST_PACKED} and advances
+ * the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readFixed32List(List<Integer> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code BOOL_LIST} or {@code BOOL_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readBoolList(List<Boolean> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code STRING_LIST} and advances the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readStringList(List<String> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code MESSAGE_LIST} and advances the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ * @param targetType the type of the elements stored in the {@code target} list.
+ */
+ <T> void readMessageList(List<T> target, Class<T> targetType) throws IOException;
+
+ /**
+ * Reads the next field of type {@code GROUP_LIST} and advances the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ * @param targetType the type of the elements stored in the {@code target} list.
+ * @deprecated groups fields are deprecated.
+ */
+ @Deprecated
+ <T> void readGroupList(List<T> target, Class<T> targetType) throws IOException;
+
+ /**
+ * Reads the next field of type {@code BYTES_LIST} and advances the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readBytesList(List<ByteString> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code UINT32_LIST} or {@code UINT32_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readUInt32List(List<Integer> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code ENUM_LIST} or {@code ENUM_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readEnumList(List<Integer> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code SFIXED32_LIST} or {@code SFIXED32_LIST_PACKED} and advances
+ * the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readSFixed32List(List<Integer> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code SFIXED64_LIST} or {@code SFIXED64_LIST_PACKED} and advances
+ * the reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readSFixed64List(List<Long> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code SINT32_LIST} or {@code SINT32_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readSInt32List(List<Integer> target) throws IOException;
+
+ /**
+ * Reads the next field of type {@code SINT64_LIST} or {@code SINT64_LIST_PACKED} and advances the
+ * reader to the next field.
+ *
+ * @param target the list that will receive the read values.
+ */
+ void readSInt64List(List<Long> target) throws IOException;
+}
diff --git a/java/core/src/main/java/com/google/protobuf/RopeByteString.java b/java/core/src/main/java/com/google/protobuf/RopeByteString.java
index 6fa555df..4c98b492 100644
--- a/java/core/src/main/java/com/google/protobuf/RopeByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/RopeByteString.java
@@ -406,6 +406,11 @@ final class RopeByteString extends ByteString {
right.writeTo(output);
}
+ @Override
+ void writeToReverse(ByteOutput output) throws IOException {
+ right.writeToReverse(output);
+ left.writeToReverse(output);
+ }
@Override
protected String toStringInternal(Charset charset) {
diff --git a/java/core/src/main/java/com/google/protobuf/Schema.java b/java/core/src/main/java/com/google/protobuf/Schema.java
new file mode 100644
index 00000000..be9f3347
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Schema.java
@@ -0,0 +1,55 @@
+// 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;
+
+/**
+ * A runtime schema for a single protobuf message. A schema provides operations on message
+ * instances such as serialization/deserialization.
+ */
+@ExperimentalApi
+public interface Schema<T> {
+ /**
+ * Writes the given message to the target {@link Writer}.
+ */
+ void writeTo(T message, Writer writer);
+
+ /**
+ * Reads fields from the given {@link Reader} and merges them into the message.
+ */
+ void mergeFrom(T message, Reader reader) throws IOException;
+
+ /**
+ * Creates a new instance of the message class.
+ */
+ T newInstance();
+}
diff --git a/java/core/src/main/java/com/google/protobuf/SchemaFactory.java b/java/core/src/main/java/com/google/protobuf/SchemaFactory.java
new file mode 100644
index 00000000..49682598
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/SchemaFactory.java
@@ -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.
+
+package com.google.protobuf;
+
+/**
+ * A factory that manufactures {@link Schema} instances for protobuf messages.
+ */
+@ExperimentalApi
+public interface SchemaFactory {
+ /**
+ * Creates a schema instance for the given protobuf message type.
+ */
+ <T> Schema<T> createSchema(Class<T> messageType);
+}
diff --git a/java/core/src/main/java/com/google/protobuf/SchemaUtil.java b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java
new file mode 100644
index 00000000..db2dc949
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java
@@ -0,0 +1,516 @@
+// 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 com.google.protobuf.Internal.ProtobufList;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Helper methods used by schemas. */
+@ExperimentalApi
+public final class SchemaUtil {
+ private static final Class<?> UNMODIFIABLE_LIST_CLASS =
+ Collections.unmodifiableList(new ArrayList<Integer>()).getClass();
+ private static final Class<?> ABSTRACT_MESSAGE_CLASS = getAbstractMessageClass();
+ private static final Class<?> GENERATED_MESSAGE_CLASS = getGeneratedMessageClass();
+ private static final Class<?> UNKNOWN_FIELD_SET_CLASS = getUnknownFieldSetClass();
+ private static final long UNKNOWN_FIELD_OFFSET = unknownFieldOffset();
+ private static final long LITE_UNKNOWN_FIELD_OFFSET = unknownFieldLiteOffset();
+ private static final Object DEFAULT_UNKNOWN_FIELD_SET = defaultUnknownFieldSet();
+ private static final long MEMOIZED_SIZE_FIELD_OFFSET = getMemoizedSizeFieldOffset();
+ private static final long LITE_MEMOIZED_SIZE_FIELD_OFFSET = getLiteMemoizedSizeFieldOffset();
+
+ private SchemaUtil() {}
+
+ /**
+ * Initializes all of the base class fields for the given {@link GeneratedMessageLite} to their
+ * default values.
+ */
+ public static void initLiteBaseClassFields(Object msg) {
+ UnsafeUtil.putObject(msg, LITE_UNKNOWN_FIELD_OFFSET, UnknownFieldSetLite.getDefaultInstance());
+ UnsafeUtil.putInt(msg, LITE_MEMOIZED_SIZE_FIELD_OFFSET, -1);
+ }
+
+ /**
+ * Initializes all of the base class fields for the given {@link
+ * com.google.protobuf.GeneratedMessage} to their default values.
+ */
+ public static void initBaseClassFields(Object msg) {
+ UnsafeUtil.putObject(msg, UNKNOWN_FIELD_OFFSET, DEFAULT_UNKNOWN_FIELD_SET);
+ UnsafeUtil.putInt(msg, MEMOIZED_SIZE_FIELD_OFFSET, -1);
+ }
+
+ /**
+ * Requires that the given message extend {@link com.google.protobuf.GeneratedMessage} or {@link
+ * GeneratedMessageLite}.
+ */
+ public static void requireGeneratedMessage(Class<?> messageType) {
+ if (!GeneratedMessageLite.class.isAssignableFrom(messageType)
+ && !GENERATED_MESSAGE_CLASS.isAssignableFrom(messageType)) {
+ throw new IllegalArgumentException(
+ "Message classes must extend GeneratedMessage or GeneratedMessageLite");
+ }
+ }
+
+ /** Initializes the message's memoized size to the default value. */
+ public static void initMemoizedSize(Object msg) {
+ final long fieldOffset =
+ (msg instanceof GeneratedMessageLite)
+ ? LITE_MEMOIZED_SIZE_FIELD_OFFSET
+ : MEMOIZED_SIZE_FIELD_OFFSET;
+
+ if (fieldOffset < 0) {
+ throw new IllegalArgumentException(
+ "Unable to identify memoizedSize field offset for message of type: "
+ + msg.getClass().getName());
+ }
+
+ UnsafeUtil.putInt(msg, fieldOffset, -1);
+ }
+
+ public static void writeDouble(int fieldNumber, double value, Writer writer) {
+ if (Double.compare(value, 0.0) != 0) {
+ writer.writeDouble(fieldNumber, value);
+ }
+ }
+
+ public static void writeFloat(int fieldNumber, float value, Writer writer) {
+ if (Float.compare(value, 0.0f) != 0) {
+ writer.writeFloat(fieldNumber, value);
+ }
+ }
+
+ public static void writeInt64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeInt64(fieldNumber, value);
+ }
+ }
+
+ public static void writeUInt64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeUInt64(fieldNumber, value);
+ }
+ }
+
+ public static void writeSInt64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeSInt64(fieldNumber, value);
+ }
+ }
+
+ public static void writeFixed64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeFixed64(fieldNumber, value);
+ }
+ }
+
+ public static void writeSFixed64(int fieldNumber, long value, Writer writer) {
+ if (value != 0) {
+ writer.writeSFixed64(fieldNumber, value);
+ }
+ }
+
+ public static void writeInt32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeInt32(fieldNumber, value);
+ }
+ }
+
+ public static void writeUInt32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeUInt32(fieldNumber, value);
+ }
+ }
+
+ public static void writeSInt32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeSInt32(fieldNumber, value);
+ }
+ }
+
+ public static void writeFixed32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeFixed32(fieldNumber, value);
+ }
+ }
+
+ public static void writeSFixed32(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeSFixed32(fieldNumber, value);
+ }
+ }
+
+ public static void writeEnum(int fieldNumber, int value, Writer writer) {
+ if (value != 0) {
+ writer.writeEnum(fieldNumber, value);
+ }
+ }
+
+ public static void writeBool(int fieldNumber, boolean value, Writer writer) {
+ if (value) {
+ writer.writeBool(fieldNumber, true);
+ }
+ }
+
+ public static void writeString(int fieldNumber, Object value, Writer writer) {
+ if (value instanceof String) {
+ writeStringInternal(fieldNumber, (String) value, writer);
+ } else {
+ writeBytes(fieldNumber, (ByteString) value, writer);
+ }
+ }
+
+ private static void writeStringInternal(int fieldNumber, String value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeString(fieldNumber, value);
+ }
+ }
+
+ public static void writeBytes(int fieldNumber, ByteString value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeBytes(fieldNumber, value);
+ }
+ }
+
+ public static void writeMessage(int fieldNumber, Object value, Writer writer) {
+ if (value != null) {
+ writer.writeMessage(fieldNumber, value);
+ }
+ }
+
+ public static void writeDoubleList(
+ int fieldNumber, List<Double> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeDoubleList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeFloatList(
+ int fieldNumber, List<Float> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeFloatList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeInt64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeInt64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeUInt64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeUInt64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSInt64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSInt64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeFixed64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeFixed64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSFixed64List(
+ int fieldNumber, List<Long> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSFixed64List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeInt32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeInt32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeUInt32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeUInt32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSInt32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSInt32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeFixed32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeFixed32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeSFixed32List(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeSFixed32List(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeEnumList(
+ int fieldNumber, List<Integer> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeEnumList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeBoolList(
+ int fieldNumber, List<Boolean> value, Writer writer, boolean packed) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeBoolList(fieldNumber, value, packed);
+ }
+ }
+
+ public static void writeStringList(int fieldNumber, List<String> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeStringList(fieldNumber, value);
+ }
+ }
+
+ public static void writeBytesList(int fieldNumber, List<ByteString> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeBytesList(fieldNumber, value);
+ }
+ }
+
+ public static void writeMessageList(int fieldNumber, List<?> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeMessageList(fieldNumber, value);
+ }
+ }
+
+ public static void writeGroupList(int fieldNumber, List<?> value, Writer writer) {
+ if (value != null && !value.isEmpty()) {
+ writer.writeGroupList(fieldNumber, value);
+ }
+ }
+
+ /**
+ * Used for protobuf-lite (i.e. messages that extend {@link GeneratedMessageLite}). Reads in a
+ * list of messages into a target {@link ProtobufList} field in the message.
+ */
+ public static final <T> void readProtobufMessageList(
+ Object message, long offset, Reader reader, Class<T> targetType) throws IOException {
+ reader.readMessageList(SchemaUtil.<T>mutableProtobufListAt(message, offset), targetType);
+ }
+
+ /**
+ * Used for standard protobuf messages (i.e. messages that extend {@link
+ * com.google.protobuf.GeneratedMessage}). Reads in a list of messages into a target list field in
+ * the message.
+ */
+ public static <T> void readMessageList(
+ Object message, long offset, Reader reader, Class<T> targetType) throws IOException {
+ reader.readMessageList(SchemaUtil.<T>mutableListAt(message, offset), targetType);
+ }
+
+ /**
+ * Used for group types. Reads in a list of group messages into a target list field in the
+ * message.
+ */
+ public static <T> void readGroupList(
+ Object message, long offset, Reader reader, Class<T> targetType) throws IOException {
+ reader.readGroupList(SchemaUtil.<T>mutableListAt(message, offset), targetType);
+ }
+
+ /**
+ * Used for protobuf-lite (i.e. messages that extend GeneratedMessageLite). Converts the {@link
+ * ProtobufList} at the given field position in the message into a mutable list (if it isn't
+ * already).
+ */
+ @SuppressWarnings("unchecked")
+ public static <L> ProtobufList<L> mutableProtobufListAt(Object message, long pos) {
+ ProtobufList<L> list = (ProtobufList<L>) UnsafeUtil.getObject(message, pos);
+ if (!list.isModifiable()) {
+ int size = list.size();
+ list = list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ UnsafeUtil.putObject(message, pos, list);
+ }
+ return list;
+ }
+
+ /** Converts the list at the given field location in the message into a mutable list. */
+ @SuppressWarnings("unchecked")
+ public static <L> List<L> mutableListAt(Object message, long pos) {
+ List<L> list = (List<L>) UnsafeUtil.getObject(message, pos);
+ if (list.isEmpty()) {
+ list =
+ (list instanceof LazyStringList)
+ ? (List<L>) new LazyStringArrayList()
+ : new ArrayList<L>();
+ UnsafeUtil.putObject(message, pos, list);
+ } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) {
+ list = new ArrayList<L>(list);
+ UnsafeUtil.putObject(message, pos, list);
+ } else if (list instanceof UnmodifiableLazyStringList) {
+ // Convert the list to a mutable list.
+ list = (List<L>) new LazyStringArrayList((List<String>) list);
+ UnsafeUtil.putObject(message, pos, list);
+ }
+ return list;
+ }
+
+ /**
+ * Determines whether to issue tableswitch or lookupswitch for the mergeFrom method.
+ *
+ * @see #shouldUseTableSwitch(int, int, int)
+ */
+ public static boolean shouldUseTableSwitch(List<FieldInfo> fields) {
+ // Determine whether to issue a tableswitch or a lookupswitch
+ // instruction.
+ if (fields.isEmpty()) {
+ return false;
+ }
+
+ int lo = fields.get(0).getFieldNumber();
+ int hi = fields.get(fields.size() - 1).getFieldNumber();
+ return shouldUseTableSwitch(lo, hi, fields.size());
+ }
+
+ /**
+ * Determines whether to issue tableswitch or lookupswitch for the mergeFrom method. This is based
+ * on the <a href=
+ * "http://hg.openjdk.java.net/jdk8/jdk8/langtools/file/30db5e0aaf83/src/share/classes/com/sun/tools/javac/jvm/Gen.java#l1159">
+ * logic in the JDK</a>.
+ *
+ * @param lo the lowest fieldNumber contained within the message.
+ * @param hi the higest fieldNumber contained within the message.
+ * @param numFields the total number of fields in the message.
+ * @return {@code true} if tableswitch should be used, rather than lookupswitch.
+ */
+ public static boolean shouldUseTableSwitch(int lo, int hi, int numFields) {
+ long tableSpaceCost = 4 + ((long) hi - lo + 1); // words
+ long tableTimeCost = 3; // comparisons
+ long lookupSpaceCost = 3 + 2 * (long) numFields;
+ long lookupTimeCost = numFields;
+ return tableSpaceCost + 3 * tableTimeCost <= lookupSpaceCost + 3 * lookupTimeCost;
+ }
+
+ private static Class<?> getAbstractMessageClass() {
+ try {
+ return Class.forName("com.google.protobuf.AbstractMessage");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static Class<?> getGeneratedMessageClass() {
+ try {
+ return Class.forName("com.google.protobuf.GeneratedMessage");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static Class<?> getUnknownFieldSetClass() {
+ try {
+ return Class.forName("com.google.protobuf.UnknownFieldSet");
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+
+ private static long unknownFieldOffset() {
+ try {
+ if (GENERATED_MESSAGE_CLASS != null) {
+ Field field = GENERATED_MESSAGE_CLASS.getDeclaredField("unknownFields");
+ return UnsafeUtil.objectFieldOffset(field);
+ }
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+
+ private static long unknownFieldLiteOffset() {
+ try {
+ Field field = GeneratedMessageLite.class.getDeclaredField("unknownFields");
+ return UnsafeUtil.objectFieldOffset(field);
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+
+ private static Object defaultUnknownFieldSet() {
+ try {
+ if (UNKNOWN_FIELD_SET_CLASS != null) {
+ Method method = UNKNOWN_FIELD_SET_CLASS.getDeclaredMethod("getDefaultInstance");
+ return method.invoke(null);
+ }
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return null;
+ }
+
+ private static long getMemoizedSizeFieldOffset() {
+ try {
+ if (ABSTRACT_MESSAGE_CLASS != null) {
+ Field field = ABSTRACT_MESSAGE_CLASS.getDeclaredField("memoizedSize");
+ return UnsafeUtil.objectFieldOffset(field);
+ }
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+
+ private static long getLiteMemoizedSizeFieldOffset() {
+ try {
+ Field field = GeneratedMessageLite.class.getDeclaredField("memoizedSerializedSize");
+ return UnsafeUtil.objectFieldOffset(field);
+ } catch (Throwable e) {
+ // Do nothing.
+ }
+ return -1;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Writer.java b/java/core/src/main/java/com/google/protobuf/Writer.java
new file mode 100644
index 00000000..829f270f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Writer.java
@@ -0,0 +1,155 @@
+// 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.util.List;
+
+/** A writer that performs serialization of protobuf message fields. */
+@ExperimentalApi
+public interface Writer {
+ /** Writes a field of type {@link FieldType#SFIXED32}. */
+ void writeSFixed32(int fieldNumber, int value);
+
+ /** Writes a field of type {@link FieldType#INT64}. */
+ void writeInt64(int fieldNumber, long value);
+
+ /** Writes a field of type {@link FieldType#SFIXED64}. */
+ void writeSFixed64(int fieldNumber, long value);
+
+ /** Writes a field of type {@link FieldType#FLOAT}. */
+ void writeFloat(int fieldNumber, float value);
+
+ /** Writes a field of type {@link FieldType#DOUBLE}. */
+ void writeDouble(int fieldNumber, double value);
+
+ /** Writes a field of type {@link FieldType#ENUM}. */
+ void writeEnum(int fieldNumber, int value);
+
+ /** Writes a field of type {@link FieldType#UINT64}. */
+ void writeUInt64(int fieldNumber, long value);
+
+ /** Writes a field of type {@link FieldType#INT32}. */
+ void writeInt32(int fieldNumber, int value);
+
+ /** Writes a field of type {@link FieldType#FIXED64}. */
+ void writeFixed64(int fieldNumber, long value);
+
+ /** Writes a field of type {@link FieldType#FIXED32}. */
+ void writeFixed32(int fieldNumber, int value);
+
+ /** Writes a field of type {@link FieldType#BOOL}. */
+ void writeBool(int fieldNumber, boolean value);
+
+ /** Writes a field of type {@link FieldType#STRING}. */
+ void writeString(int fieldNumber, String value);
+
+ /** Writes a field of type {@link FieldType#BYTES}. */
+ void writeBytes(int fieldNumber, ByteString value);
+
+ /** Writes a field of type {@link FieldType#UINT32}. */
+ void writeUInt32(int fieldNumber, int value);
+
+ /** Writes a field of type {@link FieldType#SINT32}. */
+ void writeSInt32(int fieldNumber, int value);
+
+ /** Writes a field of type {@link FieldType#SINT64}. */
+ void writeSInt64(int fieldNumber, long value);
+
+ /** Writes a field of type {@link FieldType#MESSAGE}. */
+ void writeMessage(int fieldNumber, Object value);
+
+ /**
+ * Writes a field of type {@link FieldType#GROUP}.
+ *
+ * @deprecated groups fields are deprecated.
+ */
+ @Deprecated
+ void writeGroup(int fieldNumber, Object value);
+
+ /** Writes a list field of type {@link FieldType#INT32}. */
+ void writeInt32List(int fieldNumber, List<Integer> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#FIXED32}. */
+ void writeFixed32List(int fieldNumber, List<Integer> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#INT64}. */
+ void writeInt64List(int fieldNumber, List<Long> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#UINT64}. */
+ void writeUInt64List(int fieldNumber, List<Long> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#FIXED64}. */
+ void writeFixed64List(int fieldNumber, List<Long> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#FLOAT}. */
+ void writeFloatList(int fieldNumber, List<Float> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#DOUBLE}. */
+ void writeDoubleList(int fieldNumber, List<Double> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#ENUM}. */
+ void writeEnumList(int fieldNumber, List<Integer> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#BOOL}. */
+ void writeBoolList(int fieldNumber, List<Boolean> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#STRING}. */
+ void writeStringList(int fieldNumber, List<String> value);
+
+ /** Writes a list field of type {@link FieldType#BYTES}. */
+ void writeBytesList(int fieldNumber, List<ByteString> value);
+
+ /** Writes a list field of type {@link FieldType#UINT32}. */
+ void writeUInt32List(int fieldNumber, List<Integer> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#SFIXED32}. */
+ void writeSFixed32List(int fieldNumber, List<Integer> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#SFIXED64}. */
+ void writeSFixed64List(int fieldNumber, List<Long> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#SINT32}. */
+ void writeSInt32List(int fieldNumber, List<Integer> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#SINT64}. */
+ void writeSInt64List(int fieldNumber, List<Long> value, boolean packed);
+
+ /** Writes a list field of type {@link FieldType#MESSAGE}. */
+ void writeMessageList(int fieldNumber, List<?> value);
+
+ /**
+ * Writes a list field of type {@link FieldType#GROUP}.
+ *
+ * @deprecated groups fields are deprecated.
+ */
+ @Deprecated
+ void writeGroupList(int fieldNumber, List<?> value);
+}