content: /* Copyright (C) 2016-2018 Alibaba Group Holding Limited
  
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
      http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  ==============================================================================*/
  
  // Protocol Buffers - Google's data interchange format
  // Copyright 2013 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.nano;
  
  import java.io.IOException;
  import java.lang.reflect.Array;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Represents an extension.
   *
   * @author bduff@google.com (Brian Duff)
   * @author maxtroy@google.com (Max Cai)
   * @param <M> the type of the extendable message this extension is for.
   * @param <T> the Java type of the extension; see {@link #clazz}.
   */
  public class Extension<M extends ExtendableMessageNano<M>, T> {
  
      /*
       * Because we typically only define message-typed extensions, the Extension class hierarchy is
       * designed as follows, to allow a big amount of code in this file to be removed by ProGuard:
       *
       *            Extension          // ready to use for message/group typed extensions
       *                Δ
       *                |
       *       PrimitiveExtension      // for primitive/enum typed extensions
       */
  
      public static final int TYPE_DOUBLE   = InternalNano.TYPE_DOUBLE;
      public static final int TYPE_FLOAT    = InternalNano.TYPE_FLOAT;
      public static final int TYPE_INT64    = InternalNano.TYPE_INT64;
      public static final int TYPE_UINT64   = InternalNano.TYPE_UINT64;
      public static final int TYPE_INT32    = InternalNano.TYPE_INT32;
      public static final int TYPE_FIXED64  = InternalNano.TYPE_FIXED64;
      public static final int TYPE_FIXED32  = InternalNano.TYPE_FIXED32;
      public static final int TYPE_BOOL     = InternalNano.TYPE_BOOL;
      public static final int TYPE_STRING   = InternalNano.TYPE_STRING;
      public static final int TYPE_GROUP    = InternalNano.TYPE_GROUP;
      public static final int TYPE_MESSAGE  = InternalNano.TYPE_MESSAGE;
      public static final int TYPE_BYTES    = InternalNano.TYPE_BYTES;
      public static final int TYPE_UINT32   = InternalNano.TYPE_UINT32;
      public static final int TYPE_ENUM     = InternalNano.TYPE_ENUM;
      public static final int TYPE_SFIXED32 = InternalNano.TYPE_SFIXED32;
      public static final int TYPE_SFIXED64 = InternalNano.TYPE_SFIXED64;
      public static final int TYPE_SINT32   = InternalNano.TYPE_SINT32;
      public static final int TYPE_SINT64   = InternalNano.TYPE_SINT64;
  
      /**
       * Creates an {@code Extension} of the given message type and tag number.
       * Should be used by the generated code only.
       *
       * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
       * @deprecated use {@link #createMessageTyped(int, Class, long)} instead.
       */
      @Deprecated
      public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
              Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
          return new Extension<M, T>(type, clazz, tag, false);
      }
  
      // Note: these create...() methods take a long for the tag parameter,
      // because tags are represented as unsigned ints, and these values exist
      // in generated code as long values. However, they can fit in 32-bits, so
      // it's safe to cast them to int without loss of precision.
  
      /**
       * Creates an {@code Extension} of the given message type and tag number.
       * Should be used by the generated code only.
       *
       * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
       */
      public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
              Extension<M, T> createMessageTyped(int type, Class<T> clazz, long tag) {
          return new Extension<M, T>(type, clazz, (int) tag, false);
      }
  
      /**
       * Creates a repeated {@code Extension} of the given message type and tag number.
       * Should be used by the generated code only.
       *
       * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
       */
      public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
              Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, long tag) {
          return new Extension<M, T[]>(type, clazz, (int) tag, true);
      }
  
      /**
       * Creates an {@code Extension} of the given primitive type and tag number.
       * Should be used by the generated code only.
       *
       * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP}
       * @param clazz the boxed Java type of this extension
       */
      public static <M extends ExtendableMessageNano<M>, T>
              Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, long tag) {
          return new PrimitiveExtension<M, T>(type, clazz, (int) tag, false, 0, 0);
      }
  
      /**
       * Creates a repeated {@code Extension} of the given primitive type and tag number.
       * Should be used by the generated code only.
       *
       * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP}
       * @param clazz the Java array type of this extension, with an unboxed component type
       */
      public static <M extends ExtendableMessageNano<M>, T>
              Extension<M, T> createRepeatedPrimitiveTyped(
                      int type, Class<T> clazz, long tag, long nonPackedTag, long packedTag) {
          return new PrimitiveExtension<M, T>(type, clazz, (int) tag, true,
              (int) nonPackedTag, (int) packedTag);
      }
  
      /**
       * Protocol Buffer type of this extension; one of the {@code TYPE_} constants.
       */
      protected final int type;
  
      /**
       * Java type of this extension. For a singular extension, this is the boxed Java type for the
       * Protocol Buffer {@link #type}; for a repeated extension, this is an array type whose
       * component type is the unboxed Java type for {@link #type}. For example, for a singular
       * {@code int32}/{@link #TYPE_INT32} extension, this equals {@code Integer.class}; for a
       * repeated {@code int32} extension, this equals {@code int[].class}.
       */
      protected final Class<T> clazz;
  
      /**
       * Tag number of this extension. The data should be viewed as an unsigned 32-bit value.
       */
      public final int tag;
  
      /**
       * Whether this extension is repeated.
       */
      protected final boolean repeated;
  
      private Extension(int type, Class<T> clazz, int tag, boolean repeated) {
          this.type = type;
          this.clazz = clazz;
          this.tag = tag;
          this.repeated = repeated;
      }
  
      /**
       * Returns the value of this extension stored in the given list of unknown fields, or
       * {@code null} if no unknown fields matches this extension.
       *
       * @param unknownFields a list of {@link UnknownFieldData}. All of the elements must have a tag
       *                      that matches this Extension's tag.
       *
       */
      final T getValueFrom(List<UnknownFieldData> unknownFields) {
          if (unknownFields == null) {
              return null;
          }
          return repeated ? getRepeatedValueFrom(unknownFields) : getSingularValueFrom(unknownFields);
      }
  
      private T getRepeatedValueFrom(List<UnknownFieldData> unknownFields) {
          // For repeated extensions, read all matching unknown fields in their original order.
          List<Object> resultList = new ArrayList<Object>();
          for (int i = 0; i < unknownFields.size(); i++) {
              UnknownFieldData data = unknownFields.get(i);
              if (data.bytes.length != 0) {
                  readDataInto(data, resultList);
              }
          }
  
          int resultSize = resultList.size();
          if (resultSize == 0) {
              return null;
          } else {
              T result = clazz.cast(Array.newInstance(clazz.getComponentType(), resultSize));
              for (int i = 0; i < resultSize; i++) {
                  Array.set(result, i, resultList.get(i));
              }
              return result;
          }
      }
  
      private T getSingularValueFrom(List<UnknownFieldData> unknownFields) {
          // For singular extensions, get the last piece of data stored under this extension.
          if (unknownFields.isEmpty()) {
              return null;
          }
          UnknownFieldData lastData = unknownFields.get(unknownFields.size() - 1);
          return clazz.cast(readData(CodedInputByteBufferNano.newInstance(lastData.bytes)));
      }
  
      protected Object readData(CodedInputByteBufferNano input) {
          // This implementation is for message/group extensions.
          Class<?> messageType = repeated ? clazz.getComponentType() : clazz;
          try {
              switch (type) {
                  case TYPE_GROUP:
                      MessageNano group = (MessageNano) messageType.newInstance();
                      input.readGroup(group, WireFormatNano.getTagFieldNumber(tag));
                      return group;
                  case TYPE_MESSAGE:
                      MessageNano message = (MessageNano) messageType.newInstance();
                      input.readMessage(message);
                      return message;
                  default:
                      throw new IllegalArgumentException("Unknown type " + type);
              }
          } catch (InstantiationException e) {
              throw new IllegalArgumentException(
                      "Error creating instance of class " + messageType, e);
          } catch (IllegalAccessException e) {
              throw new IllegalArgumentException(
                      "Error creating instance of class " + messageType, e);
          } catch (IOException e) {
              throw new IllegalArgumentException("Error reading extension field", e);
          }
      }
  
      protected void readDataInto(UnknownFieldData data, List<Object> resultList) {
          // This implementation is for message/group extensions.
          resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes)));
      }
  
      void writeTo(Object value, CodedOutputByteBufferNano output) throws IOException {
          if (repeated) {
              writeRepeatedData(value, output);
          } else {
              writeSingularData(value, output);
          }
      }
  
      protected void writeSingularData(Object value, CodedOutputByteBufferNano out) {
          // This implementation is for message/group extensions.
          try {
              out.writeRawVarint32(tag);
              switch (type) {
                  case TYPE_GROUP:
                      MessageNano groupValue = (MessageNano) value;
                      int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
                      out.writeGroupNoTag(groupValue);
                      // The endgroup tag must be included in the data payload.
                      out.writeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP);
                      break;
                  case TYPE_MESSAGE:
                      MessageNano messageValue = (MessageNano) value;
                      out.writeMessageNoTag(messageValue);
                      break;
                  default:
                      throw new IllegalArgumentException("Unknown type " + type);
              }
          } catch (IOException e) {
              // Should not happen
              throw new IllegalStateException(e);
          }
      }
  
      protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) {
          // This implementation is for non-packed extensions.
          int arrayLength = Array.getLength(array);
          for (int i = 0; i < arrayLength; i++) {
              Object element = Array.get(array, i);
              if (element != null) {
                  writeSingularData(element, output);
              }
          }
      }
  
      int computeSerializedSize(Object value) {
          if (repeated) {
              return computeRepeatedSerializedSize(value);
          } else {
              return computeSingularSerializedSize(value);
          }
      }
  
      protected int computeRepeatedSerializedSize(Object array) {
          // This implementation is for non-packed extensions.
          int size = 0;
          int arrayLength = Array.getLength(array);
          for (int i = 0; i < arrayLength; i++) {
              Object element = Array.get(array, i);
              if (element != null) {
                  size += computeSingularSerializedSize(Array.get(array, i));
              }
          }
          return size;
      }
  
      protected int computeSingularSerializedSize(Object value) {
          // This implementation is for message/group extensions.
          int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
          switch (type) {
              case TYPE_GROUP:
                  MessageNano groupValue = (MessageNano) value;
                  return CodedOutputByteBufferNano.computeGroupSize(fieldNumber, groupValue);
              case TYPE_MESSAGE:
                  MessageNano messageValue = (MessageNano) value;
                  return CodedOutputByteBufferNano.computeMessageSize(fieldNumber, messageValue);
              default:
                  throw new IllegalArgumentException("Unknown type " + type);
          }
      }
  
      /**
       * Represents an extension of a primitive (including enum) type. If there is no primitive
       * extensions, this subclass will be removable by ProGuard.
       */
      private static class PrimitiveExtension<M extends ExtendableMessageNano<M>, T>
              extends Extension<M, T> {
  
          /**
           * Tag of a piece of non-packed data from the wire compatible with this extension.
           */
          private final int nonPackedTag;
  
          /**
           * Tag of a piece of packed data from the wire compatible with this extension.
           * 0 if the type of this extension is not packable.
           */
          private final int packedTag;
  
          public PrimitiveExtension(int type, Class<T> clazz, int tag, boolean repeated,
                  int nonPackedTag, int packedTag) {
              super(type, clazz, tag, repeated);
              this.nonPackedTag = nonPackedTag;
              this.packedTag = packedTag;
          }
  
          @Override
          protected Object readData(CodedInputByteBufferNano input) {
              try {
                return input.readPrimitiveField(type);
              } catch (IOException e) {
                  throw new IllegalArgumentException("Error reading extension field", e);
              }
          }
  
          @Override
          protected void readDataInto(UnknownFieldData data, List<Object> resultList) {
              // This implementation is for primitive typed extensions,
              // which can read both packed and non-packed data.
              if (data.tag == nonPackedTag) {
                  resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes)));
              } else {
                  CodedInputByteBufferNano buffer =
                          CodedInputByteBufferNano.newInstance(data.bytes);
                  try {
                      buffer.pushLimit(buffer.readRawVarint32()); // length limit
                  } catch (IOException e) {
                      throw new IllegalArgumentException("Error reading extension field", e);
                  }
                  while (!buffer.isAtEnd()) {
                      resultList.add(readData(buffer));
                  }
              }
          }
  
          @Override
          protected final void writeSingularData(Object value, CodedOutputByteBufferNano output) {
              try {
                  output.writeRawVarint32(tag);
                  switch (type) {
                      case TYPE_DOUBLE:
                          Double doubleValue = (Double) value;
                          output.writeDoubleNoTag(doubleValue);
                          break;
                      case TYPE_FLOAT:
                          Float floatValue = (Float) value;
                          output.writeFloatNoTag(floatValue);
                          break;
                      case TYPE_INT64:
                          Long int64Value = (Long) value;
                          output.writeInt64NoTag(int64Value);
                          break;
                      case TYPE_UINT64:
                          Long uint64Value = (Long) value;
                          output.writeUInt64NoTag(uint64Value);
                          break;
                      case TYPE_INT32:
                          Integer int32Value = (Integer) value;
                          output.writeInt32NoTag(int32Value);
                          break;
                      case TYPE_FIXED64:
                          Long fixed64Value = (Long) value;
                          output.writeFixed64NoTag(fixed64Value);
                          break;
                      case TYPE_FIXED32:
                          Integer fixed32Value = (Integer) value;
                          output.writeFixed32NoTag(fixed32Value);
                          break;
                      case TYPE_BOOL:
                          Boolean boolValue = (Boolean) value;
                          output.writeBoolNoTag(boolValue);
                          break;
                      case TYPE_STRING:
                          String stringValue = (String) value;
                          output.writeStringNoTag(stringValue);
                          break;
                      case TYPE_BYTES:
                          byte[] bytesValue = (byte[]) value;
                          output.writeBytesNoTag(bytesValue);
                          break;
                      case TYPE_UINT32:
                          Integer uint32Value = (Integer) value;
                          output.writeUInt32NoTag(uint32Value);
                          break;
                      case TYPE_ENUM:
                          Integer enumValue = (Integer) value;
                          output.writeEnumNoTag(enumValue);
                          break;
                      case TYPE_SFIXED32:
                          Integer sfixed32Value = (Integer) value;
                          output.writeSFixed32NoTag(sfixed32Value);
                          break;
                      case TYPE_SFIXED64:
                          Long sfixed64Value = (Long) value;
                          output.writeSFixed64NoTag(sfixed64Value);
                          break;
                      case TYPE_SINT32:
                          Integer sint32Value = (Integer) value;
                          output.writeSInt32NoTag(sint32Value);
                          break;
                      case TYPE_SINT64:
                          Long sint64Value = (Long) value;
                          output.writeSInt64NoTag(sint64Value);
                          break;
                      default:
                          throw new IllegalArgumentException("Unknown type " + type);
                  }
              } catch (IOException e) {
                  // Should not happen
                  throw new IllegalStateException(e);
              }
          }
  
          @Override
          protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) {
              if (tag == nonPackedTag) {
                  // Use base implementation for non-packed data
                  super.writeRepeatedData(array, output);
              } else if (tag == packedTag) {
                  // Packed. Note that the array element type is guaranteed to be primitive, so there
                  // won't be any null elements, so no null check in this block.
                  int arrayLength = Array.getLength(array);
                  int dataSize = computePackedDataSize(array);
  
                  try {
                      output.writeRawVarint32(tag);
                      output.writeRawVarint32(dataSize);
                      switch (type) {
                          case TYPE_BOOL:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeBoolNoTag(Array.getBoolean(array, i));
                              }
                              break;
                          case TYPE_FIXED32:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeFixed32NoTag(Array.getInt(array, i));
                              }
                              break;
                          case TYPE_SFIXED32:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeSFixed32NoTag(Array.getInt(array, i));
                              }
                              break;
                          case TYPE_FLOAT:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeFloatNoTag(Array.getFloat(array, i));
                              }
                              break;
                          case TYPE_FIXED64:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeFixed64NoTag(Array.getLong(array, i));
                              }
                              break;
                          case TYPE_SFIXED64:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeSFixed64NoTag(Array.getLong(array, i));
                              }
                              break;
                          case TYPE_DOUBLE:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeDoubleNoTag(Array.getDouble(array, i));
                              }
                              break;
                          case TYPE_INT32:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeInt32NoTag(Array.getInt(array, i));
                              }
                              break;
                          case TYPE_SINT32:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeSInt32NoTag(Array.getInt(array, i));
                              }
                              break;
                          case TYPE_UINT32:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeUInt32NoTag(Array.getInt(array, i));
                              }
                              break;
                          case TYPE_INT64:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeInt64NoTag(Array.getLong(array, i));
                              }
                              break;
                          case TYPE_SINT64:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeSInt64NoTag(Array.getLong(array, i));
                              }
                              break;
                          case TYPE_UINT64:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeUInt64NoTag(Array.getLong(array, i));
                              }
                              break;
                          case TYPE_ENUM:
                              for (int i = 0; i < arrayLength; i++) {
                                  output.writeEnumNoTag(Array.getInt(array, i));
                              }
                              break;
                          default:
                              throw new IllegalArgumentException("Unpackable type " + type);
                      }
                  } catch (IOException e) {
                      // Should not happen.
                      throw new IllegalStateException(e);
                  }
              } else {
                  throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
                          + ", unequal to both non-packed variant " + nonPackedTag
                          + " and packed variant " + packedTag);
              }
          }
  
          private int computePackedDataSize(Object array) {
              int dataSize = 0;
              int arrayLength = Array.getLength(array);
              switch (type) {
                  case TYPE_BOOL:
                      // Bools are stored as int32 but just as 0 or 1, so 1 byte each.
                      dataSize = arrayLength;
                      break;
                  case TYPE_FIXED32:
                  case TYPE_SFIXED32:
                  case TYPE_FLOAT:
                      dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_32_SIZE;
                      break;
                  case TYPE_FIXED64:
                  case TYPE_SFIXED64:
                  case TYPE_DOUBLE:
                      dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_64_SIZE;
                      break;
                  case TYPE_INT32:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeInt32SizeNoTag(
                                  Array.getInt(array, i));
                      }
                      break;
                  case TYPE_SINT32:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeSInt32SizeNoTag(
                                  Array.getInt(array, i));
                      }
                      break;
                  case TYPE_UINT32:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeUInt32SizeNoTag(
                                  Array.getInt(array, i));
                      }
                      break;
                  case TYPE_INT64:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeInt64SizeNoTag(
                                  Array.getLong(array, i));
                      }
                      break;
                  case TYPE_SINT64:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeSInt64SizeNoTag(
                                  Array.getLong(array, i));
                      }
                      break;
                  case TYPE_UINT64:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeUInt64SizeNoTag(
                                  Array.getLong(array, i));
                      }
                      break;
                  case TYPE_ENUM:
                      for (int i = 0; i < arrayLength; i++) {
                          dataSize += CodedOutputByteBufferNano.computeEnumSizeNoTag(
                                  Array.getInt(array, i));
                      }
                      break;
                  default:
                      throw new IllegalArgumentException("Unexpected non-packable type " + type);
              }
              return dataSize;
          }
  
          @Override
          protected int computeRepeatedSerializedSize(Object array) {
              if (tag == nonPackedTag) {
                  // Use base implementation for non-packed data
                  return super.computeRepeatedSerializedSize(array);
              } else if (tag == packedTag) {
                  // Packed.
                  int dataSize = computePackedDataSize(array);
                  int payloadSize =
                          dataSize + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize);
                  return payloadSize + CodedOutputByteBufferNano.computeRawVarint32Size(tag);
              } else {
                  throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
                          + ", unequal to both non-packed variant " + nonPackedTag
                          + " and packed variant " + packedTag);
              }
          }
  
          @Override
          protected final int computeSingularSerializedSize(Object value) {
              int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
              switch (type) {
                  case TYPE_DOUBLE:
                      Double doubleValue = (Double) value;
                      return CodedOutputByteBufferNano.computeDoubleSize(fieldNumber, doubleValue);
                  case TYPE_FLOAT:
                      Float floatValue = (Float) value;
                      return CodedOutputByteBufferNano.computeFloatSize(fieldNumber, floatValue);
                  case TYPE_INT64:
                      Long int64Value = (Long) value;
                      return CodedOutputByteBufferNano.computeInt64Size(fieldNumber, int64Value);
                  case TYPE_UINT64:
                      Long uint64Value = (Long) value;
                      return CodedOutputByteBufferNano.computeUInt64Size(fieldNumber, uint64Value);
                  case TYPE_INT32:
                      Integer int32Value = (Integer) value;
                      return CodedOutputByteBufferNano.computeInt32Size(fieldNumber, int32Value);
                  case TYPE_FIXED64:
                      Long fixed64Value = (Long) value;
                      return CodedOutputByteBufferNano.computeFixed64Size(fieldNumber, fixed64Value);
                  case TYPE_FIXED32:
                      Integer fixed32Value = (Integer) value;
                      return CodedOutputByteBufferNano.computeFixed32Size(fieldNumber, fixed32Value);
                  case TYPE_BOOL:
                      Boolean boolValue = (Boolean) value;
                      return CodedOutputByteBufferNano.computeBoolSize(fieldNumber, boolValue);
                  case TYPE_STRING:
                      String stringValue = (String) value;
                      return CodedOutputByteBufferNano.computeStringSize(fieldNumber, stringValue);
                  case TYPE_BYTES:
                      byte[] bytesValue = (byte[]) value;
                      return CodedOutputByteBufferNano.computeBytesSize(fieldNumber, bytesValue);
                  case TYPE_UINT32:
                      Integer uint32Value = (Integer) value;
                      return CodedOutputByteBufferNano.computeUInt32Size(fieldNumber, uint32Value);
                  case TYPE_ENUM:
                      Integer enumValue = (Integer) value;
                      return CodedOutputByteBufferNano.computeEnumSize(fieldNumber, enumValue);
                  case TYPE_SFIXED32:
                      Integer sfixed32Value = (Integer) value;
                      return CodedOutputByteBufferNano.computeSFixed32Size(fieldNumber,
                              sfixed32Value);
                  case TYPE_SFIXED64:
                      Long sfixed64Value = (Long) value;
                      return CodedOutputByteBufferNano.computeSFixed64Size(fieldNumber,
                              sfixed64Value);
                  case TYPE_SINT32:
                      Integer sint32Value = (Integer) value;
                      return CodedOutputByteBufferNano.computeSInt32Size(fieldNumber, sint32Value);
                  case TYPE_SINT64:
                      Long sint64Value = (Long) value;
                      return CodedOutputByteBufferNano.computeSInt64Size(fieldNumber, sint64Value);
                  default:
                      throw new IllegalArgumentException("Unknown type " + type);
              }
          }
      }
  }
  