 
                        
        
                        
        com.google.protobuf.ArrayDecoders Maven / Gradle / Ivy
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
package com.google.protobuf;
import static com.google.protobuf.MessageSchema.getMutableUnknownFields;
import com.google.protobuf.GeneratedMessageLite.ExtensionDescriptor;
import com.google.protobuf.Internal.ProtobufList;
import java.io.IOException;
/**
 * Helper functions to decode protobuf wire format from a byte array.
 *
 * 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.
 */
@CheckReturnValue
final class ArrayDecoders {
  static final int DEFAULT_RECURSION_LIMIT = 100;
  @SuppressWarnings("NonFinalStaticField")
  private static volatile int recursionLimit = DEFAULT_RECURSION_LIMIT;
  private 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.
   */
  static final class Registers {
    public int int1;
    public long long1;
    public Object object1;
    public final ExtensionRegistryLite extensionRegistry;
    public int recursionDepth;
    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 {
    Object msg = schema.newInstance();
    int offset = mergeMessageField(msg, schema, data, position, limit, registers);
    schema.makeImmutable(msg);
    registers.object1 = msg;
    return offset;
  }
  /** Decodes a group value. */
  @SuppressWarnings({"unchecked", "rawtypes"})
  static int decodeGroupField(
      Schema schema, byte[] data, int position, int limit, int endGroup, Registers registers)
      throws IOException {
    Object msg = schema.newInstance();
    int offset = mergeGroupField(msg, schema, data, position, limit, endGroup, registers);
    schema.makeImmutable(msg);
    registers.object1 = msg;
    return offset;
  }
  @SuppressWarnings({"unchecked", "rawtypes"})
  static int mergeMessageField(
      Object msg, 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();
    }
    registers.recursionDepth++;
    checkRecursionLimit(registers.recursionDepth);
    schema.mergeFrom(msg, data, position, position + length, registers);
    registers.recursionDepth--;
    registers.object1 = msg;
    return position + length;
  }
  @SuppressWarnings({"unchecked", "rawtypes"})
  static int mergeGroupField(
      Object msg,
      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;
    registers.recursionDepth++;
    checkRecursionLimit(registers.recursionDepth);
    final int endPosition =
        messageSchema.parseMessage(msg, data, position, limit, endGroup, registers);
    registers.recursionDepth--;
    registers.object1 = msg;
    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. */
  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 output = (ProtobufList) 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 output = (ProtobufList) 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 output = (ProtobufList) 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