content: // 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}
       */
      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);
      }
  
      /**
       * 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, int tag) {
          return new Extension<M, T[]>(type, clazz, 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, int tag) {
          return new PrimitiveExtension<M, T>(type, clazz, 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, int tag, int nonPackedTag, int packedTag) {
          return new PrimitiveExtension<M, T>(type, clazz, tag, true, nonPackedTag, 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.
       */
      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);
              }
          }
      }
  }
  