| // 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.MessageSchema.getMutableUnknownFields; |
| |
| import com.google.protobuf.Internal.ProtobufList; |
| import java.io.IOException; |
| |
| /** |
| * Helper functions to decode protobuf wire format from a byte array. |
| * |
| * <p>Note that these functions don't do boundary check on the byte array but instead rely on Java |
| * VM to check it. That means parsing routines utilizing these functions must catch |
| * IndexOutOfBoundsException and convert it to protobuf's InvalidProtocolBufferException when |
| * crossing protobuf public API boundaries. |
| */ |
| final class ArrayDecoders { |
| /** |
| * A helper used to return multiple values in a Java function. Java doesn't natively support |
| * returning multiple values in a function. Creating a new Object to hold the return values will |
| * be too expensive. Instead, we pass a Registers instance to functions that want to return |
| * multiple values and let the function set the return value in this Registers instance instead. |
| * |
| * <p>TODO(xiaofeng): This could be merged into CodedInputStream or CodedInputStreamReader which |
| * is already being passed through all the parsing routines. |
| */ |
| static final class Registers { |
| public int int1; |
| public long long1; |
| public Object object1; |
| public final ExtensionRegistryLite extensionRegistry; |
| |
| Registers() { |
| this.extensionRegistry = ExtensionRegistryLite.getEmptyRegistry(); |
| } |
| |
| Registers(ExtensionRegistryLite extensionRegistry) { |
| if (extensionRegistry == null) { |
| throw new NullPointerException(); |
| } |
| this.extensionRegistry = extensionRegistry; |
| } |
| } |
| |
| /** |
| * Decodes a varint. Returns the position after the varint. The decoded varint is stored in |
| * registers.int1. |
| */ |
| static int decodeVarint32(byte[] data, int position, Registers registers) { |
| int value = data[position++]; |
| if (value >= 0) { |
| registers.int1 = value; |
| return position; |
| } |
| return decodeVarint32(value, data, position, registers); |
| } |
| |
| /** Like decodeVarint32 except that the first byte is already read. */ |
| static int decodeVarint32(int firstByte, byte[] data, int position, Registers registers) { |
| int value = firstByte & 0x7F; |
| final byte b2 = data[position++]; |
| if (b2 >= 0) { |
| registers.int1 = value | ((int) b2 << 7); |
| return position; |
| } |
| value |= (b2 & 0x7F) << 7; |
| |
| final byte b3 = data[position++]; |
| if (b3 >= 0) { |
| registers.int1 = value | ((int) b3 << 14); |
| return position; |
| } |
| value |= (b3 & 0x7F) << 14; |
| |
| final byte b4 = data[position++]; |
| if (b4 >= 0) { |
| registers.int1 = value | ((int) b4 << 21); |
| return position; |
| } |
| value |= (b4 & 0x7F) << 21; |
| |
| final byte b5 = data[position++]; |
| if (b5 >= 0) { |
| registers.int1 = value | ((int) b5 << 28); |
| return position; |
| } |
| value |= (b5 & 0x7F) << 28; |
| |
| while (data[position++] < 0) {} |
| |
| registers.int1 = value; |
| return position; |
| } |
| |
| /** |
| * Decodes a varint. Returns the position after the varint. The decoded varint is stored in |
| * registers.long1. |
| */ |
| static int decodeVarint64(byte[] data, int position, Registers registers) { |
| long value = data[position++]; |
| if (value >= 0) { |
| registers.long1 = value; |
| return position; |
| } else { |
| return decodeVarint64(value, data, position, registers); |
| } |
| } |
| |
| /** Like decodeVarint64 except that the first byte is already read. */ |
| static int decodeVarint64(long firstByte, byte[] data, int position, Registers registers) { |
| long value = firstByte & 0x7F; |
| byte next = data[position++]; |
| int shift = 7; |
| value |= (long) (next & 0x7F) << 7; |
| while (next < 0) { |
| next = data[position++]; |
| shift += 7; |
| value |= (long) (next & 0x7F) << shift; |
| } |
| registers.long1 = value; |
| return position; |
| } |
| |
| /** Decodes and returns a fixed32 value. */ |
| static int decodeFixed32(byte[] data, int position) { |
| return (data[position] & 0xff) |
| | ((data[position + 1] & 0xff) << 8) |
| | ((data[position + 2] & 0xff) << 16) |
| | ((data[position + 3] & 0xff) << 24); |
| } |
| |
| /** Decodes and returns a fixed64 value. */ |
| static long decodeFixed64(byte[] data, int position) { |
| return (data[position] & 0xffL) |
| | ((data[position + 1] & 0xffL) << 8) |
| | ((data[position + 2] & 0xffL) << 16) |
| | ((data[position + 3] & 0xffL) << 24) |
| | ((data[position + 4] & 0xffL) << 32) |
| | ((data[position + 5] & 0xffL) << 40) |
| | ((data[position + 6] & 0xffL) << 48) |
| | ((data[position + 7] & 0xffL) << 56); |
| } |
| |
| /** Decodes and returns a double value. */ |
| static double decodeDouble(byte[] data, int position) { |
| return Double.longBitsToDouble(decodeFixed64(data, position)); |
| } |
| |
| /** Decodes and returns a float value. */ |
| static float decodeFloat(byte[] data, int position) { |
| return Float.intBitsToFloat(decodeFixed32(data, position)); |
| } |
| |
| /** Decodes a string value. */ |
| static int decodeString(byte[] data, int position, Registers registers) |
| throws InvalidProtocolBufferException { |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length == 0) { |
| registers.object1 = ""; |
| return position; |
| } else { |
| registers.object1 = new String(data, position, length, Internal.UTF_8); |
| return position + length; |
| } |
| } |
| |
| /** Decodes a string value with utf8 check. */ |
| static int decodeStringRequireUtf8(byte[] data, int position, Registers registers) |
| throws InvalidProtocolBufferException { |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length == 0) { |
| registers.object1 = ""; |
| return position; |
| } else { |
| registers.object1 = Utf8.decodeUtf8(data, position, length); |
| return position + length; |
| } |
| } |
| |
| /** Decodes a bytes value. */ |
| static int decodeBytes(byte[] data, int position, Registers registers) |
| throws InvalidProtocolBufferException { |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length > data.length - position) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } else if (length == 0) { |
| registers.object1 = ByteString.EMPTY; |
| return position; |
| } else { |
| registers.object1 = ByteString.copyFrom(data, position, length); |
| return position + length; |
| } |
| } |
| |
| /** Decodes a message value. */ |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| static int decodeMessageField( |
| Schema schema, byte[] data, int position, int limit, Registers registers) throws IOException { |
| int length = data[position++]; |
| if (length < 0) { |
| position = decodeVarint32(length, data, position, registers); |
| length = registers.int1; |
| } |
| if (length < 0 || length > limit - position) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| Object result = schema.newInstance(); |
| schema.mergeFrom(result, data, position, position + length, registers); |
| schema.makeImmutable(result); |
| registers.object1 = result; |
| return position + length; |
| } |
| |
| /** Decodes a group value. */ |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| static int decodeGroupField( |
| Schema schema, byte[] data, int position, int limit, int endGroup, Registers registers) |
| throws IOException { |
| // A group field must has a MessageSchema (the only other subclass of Schema is MessageSetSchema |
| // and it can't be used in group fields). |
| final MessageSchema messageSchema = (MessageSchema) schema; |
| Object result = messageSchema.newInstance(); |
| // It's OK to directly use parseProto2Message since proto3 doesn't have group. |
| final int endPosition = |
| messageSchema.parseProto2Message(result, data, position, limit, endGroup, registers); |
| messageSchema.makeImmutable(result); |
| registers.object1 = result; |
| return endPosition; |
| } |
| |
| /** Decodes a repeated 32-bit varint field. Returns the position after all read values. */ |
| static int decodeVarint32List( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final IntArrayList output = (IntArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| output.addInt(registers.int1); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint32(data, nextPosition, registers); |
| output.addInt(registers.int1); |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated 64-bit varint field. Returns the position after all read values. */ |
| static int decodeVarint64List( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final LongArrayList output = (LongArrayList) list; |
| position = decodeVarint64(data, position, registers); |
| output.addLong(registers.long1); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint64(data, nextPosition, registers); |
| output.addLong(registers.long1); |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated fixed32 field. Returns the position after all read values. */ |
| static int decodeFixed32List( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final IntArrayList output = (IntArrayList) list; |
| output.addInt(decodeFixed32(data, position)); |
| position += 4; |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| output.addInt(decodeFixed32(data, nextPosition)); |
| position = nextPosition + 4; |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated fixed64 field. Returns the position after all read values. */ |
| static int decodeFixed64List( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final LongArrayList output = (LongArrayList) list; |
| output.addLong(decodeFixed64(data, position)); |
| position += 8; |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| output.addLong(decodeFixed64(data, nextPosition)); |
| position = nextPosition + 8; |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated float field. Returns the position after all read values. */ |
| static int decodeFloatList( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final FloatArrayList output = (FloatArrayList) list; |
| output.addFloat(decodeFloat(data, position)); |
| position += 4; |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| output.addFloat(decodeFloat(data, nextPosition)); |
| position = nextPosition + 4; |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated double field. Returns the position after all read values. */ |
| static int decodeDoubleList( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final DoubleArrayList output = (DoubleArrayList) list; |
| output.addDouble(decodeDouble(data, position)); |
| position += 8; |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| output.addDouble(decodeDouble(data, nextPosition)); |
| position = nextPosition + 8; |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated boolean field. Returns the position after all read values. */ |
| static int decodeBoolList( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final BooleanArrayList output = (BooleanArrayList) list; |
| position = decodeVarint64(data, position, registers); |
| output.addBoolean(registers.long1 != 0); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint64(data, nextPosition, registers); |
| output.addBoolean(registers.long1 != 0); |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated sint32 field. Returns the position after all read values. */ |
| static int decodeSInt32List( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final IntArrayList output = (IntArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| output.addInt(CodedInputStream.decodeZigZag32(registers.int1)); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint32(data, nextPosition, registers); |
| output.addInt(CodedInputStream.decodeZigZag32(registers.int1)); |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated sint64 field. Returns the position after all read values. */ |
| static int decodeSInt64List( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) { |
| final LongArrayList output = (LongArrayList) list; |
| position = decodeVarint64(data, position, registers); |
| output.addLong(CodedInputStream.decodeZigZag64(registers.long1)); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint64(data, nextPosition, registers); |
| output.addLong(CodedInputStream.decodeZigZag64(registers.long1)); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed 32-bit varint field. Returns the position after all read values. */ |
| static int decodePackedVarint32List( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final IntArrayList output = (IntArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| position = decodeVarint32(data, position, registers); |
| output.addInt(registers.int1); |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed 64-bit varint field. Returns the position after all read values. */ |
| static int decodePackedVarint64List( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final LongArrayList output = (LongArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| position = decodeVarint64(data, position, registers); |
| output.addLong(registers.long1); |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed fixed32 field. Returns the position after all read values. */ |
| static int decodePackedFixed32List( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final IntArrayList output = (IntArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| output.addInt(decodeFixed32(data, position)); |
| position += 4; |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed fixed64 field. Returns the position after all read values. */ |
| static int decodePackedFixed64List( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final LongArrayList output = (LongArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| output.addLong(decodeFixed64(data, position)); |
| position += 8; |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed float field. Returns the position after all read values. */ |
| static int decodePackedFloatList( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final FloatArrayList output = (FloatArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| output.addFloat(decodeFloat(data, position)); |
| position += 4; |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed double field. Returns the position after all read values. */ |
| static int decodePackedDoubleList( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final DoubleArrayList output = (DoubleArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| output.addDouble(decodeDouble(data, position)); |
| position += 8; |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed boolean field. Returns the position after all read values. */ |
| static int decodePackedBoolList( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final BooleanArrayList output = (BooleanArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| position = decodeVarint64(data, position, registers); |
| output.addBoolean(registers.long1 != 0); |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed sint32 field. Returns the position after all read values. */ |
| static int decodePackedSInt32List( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final IntArrayList output = (IntArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| position = decodeVarint32(data, position, registers); |
| output.addInt(CodedInputStream.decodeZigZag32(registers.int1)); |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a packed sint64 field. Returns the position after all read values. */ |
| @SuppressWarnings("unchecked") |
| static int decodePackedSInt64List( |
| byte[] data, int position, ProtobufList<?> list, Registers registers) throws IOException { |
| final LongArrayList output = (LongArrayList) list; |
| position = decodeVarint32(data, position, registers); |
| final int fieldLimit = position + registers.int1; |
| while (position < fieldLimit) { |
| position = decodeVarint64(data, position, registers); |
| output.addLong(CodedInputStream.decodeZigZag64(registers.long1)); |
| } |
| if (position != fieldLimit) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated string field. Returns the position after all read values. */ |
| @SuppressWarnings("unchecked") |
| static int decodeStringList( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) |
| throws InvalidProtocolBufferException { |
| final ProtobufList<String> output = (ProtobufList<String>) list; |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length == 0) { |
| output.add(""); |
| } else { |
| String value = new String(data, position, length, Internal.UTF_8); |
| output.add(value); |
| position += length; |
| } |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint32(data, nextPosition, registers); |
| final int nextLength = registers.int1; |
| if (nextLength < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (nextLength == 0) { |
| output.add(""); |
| } else { |
| String value = new String(data, position, nextLength, Internal.UTF_8); |
| output.add(value); |
| position += nextLength; |
| } |
| } |
| return position; |
| } |
| |
| /** |
| * Decodes a repeated string field with utf8 check. Returns the position after all read values. |
| */ |
| @SuppressWarnings("unchecked") |
| static int decodeStringListRequireUtf8( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) |
| throws InvalidProtocolBufferException { |
| final ProtobufList<String> output = (ProtobufList<String>) list; |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length == 0) { |
| output.add(""); |
| } else { |
| if (!Utf8.isValidUtf8(data, position, position + length)) { |
| throw InvalidProtocolBufferException.invalidUtf8(); |
| } |
| String value = new String(data, position, length, Internal.UTF_8); |
| output.add(value); |
| position += length; |
| } |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint32(data, nextPosition, registers); |
| final int nextLength = registers.int1; |
| if (nextLength < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (nextLength == 0) { |
| output.add(""); |
| } else { |
| if (!Utf8.isValidUtf8(data, position, position + nextLength)) { |
| throw InvalidProtocolBufferException.invalidUtf8(); |
| } |
| String value = new String(data, position, nextLength, Internal.UTF_8); |
| output.add(value); |
| position += nextLength; |
| } |
| } |
| return position; |
| } |
| |
| /** Decodes a repeated bytes field. Returns the position after all read values. */ |
| @SuppressWarnings("unchecked") |
| static int decodeBytesList( |
| int tag, byte[] data, int position, int limit, ProtobufList<?> list, Registers registers) |
| throws InvalidProtocolBufferException { |
| final ProtobufList<ByteString> output = (ProtobufList<ByteString>) list; |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length > data.length - position) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } else if (length == 0) { |
| output.add(ByteString.EMPTY); |
| } else { |
| output.add(ByteString.copyFrom(data, position, length)); |
| position += length; |
| } |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeVarint32(data, nextPosition, registers); |
| final int nextLength = registers.int1; |
| if (nextLength < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (nextLength > data.length - position) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } else if (nextLength == 0) { |
| output.add(ByteString.EMPTY); |
| } else { |
| output.add(ByteString.copyFrom(data, position, nextLength)); |
| position += nextLength; |
| } |
| } |
| return position; |
| } |
| |
| /** |
| * Decodes a repeated message field |
| * |
| * @return The position of after read all messages |
| */ |
| @SuppressWarnings({"unchecked"}) |
| static int decodeMessageList( |
| Schema<?> schema, |
| int tag, |
| byte[] data, |
| int position, |
| int limit, |
| ProtobufList<?> list, |
| Registers registers) |
| throws IOException { |
| final ProtobufList<Object> output = (ProtobufList<Object>) list; |
| position = decodeMessageField(schema, data, position, limit, registers); |
| output.add(registers.object1); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeMessageField(schema, data, nextPosition, limit, registers); |
| output.add(registers.object1); |
| } |
| return position; |
| } |
| |
| /** |
| * Decodes a repeated group field |
| * |
| * @return The position of after read all groups |
| */ |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| static int decodeGroupList( |
| Schema schema, |
| int tag, |
| byte[] data, |
| int position, |
| int limit, |
| ProtobufList<?> list, |
| Registers registers) |
| throws IOException { |
| final ProtobufList<Object> output = (ProtobufList<Object>) list; |
| final int endgroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP; |
| position = decodeGroupField(schema, data, position, limit, endgroup, registers); |
| output.add(registers.object1); |
| while (position < limit) { |
| int nextPosition = decodeVarint32(data, position, registers); |
| if (tag != registers.int1) { |
| break; |
| } |
| position = decodeGroupField(schema, data, nextPosition, limit, endgroup, registers); |
| output.add(registers.object1); |
| } |
| return position; |
| } |
| |
| static int decodeExtensionOrUnknownField( |
| int tag, byte[] data, int position, int limit, |
| Object message, |
| MessageLite defaultInstance, |
| UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite> unknownFieldSchema, |
| Registers registers) |
| throws IOException { |
| final int number = tag >>> 3; |
| GeneratedMessageLite.GeneratedExtension extension = |
| registers.extensionRegistry.findLiteExtensionByNumber(defaultInstance, number); |
| if (extension == null) { |
| return decodeUnknownField( |
| tag, data, position, limit, getMutableUnknownFields(message), registers); |
| } else { |
| ((GeneratedMessageLite.ExtendableMessage<?, ?>) message).ensureExtensionsAreMutable(); |
| return decodeExtension( |
| tag, data, position, limit, (GeneratedMessageLite.ExtendableMessage) message, |
| extension, unknownFieldSchema, registers); |
| } |
| } |
| |
| static int decodeExtension( |
| int tag, |
| byte[] data, |
| int position, |
| int limit, |
| GeneratedMessageLite.ExtendableMessage<?, ?> message, |
| GeneratedMessageLite.GeneratedExtension<?, ?> extension, |
| UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite> unknownFieldSchema, |
| Registers registers) |
| throws IOException { |
| final FieldSet<GeneratedMessageLite.ExtensionDescriptor> extensions = message.extensions; |
| final int fieldNumber = tag >>> 3; |
| if (extension.descriptor.isRepeated() && extension.descriptor.isPacked()) { |
| switch (extension.getLiteType()) { |
| case DOUBLE: |
| { |
| DoubleArrayList list = new DoubleArrayList(); |
| position = decodePackedDoubleList(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case FLOAT: |
| { |
| FloatArrayList list = new FloatArrayList(); |
| position = decodePackedFloatList(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case INT64: |
| case UINT64: |
| { |
| LongArrayList list = new LongArrayList(); |
| position = decodePackedVarint64List(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case INT32: |
| case UINT32: |
| { |
| IntArrayList list = new IntArrayList(); |
| position = decodePackedVarint32List(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case FIXED64: |
| case SFIXED64: |
| { |
| LongArrayList list = new LongArrayList(); |
| position = decodePackedFixed64List(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case FIXED32: |
| case SFIXED32: |
| { |
| IntArrayList list = new IntArrayList(); |
| position = decodePackedFixed32List(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case BOOL: |
| { |
| BooleanArrayList list = new BooleanArrayList(); |
| position = decodePackedBoolList(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case SINT32: |
| { |
| IntArrayList list = new IntArrayList(); |
| position = decodePackedSInt32List(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case SINT64: |
| { |
| LongArrayList list = new LongArrayList(); |
| position = decodePackedSInt64List(data, position, list, registers); |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| case ENUM: |
| { |
| IntArrayList list = new IntArrayList(); |
| position = decodePackedVarint32List(data, position, list, registers); |
| UnknownFieldSetLite unknownFields = message.unknownFields; |
| if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { |
| unknownFields = null; |
| } |
| unknownFields = |
| SchemaUtil.filterUnknownEnumList( |
| fieldNumber, |
| list, |
| extension.descriptor.getEnumType(), |
| unknownFields, |
| unknownFieldSchema); |
| if (unknownFields != null) { |
| message.unknownFields = unknownFields; |
| } |
| extensions.setField(extension.descriptor, list); |
| break; |
| } |
| default: |
| throw new IllegalStateException( |
| "Type cannot be packed: " + extension.descriptor.getLiteType()); |
| } |
| } else { |
| Object value = null; |
| // Enum is a special case because unknown enum values will be put into UnknownFieldSetLite. |
| if (extension.getLiteType() == WireFormat.FieldType.ENUM) { |
| position = decodeVarint32(data, position, registers); |
| Object enumValue = extension.descriptor.getEnumType().findValueByNumber(registers.int1); |
| if (enumValue == null) { |
| UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; |
| if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { |
| unknownFields = UnknownFieldSetLite.newInstance(); |
| ((GeneratedMessageLite) message).unknownFields = unknownFields; |
| } |
| SchemaUtil.storeUnknownEnum( |
| fieldNumber, registers.int1, unknownFields, unknownFieldSchema); |
| return position; |
| } |
| // Note, we store the integer value instead of the actual enum object in FieldSet. |
| // This is also different from full-runtime where we store EnumValueDescriptor. |
| value = registers.int1; |
| } else { |
| switch (extension.getLiteType()) { |
| case DOUBLE: |
| value = decodeDouble(data, position); |
| position += 8; |
| break; |
| case FLOAT: |
| value = decodeFloat(data, position); |
| position += 4; |
| break; |
| case INT64: |
| case UINT64: |
| position = decodeVarint64(data, position, registers); |
| value = registers.long1; |
| break; |
| case INT32: |
| case UINT32: |
| position = decodeVarint32(data, position, registers); |
| value = registers.int1; |
| break; |
| case FIXED64: |
| case SFIXED64: |
| value = decodeFixed64(data, position); |
| position += 8; |
| break; |
| case FIXED32: |
| case SFIXED32: |
| value = decodeFixed32(data, position); |
| position += 4; |
| break; |
| case BOOL: |
| position = decodeVarint64(data, position, registers); |
| value = (registers.long1 != 0); |
| break; |
| case BYTES: |
| position = decodeBytes(data, position, registers); |
| value = registers.object1; |
| break; |
| case SINT32: |
| position = decodeVarint32(data, position, registers); |
| value = CodedInputStream.decodeZigZag32(registers.int1); |
| break; |
| case SINT64: |
| position = decodeVarint64(data, position, registers); |
| value = CodedInputStream.decodeZigZag64(registers.long1); |
| break; |
| case STRING: |
| position = decodeString(data, position, registers); |
| value = registers.object1; |
| break; |
| case GROUP: |
| final int endTag = (fieldNumber << 3) | WireFormat.WIRETYPE_END_GROUP; |
| position = decodeGroupField( |
| Protobuf.getInstance().schemaFor(extension.getMessageDefaultInstance().getClass()), |
| data, position, limit, endTag, registers); |
| value = registers.object1; |
| break; |
| |
| case MESSAGE: |
| position = decodeMessageField( |
| Protobuf.getInstance().schemaFor(extension.getMessageDefaultInstance().getClass()), |
| data, position, limit, registers); |
| value = registers.object1; |
| break; |
| |
| case ENUM: |
| throw new IllegalStateException("Shouldn't reach here."); |
| } |
| } |
| if (extension.isRepeated()) { |
| extensions.addRepeatedField(extension.descriptor, value); |
| } else { |
| switch (extension.getLiteType()) { |
| case MESSAGE: |
| case GROUP: |
| Object oldValue = extensions.getField(extension.descriptor); |
| if (oldValue != null) { |
| value = Internal.mergeMessage(oldValue, value); |
| } |
| break; |
| default: |
| break; |
| } |
| extensions.setField(extension.descriptor, value); |
| } |
| } |
| return position; |
| } |
| |
| /** Decodes an unknown field. */ |
| static int decodeUnknownField( |
| int tag, |
| byte[] data, |
| int position, |
| int limit, |
| UnknownFieldSetLite unknownFields, |
| Registers registers) |
| throws InvalidProtocolBufferException { |
| if (WireFormat.getTagFieldNumber(tag) == 0) { |
| throw InvalidProtocolBufferException.invalidTag(); |
| } |
| switch (WireFormat.getTagWireType(tag)) { |
| case WireFormat.WIRETYPE_VARINT: |
| position = decodeVarint64(data, position, registers); |
| unknownFields.storeField(tag, registers.long1); |
| return position; |
| case WireFormat.WIRETYPE_FIXED32: |
| unknownFields.storeField(tag, decodeFixed32(data, position)); |
| return position + 4; |
| case WireFormat.WIRETYPE_FIXED64: |
| unknownFields.storeField(tag, decodeFixed64(data, position)); |
| return position + 8; |
| case WireFormat.WIRETYPE_LENGTH_DELIMITED: |
| position = decodeVarint32(data, position, registers); |
| final int length = registers.int1; |
| if (length < 0) { |
| throw InvalidProtocolBufferException.negativeSize(); |
| } else if (length > data.length - position) { |
| throw InvalidProtocolBufferException.truncatedMessage(); |
| } else if (length == 0) { |
| unknownFields.storeField(tag, ByteString.EMPTY); |
| } else { |
| unknownFields.storeField(tag, ByteString.copyFrom(data, position, length)); |
| } |
| return position + length; |
| case WireFormat.WIRETYPE_START_GROUP: |
| final UnknownFieldSetLite child = UnknownFieldSetLite.newInstance(); |
| final int endGroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP; |
| int lastTag = 0; |
| while (position < limit) { |
| position = decodeVarint32(data, position, registers); |
| lastTag = registers.int1; |
| if (lastTag == endGroup) { |
| break; |
| } |
| position = decodeUnknownField(lastTag, data, position, limit, child, registers); |
| } |
| if (position > limit || lastTag != endGroup) { |
| throw InvalidProtocolBufferException.parseFailure(); |
| } |
| unknownFields.storeField(tag, child); |
| return position; |
| default: |
| throw InvalidProtocolBufferException.invalidTag(); |
| } |
| } |
| |
| /** Skips an unknown field. */ |
| static int skipField(int tag, byte[] data, int position, int limit, Registers registers) |
| throws InvalidProtocolBufferException { |
| if (WireFormat.getTagFieldNumber(tag) == 0) { |
| throw InvalidProtocolBufferException.invalidTag(); |
| } |
| switch (WireFormat.getTagWireType(tag)) { |
| case WireFormat.WIRETYPE_VARINT: |
| position = decodeVarint64(data, position, registers); |
| return position; |
| case WireFormat.WIRETYPE_FIXED32: |
| return position + 4; |
| case WireFormat.WIRETYPE_FIXED64: |
| return position + 8; |
| case WireFormat.WIRETYPE_LENGTH_DELIMITED: |
| position = decodeVarint32(data, position, registers); |
| return position + registers.int1; |
| case WireFormat.WIRETYPE_START_GROUP: |
| final int endGroup = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP; |
| int lastTag = 0; |
| while (position < limit) { |
| position = decodeVarint32(data, position, registers); |
| lastTag = registers.int1; |
| if (lastTag == endGroup) { |
| break; |
| } |
| position = skipField(lastTag, data, position, limit, registers); |
| } |
| if (position > limit || lastTag != endGroup) { |
| throw InvalidProtocolBufferException.parseFailure(); |
| } |
| return position; |
| default: |
| throw InvalidProtocolBufferException.invalidTag(); |
| } |
| } |
| } |