Forráskód Böngészése

Merge "Extension overhaul."

Max Cai 11 éve
szülő
commit
3a2109246f

+ 0 - 31
java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java

@@ -168,14 +168,6 @@ public final class CodedOutputByteBufferNano {
     writeBytesNoTag(value);
     writeBytesNoTag(value);
   }
   }
 
 
-  /** Write a {@code byte} field, including tag, to the stream. */
-  public void writeByteArray(final int fieldNumber, final byte[] value)
-                         throws IOException {
-    writeTag(fieldNumber, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
-    writeByteArrayNoTag(value);
-  }
-
-
   /** Write a {@code uint32} field, including tag, to the stream. */
   /** Write a {@code uint32} field, including tag, to the stream. */
   public void writeUInt32(final int fieldNumber, final int value)
   public void writeUInt32(final int fieldNumber, final int value)
                           throws IOException {
                           throws IOException {
@@ -321,12 +313,6 @@ public final class CodedOutputByteBufferNano {
     writeRawBytes(value);
     writeRawBytes(value);
   }
   }
 
 
-  /** Write a {@code byte[]} field to the stream. */
-  public void writeByteArrayNoTag(final byte [] value) throws IOException {
-    writeRawVarint32(value.length);
-    writeRawBytes(value);
-  }
-
   /** Write a {@code uint32} field to the stream. */
   /** Write a {@code uint32} field to the stream. */
   public void writeUInt32NoTag(final int value) throws IOException {
   public void writeUInt32NoTag(final int value) throws IOException {
     writeRawVarint32(value);
     writeRawVarint32(value);
@@ -466,15 +452,6 @@ public final class CodedOutputByteBufferNano {
     return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value);
     return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value);
   }
   }
 
 
-  /**
-   * Compute the number of bytes that would be needed to encode a
-   * {@code byte[]} field, including tag.
-   */
-  public static int computeByteArraySize(final int fieldNumber,
-                                     final byte[] value) {
-    return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value);
-  }
-
   /**
   /**
    * Compute the number of bytes that would be needed to encode a
    * Compute the number of bytes that would be needed to encode a
    * {@code uint32} field, including tag.
    * {@code uint32} field, including tag.
@@ -660,14 +637,6 @@ public final class CodedOutputByteBufferNano {
     return computeRawVarint32Size(value.length) + value.length;
     return computeRawVarint32Size(value.length) + value.length;
   }
   }
 
 
-  /**
-   * Compute the number of bytes that would be needed to encode a
-   * {@code byte[]} field.
-   */
-  public static int computeByteArraySizeNoTag(final byte[] value) {
-    return computeRawVarint32Size(value.length) + value.length;
-  }
-
   /**
   /**
    * Compute the number of bytes that would be needed to encode a
    * Compute the number of bytes that would be needed to encode a
    * {@code uint32} field.
    * {@code uint32} field.

+ 56 - 8
java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java

@@ -30,6 +30,7 @@
 
 
 package com.google.protobuf.nano;
 package com.google.protobuf.nano;
 
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
@@ -37,34 +38,81 @@ import java.util.List;
  * Base class of those Protocol Buffer messages that need to store unknown fields,
  * Base class of those Protocol Buffer messages that need to store unknown fields,
  * such as extensions.
  * such as extensions.
  */
  */
-public abstract class ExtendableMessageNano extends MessageNano {
+public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
+        extends MessageNano {
     /**
     /**
      * A container for fields unknown to the message, including extensions. Extension fields can
      * A container for fields unknown to the message, including extensions. Extension fields can
-     * can be accessed through the {@link getExtension()} and {@link setExtension()} methods.
+     * can be accessed through the {@link #getExtension} and {@link #setExtension} methods.
      */
      */
     protected List<UnknownFieldData> unknownFieldData;
     protected List<UnknownFieldData> unknownFieldData;
 
 
     @Override
     @Override
     public int getSerializedSize() {
     public int getSerializedSize() {
-        int size = WireFormatNano.computeWireSize(unknownFieldData);
+        int size = 0;
+        int unknownFieldCount = unknownFieldData == null ? 0 : unknownFieldData.size();
+        for (int i = 0; i < unknownFieldCount; i++) {
+            UnknownFieldData unknownField = unknownFieldData.get(i);
+            size += CodedOutputByteBufferNano.computeRawVarint32Size(unknownField.tag);
+            size += unknownField.bytes.length;
+        }
         cachedSize = size;
         cachedSize = size;
         return size;
         return size;
     }
     }
 
 
+    @Override
+    public void writeTo(CodedOutputByteBufferNano output) throws IOException {
+        int unknownFieldCount = unknownFieldData == null ? 0 : unknownFieldData.size();
+        for (int i = 0; i < unknownFieldCount; i++) {
+            UnknownFieldData unknownField = unknownFieldData.get(i);
+            output.writeRawVarint32(unknownField.tag);
+            output.writeRawBytes(unknownField.bytes);
+        }
+    }
+
     /**
     /**
      * Gets the value stored in the specified extension of this message.
      * Gets the value stored in the specified extension of this message.
      */
      */
-    public <T> T getExtension(Extension<T> extension) {
-        return WireFormatNano.getExtension(extension, unknownFieldData);
+    public final <T> T getExtension(Extension<M, T> extension) {
+        return extension.getValueFrom(unknownFieldData);
     }
     }
 
 
     /**
     /**
      * Sets the value of the specified extension of this message.
      * Sets the value of the specified extension of this message.
      */
      */
-    public <T> void setExtension(Extension<T> extension, T value) {
+    public final <T> M setExtension(Extension<M, T> extension, T value) {
+        unknownFieldData = extension.setValueTo(value, unknownFieldData);
+
+        @SuppressWarnings("unchecked") // Generated code should guarantee type safety
+        M typedThis = (M) this;
+        return typedThis;
+    }
+
+    /**
+     * Stores the binary data of an unknown field.
+     *
+     * <p>Generated messages will call this for unknown fields if the store_unknown_fields
+     * option is on.
+     *
+     * <p>Note that the tag might be a end-group tag (rather than the start of an unknown field) in
+     * which case we do not want to add an unknown field entry.
+     *
+     * @param input the input buffer.
+     * @param tag the tag of the field.
+
+     * @return {@literal true} unless the tag is an end-group tag.
+     */
+    protected final boolean storeUnknownField(CodedInputByteBufferNano input, int tag)
+            throws IOException {
+        int startPos = input.getPosition();
+        if (!input.skipField(tag)) {
+            return false;  // This wasn't an unknown field, it's an end-group tag.
+        }
         if (unknownFieldData == null) {
         if (unknownFieldData == null) {
             unknownFieldData = new ArrayList<UnknownFieldData>();
             unknownFieldData = new ArrayList<UnknownFieldData>();
         }
         }
-        WireFormatNano.setExtension(extension, value, unknownFieldData);
+        int endPos = input.getPosition();
+        byte[] bytes = input.getData(startPos, endPos - startPos);
+        unknownFieldData.add(new UnknownFieldData(tag, bytes));
+        return true;
     }
     }
-}
+}

+ 637 - 63
java/src/main/java/com/google/protobuf/nano/Extension.java

@@ -30,85 +30,659 @@
 
 
 package com.google.protobuf.nano;
 package com.google.protobuf.nano;
 
 
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
  * Represents an extension.
  * Represents an extension.
  *
  *
  * @author bduff@google.com (Brian Duff)
  * @author bduff@google.com (Brian Duff)
- * @param <T> the type of the extension.
+ * @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<T> {
-  public final int fieldNumber;
-  public boolean isRepeatedField;
-  public Class<T> fieldType;
-  public Class<T> listType;
-
-  private Extension(int fieldNumber, TypeLiteral<T> type) {
-    this.fieldNumber = fieldNumber;
-    isRepeatedField = type.isList();
-    fieldType = type.getTargetClass();
-    listType = isRepeatedField ? type.getListType() : null;
-  }
-
-  /**
-   * Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and
-   * {@code type}.
-   */
-  public static <T> Extension<T> create(int fieldNumber, TypeLiteral<T> type) {
-    return new Extension<T>(fieldNumber, type);
-  }
-
-  /**
-   * Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and
-   * {@code type}. This version is used for repeated fields.
-   */
-  public static <T> Extension<List<T>> createRepeated(int fieldNumber, TypeLiteral<List<T>> type) {
-    return new Extension<List<T>>(fieldNumber, type);
-  }
-
-  /**
-   * Represents a generic type literal. We can't typesafely reference a
-   * Class&lt;List&lt;Foo>>.class in Java, so we use this instead.
-   * See: http://gafter.blogspot.com/2006/12/super-type-tokens.html
-   *
-   * <p>Somewhat specialized because we only ever have a Foo or a List&lt;Foo>.
-   */
-  public static abstract class TypeLiteral<T> {
-    private final Type type;
-
-    protected TypeLiteral() {
-      Type superclass = getClass().getGenericSuperclass();
-      if (superclass instanceof Class) {
-        throw new RuntimeException("Missing type parameter");
-      }
-      this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
+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   = 1;
+    public static final int TYPE_FLOAT    = 2;
+    public static final int TYPE_INT64    = 3;
+    public static final int TYPE_UINT64   = 4;
+    public static final int TYPE_INT32    = 5;
+    public static final int TYPE_FIXED64  = 6;
+    public static final int TYPE_FIXED32  = 7;
+    public static final int TYPE_BOOL     = 8;
+    public static final int TYPE_STRING   = 9;
+    public static final int TYPE_GROUP    = 10;
+    public static final int TYPE_MESSAGE  = 11;
+    public static final int TYPE_BYTES    = 12;
+    public static final int TYPE_UINT32   = 13;
+    public static final int TYPE_ENUM     = 14;
+    public static final int TYPE_SFIXED32 = 15;
+    public static final int TYPE_SFIXED64 = 16;
+    public static final int TYPE_SINT32   = 17;
+    public static final int TYPE_SINT64   = 18;
+
+    /**
+     * 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.
+     */
+    protected 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;
+    }
+
+    protected boolean isMatch(int unknownDataTag) {
+        // This implementation is for message/group extensions.
+        return unknownDataTag == tag;
+    }
+
+    /**
+     * Returns the value of this extension stored in the given list of unknown fields, or
+     * {@code null} if no unknown fields matches this extension.
+     */
+    final T getValueFrom(List<UnknownFieldData> unknownFields) {
+        if (unknownFields == null) {
+            return null;
+        }
+
+        if (repeated) {
+            // 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 (isMatch(data.tag) && data.bytes.length != 0) {
+                    readDataInto(data, resultList);
+                }
+            }
+
+            int resultSize = resultList.size();
+            if (resultSize == 0) {
+                return null;
+            }
+
+            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;
+        } else {
+            // For singular extensions, get the last piece of data stored under this extension.
+            UnknownFieldData lastData = null;
+            for (int i = unknownFields.size() - 1; lastData == null && i >= 0; i--) {
+                UnknownFieldData data = unknownFields.get(i);
+                if (isMatch(data.tag) && data.bytes.length != 0) {
+                    lastData = data;
+                }
+            }
+
+            if (lastData == null) {
+                return null;
+            }
+
+            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)));
     }
     }
 
 
     /**
     /**
-     * If the generic type is a list, returns {@code true}.
+     * Sets the value of this extension to the given list of unknown fields. This removes any
+     * previously stored data matching this extension.
+     *
+     * @param value The value of this extension, or {@code null} to clear this extension from the
+     *     unknown fields.
+     * @return The same {@code unknownFields} list, or a new list storing the extension value if
+     *     the argument was null.
      */
      */
-    private boolean isList() {
-      return type instanceof ParameterizedType;
+    final List<UnknownFieldData> setValueTo(T value, List<UnknownFieldData> unknownFields) {
+        if (unknownFields != null) {
+            // Delete all data matching this extension
+            for (int i = unknownFields.size() - 1; i >= 0; i--) {
+                if (isMatch(unknownFields.get(i).tag)) {
+                    unknownFields.remove(i);
+                }
+            }
+        }
+
+        if (value != null) {
+            if (unknownFields == null) {
+                unknownFields = new ArrayList<UnknownFieldData>();
+            }
+            if (repeated) {
+                writeDataInto(value, unknownFields);
+            } else {
+                unknownFields.add(writeData(value));
+            }
+        }
+
+        // After deletion or no-op addition (due to 'value' being an array of empty or
+        // null-only elements), unknownFields may be empty. Discard the ArrayList if so.
+        return (unknownFields.size() == 0) ? null : unknownFields;
     }
     }
 
 
-    @SuppressWarnings("unchecked")
-    private Class<T> getListType() {
-      return (Class<T>) ((ParameterizedType) type).getRawType();
+    protected UnknownFieldData writeData(Object value) {
+        // This implementation is for message/group extensions.
+        byte[] data;
+        try {
+            switch (type) {
+                case TYPE_GROUP:
+                    MessageNano groupValue = (MessageNano) value;
+                    int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
+                    data = new byte[CodedOutputByteBufferNano.computeGroupSizeNoTag(groupValue)
+                            + CodedOutputByteBufferNano.computeTagSize(fieldNumber)];
+                    CodedOutputByteBufferNano out = CodedOutputByteBufferNano.newInstance(data);
+                    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;
+                    data = new byte[
+                            CodedOutputByteBufferNano.computeMessageSizeNoTag(messageValue)];
+                    CodedOutputByteBufferNano.newInstance(data).writeMessageNoTag(messageValue);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown type " + type);
+            }
+        } catch (IOException e) {
+            // Should not happen
+            throw new IllegalStateException(e);
+        }
+        return new UnknownFieldData(tag, data);
+    }
+
+    protected void writeDataInto(T array, List<UnknownFieldData> unknownFields) {
+        // 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) {
+                unknownFields.add(writeData(element));
+            }
+        }
     }
     }
 
 
     /**
     /**
-     * If the generic type is a list, returns the type of element in the list. Otherwise,
-     * returns the actual type.
+     * Represents an extension of a primitive (including enum) type. If there is no primitive
+     * extensions, this subclass will be removable by ProGuard.
      */
      */
-    @SuppressWarnings("unchecked")
-    private Class<T> getTargetClass() {
-      if (isList()) {
-        return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
-      }
-      return (Class<T>) type;
+    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 boolean isMatch(int unknownDataTag) {
+            if (repeated) {
+                return unknownDataTag == nonPackedTag || unknownDataTag == packedTag;
+            } else {
+                return unknownDataTag == tag;
+            }
+        }
+
+        @Override
+        protected Object readData(CodedInputByteBufferNano input) {
+            try {
+                switch (type) {
+                    case TYPE_DOUBLE:
+                        return input.readDouble();
+                    case TYPE_FLOAT:
+                        return input.readFloat();
+                    case TYPE_INT64:
+                        return input.readInt64();
+                    case TYPE_UINT64:
+                        return input.readUInt64();
+                    case TYPE_INT32:
+                        return input.readInt32();
+                    case TYPE_FIXED64:
+                        return input.readFixed64();
+                    case TYPE_FIXED32:
+                        return input.readFixed32();
+                    case TYPE_BOOL:
+                        return input.readBool();
+                    case TYPE_STRING:
+                        return input.readString();
+                    case TYPE_BYTES:
+                        return input.readBytes();
+                    case TYPE_UINT32:
+                        return input.readUInt32();
+                    case TYPE_ENUM:
+                        return input.readEnum();
+                    case TYPE_SFIXED32:
+                        return input.readSFixed32();
+                    case TYPE_SFIXED64:
+                        return input.readSFixed64();
+                    case TYPE_SINT32:
+                        return input.readSInt32();
+                    case TYPE_SINT64:
+                        return input.readSInt64();
+                    default:
+                        throw new IllegalArgumentException("Unknown type " + 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 UnknownFieldData writeData(Object value) {
+            byte[] data;
+            try {
+                switch (type) {
+                    case TYPE_DOUBLE:
+                        Double doubleValue = (Double) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeDoubleSizeNoTag(doubleValue)];
+                        CodedOutputByteBufferNano.newInstance(data).writeDoubleNoTag(doubleValue);
+                        break;
+                    case TYPE_FLOAT:
+                        Float floatValue = (Float) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeFloatSizeNoTag(floatValue)];
+                        CodedOutputByteBufferNano.newInstance(data).writeFloatNoTag(floatValue);
+                        break;
+                    case TYPE_INT64:
+                        Long int64Value = (Long) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeInt64SizeNoTag(int64Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeInt64NoTag(int64Value);
+                        break;
+                    case TYPE_UINT64:
+                        Long uint64Value = (Long) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeUInt64SizeNoTag(uint64Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeUInt64NoTag(uint64Value);
+                        break;
+                    case TYPE_INT32:
+                        Integer int32Value = (Integer) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeInt32SizeNoTag(int32Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeInt32NoTag(int32Value);
+                        break;
+                    case TYPE_FIXED64:
+                        Long fixed64Value = (Long) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeFixed64SizeNoTag(fixed64Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeFixed64NoTag(fixed64Value);
+                        break;
+                    case TYPE_FIXED32:
+                        Integer fixed32Value = (Integer) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeFixed32SizeNoTag(fixed32Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeFixed32NoTag(fixed32Value);
+                        break;
+                    case TYPE_BOOL:
+                        Boolean boolValue = (Boolean) value;
+                        data = new byte[CodedOutputByteBufferNano.computeBoolSizeNoTag(boolValue)];
+                        CodedOutputByteBufferNano.newInstance(data).writeBoolNoTag(boolValue);
+                        break;
+                    case TYPE_STRING:
+                        String stringValue = (String) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeStringSizeNoTag(stringValue)];
+                        CodedOutputByteBufferNano.newInstance(data).writeStringNoTag(stringValue);
+                        break;
+                    case TYPE_BYTES:
+                        byte[] bytesValue = (byte[]) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeBytesSizeNoTag(bytesValue)];
+                        CodedOutputByteBufferNano.newInstance(data).writeBytesNoTag(bytesValue);
+                        break;
+                    case TYPE_UINT32:
+                        Integer uint32Value = (Integer) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeUInt32SizeNoTag(uint32Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeUInt32NoTag(uint32Value);
+                        break;
+                    case TYPE_ENUM:
+                        Integer enumValue = (Integer) value;
+                        data = new byte[CodedOutputByteBufferNano.computeEnumSizeNoTag(enumValue)];
+                        CodedOutputByteBufferNano.newInstance(data).writeEnumNoTag(enumValue);
+                        break;
+                    case TYPE_SFIXED32:
+                        Integer sfixed32Value = (Integer) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeSFixed32SizeNoTag(sfixed32Value)];
+                        CodedOutputByteBufferNano.newInstance(data)
+                                .writeSFixed32NoTag(sfixed32Value);
+                        break;
+                    case TYPE_SFIXED64:
+                        Long sfixed64Value = (Long) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeSFixed64SizeNoTag(sfixed64Value)];
+                        CodedOutputByteBufferNano.newInstance(data)
+                                .writeSFixed64NoTag(sfixed64Value);
+                        break;
+                    case TYPE_SINT32:
+                        Integer sint32Value = (Integer) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeSInt32SizeNoTag(sint32Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeSInt32NoTag(sint32Value);
+                        break;
+                    case TYPE_SINT64:
+                        Long sint64Value = (Long) value;
+                        data = new byte[
+                                CodedOutputByteBufferNano.computeSInt64SizeNoTag(sint64Value)];
+                        CodedOutputByteBufferNano.newInstance(data).writeSInt64NoTag(sint64Value);
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Unknown type " + type);
+                }
+            } catch (IOException e) {
+                // Should not happen
+                throw new IllegalStateException(e);
+            }
+            return new UnknownFieldData(tag, data);
+        }
+
+        @Override
+        protected void writeDataInto(T array, List<UnknownFieldData> unknownFields) {
+            if (tag == nonPackedTag) {
+                // Use base implementation for non-packed data
+                super.writeDataInto(array, unknownFields);
+            } 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. First get data size.
+                int arrayLength = Array.getLength(array);
+                int dataSize = 0;
+                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);
+                }
+
+                // Then construct payload.
+                int payloadSize =
+                        dataSize + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize);
+                byte[] data = new byte[payloadSize];
+                CodedOutputByteBufferNano output = CodedOutputByteBufferNano.newInstance(data);
+                try {
+                    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);
+                }
+                unknownFields.add(new UnknownFieldData(tag, data));
+            } else {
+                throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
+                        + ", unequal to both non-packed variant " + nonPackedTag
+                        + " and packed variant " + packedTag);
+            }
+        }
     }
     }
-  }
 }
 }

+ 9 - 6
java/src/main/java/com/google/protobuf/nano/MessageNano.java

@@ -67,16 +67,20 @@ public abstract class MessageNano {
     }
     }
 
 
     /**
     /**
-     * Serializes the message and writes it to {@code output}.  This does not
-     * flush or close the stream.
+     * Serializes the message and writes it to {@code output}.
+     *
+     * @param output the output to receive the serialized form.
+     * @throws IOException if an error occurred writing to {@code output}.
      */
      */
-    abstract public void writeTo(CodedOutputByteBufferNano output) throws java.io.IOException;
+    public void writeTo(CodedOutputByteBufferNano output) throws IOException {
+        // Does nothing by default. Overridden by subclasses which have data to write.
+    }
 
 
     /**
     /**
      * Parse {@code input} as a message of this type and merge it with the
      * Parse {@code input} as a message of this type and merge it with the
      * message being built.
      * message being built.
      */
      */
-    abstract public MessageNano mergeFrom(final CodedInputByteBufferNano input) throws IOException;
+    public abstract MessageNano mergeFrom(CodedInputByteBufferNano input) throws IOException;
 
 
     /**
     /**
      * Serialize to a byte array.
      * Serialize to a byte array.
@@ -95,9 +99,8 @@ public abstract class MessageNano {
      * write more than length bytes OutOfSpaceException will be thrown
      * write more than length bytes OutOfSpaceException will be thrown
      * and if length bytes are not written then IllegalStateException
      * and if length bytes are not written then IllegalStateException
      * is thrown.
      * is thrown.
-     * @return byte array with the serialized data.
      */
      */
-    public static final void toByteArray(MessageNano msg, byte [] data, int offset, int length) {
+    public static final void toByteArray(MessageNano msg, byte[] data, int offset, int length) {
         try {
         try {
             final CodedOutputByteBufferNano output =
             final CodedOutputByteBufferNano output =
                 CodedOutputByteBufferNano.newInstance(data, offset, length);
                 CodedOutputByteBufferNano.newInstance(data, offset, length);

+ 0 - 236
java/src/main/java/com/google/protobuf/nano/WireFormatNano.java

@@ -31,9 +31,6 @@
 package com.google.protobuf.nano;
 package com.google.protobuf.nano;
 
 
 import java.io.IOException;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
 
 
 /**
 /**
  * This class is used internally by the Protocol Buffer library and generated
  * This class is used internally by the Protocol Buffer library and generated
@@ -75,21 +72,6 @@ public final class WireFormatNano {
     return (fieldNumber << TAG_TYPE_BITS) | wireType;
     return (fieldNumber << TAG_TYPE_BITS) | wireType;
   }
   }
 
 
-  // Field numbers for feilds in MessageSet wire format.
-  static final int MESSAGE_SET_ITEM    = 1;
-  static final int MESSAGE_SET_TYPE_ID = 2;
-  static final int MESSAGE_SET_MESSAGE = 3;
-
-  // Tag numbers.
-  static final int MESSAGE_SET_ITEM_TAG =
-    makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP);
-  static final int MESSAGE_SET_ITEM_END_TAG =
-    makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP);
-  static final int MESSAGE_SET_TYPE_ID_TAG =
-    makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT);
-  static final int MESSAGE_SET_MESSAGE_TAG =
-    makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED);
-
   public static final int EMPTY_INT_ARRAY[] = {};
   public static final int EMPTY_INT_ARRAY[] = {};
   public static final long EMPTY_LONG_ARRAY[] = {};
   public static final long EMPTY_LONG_ARRAY[] = {};
   public static final float EMPTY_FLOAT_ARRAY[] = {};
   public static final float EMPTY_FLOAT_ARRAY[] = {};
@@ -113,35 +95,6 @@ public final class WireFormatNano {
     return input.skipField(tag);
     return input.skipField(tag);
   }
   }
 
 
-  /**
-   * Stores the binary data of an unknown field.
-   *
-   * <p>Generated messages will call this for unknown fields if the store_unknown_fields
-   * option is on.
-   *
-   * <p>Note that the tag might be a end-group tag (rather than the start of an unknown field) in
-   * which case we do not want to add an unknown field entry.
-   *
-   * @param data a Collection in which to store the data.
-   * @param input the input buffer.
-   * @param tag the tag of the field.
-
-   * @return {@literal true} unless the tag is an end-group tag.
-   */
-  public static boolean storeUnknownField(
-      final List<UnknownFieldData> data,
-      final CodedInputByteBufferNano input,
-      final int tag) throws IOException {
-    int startPos = input.getPosition();
-    if (!input.skipField(tag)) {
-      return false;  // This wasn't an unknown field, it's an end-group tag.
-    }
-    int endPos = input.getPosition();
-    byte[] bytes = input.getData(startPos, endPos - startPos);
-    data.add(new UnknownFieldData(tag, bytes));
-    return true;
-  }
-
   /**
   /**
    * Computes the array length of a repeated field. We assume that in the common case repeated
    * Computes the array length of a repeated field. We assume that in the common case repeated
    * fields are contiguously serialized but we still correctly handle interspersed values of a
    * fields are contiguously serialized but we still correctly handle interspersed values of a
@@ -172,193 +125,4 @@ public final class WireFormatNano {
     return arrayLength;
     return arrayLength;
   }
   }
 
 
-  /**
-   * Decodes the value of an extension.
-   */
-  public static <T> T getExtension(Extension<T> extension, List<UnknownFieldData> unknownFields) {
-    if (unknownFields == null) {
-      return null;
-    }
-    List<UnknownFieldData> dataForField = new ArrayList<UnknownFieldData>();
-    for (UnknownFieldData data : unknownFields) {
-      if (getTagFieldNumber(data.tag) == extension.fieldNumber) {
-        dataForField.add(data);
-      }
-    }
-    if (dataForField.isEmpty()) {
-      return null;
-    }
-
-    if (extension.isRepeatedField) {
-      List<Object> result = new ArrayList<Object>(dataForField.size());
-      for (UnknownFieldData data : dataForField) {
-        result.add(readData(extension.fieldType, data.bytes));
-      }
-      return extension.listType.cast(result);
-    }
-
-    // Normal fields. Note that the protobuf docs require us to handle multiple instances
-    // of the same field even for fields that are not repeated.
-    UnknownFieldData lastData = dataForField.get(dataForField.size() - 1);
-    return readData(extension.fieldType, lastData.bytes);
-  }
-
-  /**
-   * Reads (extension) data of the specified type from the specified byte array.
-   *
-   * @throws IllegalArgumentException if an error occurs while reading the data.
-   */
-  private static <T> T readData(Class<T> clazz, byte[] data) {
-    if (data.length == 0) {
-      return null;
-    }
-    CodedInputByteBufferNano buffer = CodedInputByteBufferNano.newInstance(data);
-    try {
-      if (clazz == String.class) {
-        return clazz.cast(buffer.readString());
-      } else if (clazz == Integer.class) {
-        return clazz.cast(buffer.readInt32());
-      } else if (clazz == Long.class) {
-        return clazz.cast(buffer.readInt64());
-      } else if (clazz == Boolean.class) {
-        return clazz.cast(buffer.readBool());
-      } else if (clazz == Float.class) {
-        return clazz.cast(buffer.readFloat());
-      } else if (clazz == Double.class) {
-        return clazz.cast(buffer.readDouble());
-      } else if (clazz == byte[].class) {
-        return clazz.cast(buffer.readBytes());
-      } else if (MessageNano.class.isAssignableFrom(clazz)) {
-        try {
-          MessageNano message = (MessageNano) clazz.newInstance();
-          buffer.readMessage(message);
-          return clazz.cast(message);
-        } catch (IllegalAccessException e) {
-          throw new IllegalArgumentException("Error creating instance of class " + clazz, e);
-        } catch (InstantiationException e) {
-          throw new IllegalArgumentException("Error creating instance of class " + clazz, e);
-        }
-      } else {
-        throw new IllegalArgumentException("Unhandled extension field type: " + clazz);
-      }
-    } catch (IOException e) {
-      throw new IllegalArgumentException("Error reading extension field", e);
-    }
-  }
-
-  public static <T> void setExtension(Extension<T> extension, T value,
-      List<UnknownFieldData> unknownFields) {
-    // First, remove all unknown fields with this tag.
-    for (Iterator<UnknownFieldData> i = unknownFields.iterator(); i.hasNext();) {
-      UnknownFieldData data = i.next();
-      if (extension.fieldNumber == getTagFieldNumber(data.tag)) {
-        i.remove();
-      }
-    }
-    if (value == null) {
-      return;
-    }
-    // Repeated field.
-    if (value instanceof List) {
-      for (Object item : (List<?>) value) {
-        unknownFields.add(write(extension.fieldNumber, item));
-      }
-    } else {
-      unknownFields.add(write(extension.fieldNumber, value));
-    }
-  }
-
-  /**
-   * Writes extension data and returns an {@link UnknownFieldData} containing
-   * bytes and a tag.
-   *
-   * @throws IllegalArgumentException if an error occurs while writing.
-   */
-  private static UnknownFieldData write(int fieldNumber, Object object) {
-    byte[] data;
-    int tag;
-    Class<?> clazz = object.getClass();
-    try {
-      if (clazz == String.class) {
-        String str = (String) object;
-        data = new byte[CodedOutputByteBufferNano.computeStringSizeNoTag(str)];
-        CodedOutputByteBufferNano.newInstance(data).writeStringNoTag(str);
-        tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
-      } else if (clazz == Integer.class) {
-        Integer integer = (Integer) object;
-        data = new byte[CodedOutputByteBufferNano.computeInt32SizeNoTag(integer)];
-        CodedOutputByteBufferNano.newInstance(data).writeInt32NoTag(integer);
-        tag = makeTag(fieldNumber, WIRETYPE_VARINT);
-      } else if (clazz == Long.class) {
-        Long longValue = (Long) object;
-        data = new byte[CodedOutputByteBufferNano.computeInt64SizeNoTag(longValue)];
-        CodedOutputByteBufferNano.newInstance(data).writeInt64NoTag(longValue);
-        tag = makeTag(fieldNumber, WIRETYPE_VARINT);
-      } else if (clazz == Boolean.class) {
-        Boolean boolValue = (Boolean) object;
-        data = new byte[CodedOutputByteBufferNano.computeBoolSizeNoTag(boolValue)];
-        CodedOutputByteBufferNano.newInstance(data).writeBoolNoTag(boolValue);
-        tag = makeTag(fieldNumber, WIRETYPE_VARINT);
-      } else if (clazz == Float.class) {
-        Float floatValue = (Float) object;
-        data = new byte[CodedOutputByteBufferNano.computeFloatSizeNoTag(floatValue)];
-        CodedOutputByteBufferNano.newInstance(data).writeFloatNoTag(floatValue);
-        tag = makeTag(fieldNumber, WIRETYPE_FIXED32);
-      } else if (clazz == Double.class) {
-        Double doubleValue = (Double) object;
-        data = new byte[CodedOutputByteBufferNano.computeDoubleSizeNoTag(doubleValue)];
-        CodedOutputByteBufferNano.newInstance(data).writeDoubleNoTag(doubleValue);
-        tag = makeTag(fieldNumber, WIRETYPE_FIXED64);
-      } else if (clazz == byte[].class) {
-        byte[] byteArrayValue = (byte[]) object;
-        data = new byte[CodedOutputByteBufferNano.computeByteArraySizeNoTag(byteArrayValue)];
-        CodedOutputByteBufferNano.newInstance(data).writeByteArrayNoTag(byteArrayValue);
-        tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
-      } else if (MessageNano.class.isAssignableFrom(clazz)) {
-        MessageNano messageValue = (MessageNano) object;
-
-        int messageSize = messageValue.getSerializedSize();
-        int delimiterSize = CodedOutputByteBufferNano.computeRawVarint32Size(messageSize);
-        data = new byte[messageSize + delimiterSize];
-        CodedOutputByteBufferNano buffer = CodedOutputByteBufferNano.newInstance(data);
-        buffer.writeRawVarint32(messageSize);
-        buffer.writeRawBytes(MessageNano.toByteArray(messageValue));
-        tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
-      } else {
-        throw new IllegalArgumentException("Unhandled extension field type: " + clazz);
-      }
-    } catch (IOException e) {
-      throw new IllegalArgumentException(e);
-    }
-    return new UnknownFieldData(tag, data);
-  }
-
-  /**
-   * Given a set of unknown field data, compute the wire size.
-   */
-  public static int computeWireSize(List<UnknownFieldData> unknownFields) {
-    if (unknownFields == null) {
-      return 0;
-    }
-    int size = 0;
-    for (UnknownFieldData unknownField : unknownFields) {
-      size += CodedOutputByteBufferNano.computeRawVarint32Size(unknownField.tag);
-      size += unknownField.bytes.length;
-    }
-    return size;
-  }
-
-  /**
-   * Write unknown fields.
-   */
-  public static void writeUnknownFields(List<UnknownFieldData> unknownFields,
-      CodedOutputByteBufferNano outBuffer) throws IOException {
-    if (unknownFields == null) {
-      return;
-    }
-    for (UnknownFieldData data : unknownFields) {
-      outBuffer.writeTag(getTagFieldNumber(data.tag), getTagWireType(data.tag));
-      outBuffer.writeRawBytes(data.bytes);
-    }
-  }
 }
 }

+ 164 - 63
java/src/test/java/com/google/protobuf/NanoTest.java

@@ -52,6 +52,9 @@ import com.google.protobuf.nano.NanoOuterClass;
 import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
 import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
 import com.google.protobuf.nano.NanoReferenceTypes;
 import com.google.protobuf.nano.NanoReferenceTypes;
 import com.google.protobuf.nano.NanoRepeatedPackables;
 import com.google.protobuf.nano.NanoRepeatedPackables;
+import com.google.protobuf.nano.PackedExtensions;
+import com.google.protobuf.nano.RepeatedExtensions;
+import com.google.protobuf.nano.SingularExtensions;
 import com.google.protobuf.nano.TestRepeatedMergeNano;
 import com.google.protobuf.nano.TestRepeatedMergeNano;
 import com.google.protobuf.nano.UnittestImportNano;
 import com.google.protobuf.nano.UnittestImportNano;
 import com.google.protobuf.nano.UnittestMultipleNano;
 import com.google.protobuf.nano.UnittestMultipleNano;
@@ -61,10 +64,8 @@ import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
 
 
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashMap;
-import java.util.List;
 
 
 /**
 /**
  * Test nano runtime.
  * Test nano runtime.
@@ -520,12 +521,12 @@ public class NanoTest extends TestCase {
     byte [] serialized = MessageNano.toByteArray(msg);
     byte [] serialized = MessageNano.toByteArray(msg);
 
 
     MessageWithGroup parsed = MessageWithGroup.parseFrom(serialized);
     MessageWithGroup parsed = MessageWithGroup.parseFrom(serialized);
-    assertTrue(msg.group != null);
-    assertEquals(1, msg.group.a);
+    assertEquals(1, parsed.group.a);
 
 
     byte [] serialized2 = MessageNano.toByteArray(parsed);
     byte [] serialized2 = MessageNano.toByteArray(parsed);
-    assertEquals(serialized2.length, serialized.length);
+    assertEquals(serialized.length, serialized2.length);
     MessageWithGroup parsed2 = MessageWithGroup.parseFrom(serialized2);
     MessageWithGroup parsed2 = MessageWithGroup.parseFrom(serialized2);
+    assertEquals(1, parsed2.group.a);
   }
   }
 
 
   public void testNanoOptionalNestedMessage() throws Exception {
   public void testNanoOptionalNestedMessage() throws Exception {
@@ -2222,6 +2223,7 @@ public class NanoTest extends TestCase {
    */
    */
   public void testNanoSingle() throws Exception {
   public void testNanoSingle() throws Exception {
     SingleMessageNano msg = new SingleMessageNano();
     SingleMessageNano msg = new SingleMessageNano();
+    assertNotNull(msg);
   }
   }
 
 
   /**
   /**
@@ -2597,12 +2599,14 @@ public class NanoTest extends TestCase {
     msg.defaultFloatNan = 0;
     msg.defaultFloatNan = 0;
     byte[] result = MessageNano.toByteArray(msg);
     byte[] result = MessageNano.toByteArray(msg);
     int msgSerializedSize = msg.getSerializedSize();
     int msgSerializedSize = msg.getSerializedSize();
+    assertTrue(result.length == msgSerializedSize);
     assertTrue(msgSerializedSize > 3);
     assertTrue(msgSerializedSize > 3);
 
 
     msg.defaultDoubleNan = Double.NaN;
     msg.defaultDoubleNan = Double.NaN;
     msg.defaultFloatNan = Float.NaN;
     msg.defaultFloatNan = Float.NaN;
     result = MessageNano.toByteArray(msg);
     result = MessageNano.toByteArray(msg);
     msgSerializedSize = msg.getSerializedSize();
     msgSerializedSize = msg.getSerializedSize();
+    assertEquals(3, result.length);
     assertEquals(3, msgSerializedSize);
     assertEquals(3, msgSerializedSize);
   }
   }
 
 
@@ -2735,57 +2739,163 @@ public class NanoTest extends TestCase {
   public void testExtensions() throws Exception {
   public void testExtensions() throws Exception {
     Extensions.ExtendableMessage message = new Extensions.ExtendableMessage();
     Extensions.ExtendableMessage message = new Extensions.ExtendableMessage();
     message.field = 5;
     message.field = 5;
-    message.setExtension(Extensions.someString, "Hello World!");
-    message.setExtension(Extensions.someBool, true);
-    message.setExtension(Extensions.someInt, 42);
-    message.setExtension(Extensions.someLong, 124234234234L);
-    message.setExtension(Extensions.someFloat, 42.0f);
-    message.setExtension(Extensions.someDouble, 422222.0);
-    message.setExtension(Extensions.someEnum, Extensions.FIRST_VALUE);
-    AnotherMessage another = new AnotherMessage();
-    another.string = "Foo";
-    another.value = true;
-    message.setExtension(Extensions.someMessage, another);
-
-    message.setExtension(Extensions.someRepeatedString, list("a", "bee", "seeya"));
-    message.setExtension(Extensions.someRepeatedBool, list(true, false, true));
-    message.setExtension(Extensions.someRepeatedInt, list(4, 8, 15, 16, 23, 42));
-    message.setExtension(Extensions.someRepeatedLong, list(4L, 8L, 15L, 16L, 23L, 42L));
-    message.setExtension(Extensions.someRepeatedFloat, list(1.0f, 3.0f));
-    message.setExtension(Extensions.someRepeatedDouble, list(55.133, 3.14159));
-    message.setExtension(Extensions.someRepeatedEnum, list(Extensions.FIRST_VALUE,
-        Extensions.SECOND_VALUE));
-    AnotherMessage second = new AnotherMessage();
-    second.string = "Whee";
-    second.value = false;
-    message.setExtension(Extensions.someRepeatedMessage, list(another, second));
+    int[] int32s = {1, 2};
+    int[] uint32s = {3, 4};
+    int[] sint32s = {-5, -6};
+    long[] int64s = {7, 8};
+    long[] uint64s = {9, 10};
+    long[] sint64s = {-11, -12};
+    int[] fixed32s = {13, 14};
+    int[] sfixed32s = {-15, -16};
+    long[] fixed64s = {17, 18};
+    long[] sfixed64s = {-19, -20};
+    boolean[] bools = {true, false};
+    float[] floats = {2.1f, 2.2f};
+    double[] doubles = {2.3, 2.4};
+    int[] enums = {Extensions.SECOND_VALUE, Extensions.FIRST_VALUE};
+    String[] strings = {"vijfentwintig", "twenty-six"};
+    byte[][] bytess = {{2, 7}, {2, 8}};
+    AnotherMessage another1 = new AnotherMessage();
+    another1.string = "er shi jiu";
+    another1.value = false;
+    AnotherMessage another2 = new AnotherMessage();
+    another2.string = "trente";
+    another2.value = true;
+    AnotherMessage[] messages = {another1, another2};
+    RepeatedExtensions.RepeatedGroup group1 = new RepeatedExtensions.RepeatedGroup();
+    group1.a = 31;
+    RepeatedExtensions.RepeatedGroup group2 = new RepeatedExtensions.RepeatedGroup();
+    group2.a = 32;
+    RepeatedExtensions.RepeatedGroup[] groups = {group1, group2};
+    message.setExtension(RepeatedExtensions.repeatedInt32, int32s);
+    message.setExtension(RepeatedExtensions.repeatedUint32, uint32s);
+    message.setExtension(RepeatedExtensions.repeatedSint32, sint32s);
+    message.setExtension(RepeatedExtensions.repeatedInt64, int64s);
+    message.setExtension(RepeatedExtensions.repeatedUint64, uint64s);
+    message.setExtension(RepeatedExtensions.repeatedSint64, sint64s);
+    message.setExtension(RepeatedExtensions.repeatedFixed32, fixed32s);
+    message.setExtension(RepeatedExtensions.repeatedSfixed32, sfixed32s);
+    message.setExtension(RepeatedExtensions.repeatedFixed64, fixed64s);
+    message.setExtension(RepeatedExtensions.repeatedSfixed64, sfixed64s);
+    message.setExtension(RepeatedExtensions.repeatedBool, bools);
+    message.setExtension(RepeatedExtensions.repeatedFloat, floats);
+    message.setExtension(RepeatedExtensions.repeatedDouble, doubles);
+    message.setExtension(RepeatedExtensions.repeatedEnum, enums);
+    message.setExtension(RepeatedExtensions.repeatedString, strings);
+    message.setExtension(RepeatedExtensions.repeatedBytes, bytess);
+    message.setExtension(RepeatedExtensions.repeatedMessage, messages);
+    message.setExtension(RepeatedExtensions.repeatedGroup, groups);
 
 
     byte[] data = MessageNano.toByteArray(message);
     byte[] data = MessageNano.toByteArray(message);
-
-    Extensions.ExtendableMessage deserialized = Extensions.ExtendableMessage.parseFrom(data);
-    assertEquals(5, deserialized.field);
-    assertEquals("Hello World!", deserialized.getExtension(Extensions.someString));
-    assertEquals(Boolean.TRUE, deserialized.getExtension(Extensions.someBool));
-    assertEquals(Integer.valueOf(42), deserialized.getExtension(Extensions.someInt));
-    assertEquals(Long.valueOf(124234234234L), deserialized.getExtension(Extensions.someLong));
-    assertEquals(Float.valueOf(42.0f), deserialized.getExtension(Extensions.someFloat));
-    assertEquals(Double.valueOf(422222.0), deserialized.getExtension(Extensions.someDouble));
-    assertEquals(Integer.valueOf(Extensions.FIRST_VALUE),
-        deserialized.getExtension(Extensions.someEnum));
-    assertEquals(another.string, deserialized.getExtension(Extensions.someMessage).string);
-    assertEquals(another.value, deserialized.getExtension(Extensions.someMessage).value);
-    assertEquals(list("a", "bee", "seeya"), deserialized.getExtension(Extensions.someRepeatedString));
-    assertEquals(list(true, false, true), deserialized.getExtension(Extensions.someRepeatedBool));
-    assertEquals(list(4, 8, 15, 16, 23, 42), deserialized.getExtension(Extensions.someRepeatedInt));
-    assertEquals(list(4L, 8L, 15L, 16L, 23L, 42L), deserialized.getExtension(Extensions.someRepeatedLong));
-    assertEquals(list(1.0f, 3.0f), deserialized.getExtension(Extensions.someRepeatedFloat));
-    assertEquals(list(55.133, 3.14159), deserialized.getExtension(Extensions.someRepeatedDouble));
-    assertEquals(list(Extensions.FIRST_VALUE,
-        Extensions.SECOND_VALUE), deserialized.getExtension(Extensions.someRepeatedEnum));
-    assertEquals("Foo", deserialized.getExtension(Extensions.someRepeatedMessage).get(0).string);
-    assertEquals(true, deserialized.getExtension(Extensions.someRepeatedMessage).get(0).value);
-    assertEquals("Whee", deserialized.getExtension(Extensions.someRepeatedMessage).get(1).string);
-    assertEquals(false, deserialized.getExtension(Extensions.someRepeatedMessage).get(1).value);
+    message = Extensions.ExtendableMessage.parseFrom(data);
+    assertEquals(5, message.field);
+
+    // Test reading back using SingularExtensions: the retrieved value should equal the last
+    // in each array.
+    assertEquals(int32s[1], (int) message.getExtension(SingularExtensions.someInt32));
+    assertEquals(uint32s[1], (int) message.getExtension(SingularExtensions.someUint32));
+    assertEquals(sint32s[1], (int) message.getExtension(SingularExtensions.someSint32));
+    assertEquals(int64s[1], (long) message.getExtension(SingularExtensions.someInt64));
+    assertEquals(uint64s[1], (long) message.getExtension(SingularExtensions.someUint64));
+    assertEquals(sint64s[1], (long) message.getExtension(SingularExtensions.someSint64));
+    assertEquals(fixed32s[1], (int) message.getExtension(SingularExtensions.someFixed32));
+    assertEquals(sfixed32s[1], (int) message.getExtension(SingularExtensions.someSfixed32));
+    assertEquals(fixed64s[1], (long) message.getExtension(SingularExtensions.someFixed64));
+    assertEquals(sfixed64s[1], (long) message.getExtension(SingularExtensions.someSfixed64));
+    assertEquals(bools[1], (boolean) message.getExtension(SingularExtensions.someBool));
+    assertEquals(floats[1], (float) message.getExtension(SingularExtensions.someFloat));
+    assertEquals(doubles[1], (double) message.getExtension(SingularExtensions.someDouble));
+    assertEquals(enums[1], (int) message.getExtension(SingularExtensions.someEnum));
+    assertEquals(strings[1], message.getExtension(SingularExtensions.someString));
+    assertTrue(Arrays.equals(bytess[1], message.getExtension(SingularExtensions.someBytes)));
+    AnotherMessage deserializedMessage = message.getExtension(SingularExtensions.someMessage);
+    assertEquals(another2.string, deserializedMessage.string);
+    assertEquals(another2.value, deserializedMessage.value);
+    assertEquals(group2.a, message.getExtension(SingularExtensions.someGroup).a);
+
+    // Test reading back using RepeatedExtensions: the arrays should be equal.
+    assertTrue(Arrays.equals(int32s, message.getExtension(RepeatedExtensions.repeatedInt32)));
+    assertTrue(Arrays.equals(uint32s, message.getExtension(RepeatedExtensions.repeatedUint32)));
+    assertTrue(Arrays.equals(sint32s, message.getExtension(RepeatedExtensions.repeatedSint32)));
+    assertTrue(Arrays.equals(int64s, message.getExtension(RepeatedExtensions.repeatedInt64)));
+    assertTrue(Arrays.equals(uint64s, message.getExtension(RepeatedExtensions.repeatedUint64)));
+    assertTrue(Arrays.equals(sint64s, message.getExtension(RepeatedExtensions.repeatedSint64)));
+    assertTrue(Arrays.equals(fixed32s, message.getExtension(RepeatedExtensions.repeatedFixed32)));
+    assertTrue(Arrays.equals(sfixed32s, message.getExtension(RepeatedExtensions.repeatedSfixed32)));
+    assertTrue(Arrays.equals(fixed64s, message.getExtension(RepeatedExtensions.repeatedFixed64)));
+    assertTrue(Arrays.equals(sfixed64s, message.getExtension(RepeatedExtensions.repeatedSfixed64)));
+    assertTrue(Arrays.equals(bools, message.getExtension(RepeatedExtensions.repeatedBool)));
+    assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat)));
+    assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble)));
+    assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum)));
+    assertTrue(Arrays.equals(strings, message.getExtension(RepeatedExtensions.repeatedString)));
+    byte[][] deserializedRepeatedBytes = message.getExtension(RepeatedExtensions.repeatedBytes);
+    assertEquals(2, deserializedRepeatedBytes.length);
+    assertTrue(Arrays.equals(bytess[0], deserializedRepeatedBytes[0]));
+    assertTrue(Arrays.equals(bytess[1], deserializedRepeatedBytes[1]));
+    AnotherMessage[] deserializedRepeatedMessage =
+        message.getExtension(RepeatedExtensions.repeatedMessage);
+    assertEquals(2, deserializedRepeatedMessage.length);
+    assertEquals(another1.string, deserializedRepeatedMessage[0].string);
+    assertEquals(another1.value, deserializedRepeatedMessage[0].value);
+    assertEquals(another2.string, deserializedRepeatedMessage[1].string);
+    assertEquals(another2.value, deserializedRepeatedMessage[1].value);
+    RepeatedExtensions.RepeatedGroup[] deserializedRepeatedGroup =
+        message.getExtension(RepeatedExtensions.repeatedGroup);
+    assertEquals(2, deserializedRepeatedGroup.length);
+    assertEquals(group1.a, deserializedRepeatedGroup[0].a);
+    assertEquals(group2.a, deserializedRepeatedGroup[1].a);
+
+    // Test reading back using PackedExtensions: the arrays should be equal, even the fields
+    // are non-packed.
+    assertTrue(Arrays.equals(int32s, message.getExtension(PackedExtensions.packedInt32)));
+    assertTrue(Arrays.equals(uint32s, message.getExtension(PackedExtensions.packedUint32)));
+    assertTrue(Arrays.equals(sint32s, message.getExtension(PackedExtensions.packedSint32)));
+    assertTrue(Arrays.equals(int64s, message.getExtension(PackedExtensions.packedInt64)));
+    assertTrue(Arrays.equals(uint64s, message.getExtension(PackedExtensions.packedUint64)));
+    assertTrue(Arrays.equals(sint64s, message.getExtension(PackedExtensions.packedSint64)));
+    assertTrue(Arrays.equals(fixed32s, message.getExtension(PackedExtensions.packedFixed32)));
+    assertTrue(Arrays.equals(sfixed32s, message.getExtension(PackedExtensions.packedSfixed32)));
+    assertTrue(Arrays.equals(fixed64s, message.getExtension(PackedExtensions.packedFixed64)));
+    assertTrue(Arrays.equals(sfixed64s, message.getExtension(PackedExtensions.packedSfixed64)));
+    assertTrue(Arrays.equals(bools, message.getExtension(PackedExtensions.packedBool)));
+    assertTrue(Arrays.equals(floats, message.getExtension(PackedExtensions.packedFloat)));
+    assertTrue(Arrays.equals(doubles, message.getExtension(PackedExtensions.packedDouble)));
+    assertTrue(Arrays.equals(enums, message.getExtension(PackedExtensions.packedEnum)));
+
+    // Now set the packable extension values using PackedExtensions so they're serialized packed.
+    message.setExtension(PackedExtensions.packedInt32, int32s);
+    message.setExtension(PackedExtensions.packedUint32, uint32s);
+    message.setExtension(PackedExtensions.packedSint32, sint32s);
+    message.setExtension(PackedExtensions.packedInt64, int64s);
+    message.setExtension(PackedExtensions.packedUint64, uint64s);
+    message.setExtension(PackedExtensions.packedSint64, sint64s);
+    message.setExtension(PackedExtensions.packedFixed32, fixed32s);
+    message.setExtension(PackedExtensions.packedSfixed32, sfixed32s);
+    message.setExtension(PackedExtensions.packedFixed64, fixed64s);
+    message.setExtension(PackedExtensions.packedSfixed64, sfixed64s);
+    message.setExtension(PackedExtensions.packedBool, bools);
+    message.setExtension(PackedExtensions.packedFloat, floats);
+    message.setExtension(PackedExtensions.packedDouble, doubles);
+    message.setExtension(PackedExtensions.packedEnum, enums);
+
+    // And read back using non-packed RepeatedExtensions.
+    byte[] data2 = MessageNano.toByteArray(message);
+    message = MessageNano.mergeFrom(new Extensions.ExtendableMessage(), data2);
+    assertTrue(Arrays.equals(int32s, message.getExtension(RepeatedExtensions.repeatedInt32)));
+    assertTrue(Arrays.equals(uint32s, message.getExtension(RepeatedExtensions.repeatedUint32)));
+    assertTrue(Arrays.equals(sint32s, message.getExtension(RepeatedExtensions.repeatedSint32)));
+    assertTrue(Arrays.equals(int64s, message.getExtension(RepeatedExtensions.repeatedInt64)));
+    assertTrue(Arrays.equals(uint64s, message.getExtension(RepeatedExtensions.repeatedUint64)));
+    assertTrue(Arrays.equals(sint64s, message.getExtension(RepeatedExtensions.repeatedSint64)));
+    assertTrue(Arrays.equals(fixed32s, message.getExtension(RepeatedExtensions.repeatedFixed32)));
+    assertTrue(Arrays.equals(sfixed32s, message.getExtension(RepeatedExtensions.repeatedSfixed32)));
+    assertTrue(Arrays.equals(fixed64s, message.getExtension(RepeatedExtensions.repeatedFixed64)));
+    assertTrue(Arrays.equals(sfixed64s, message.getExtension(RepeatedExtensions.repeatedSfixed64)));
+    assertTrue(Arrays.equals(bools, message.getExtension(RepeatedExtensions.repeatedBool)));
+    assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat)));
+    assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble)));
+    assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum)));
   }
   }
 
 
   public void testUnknownFields() throws Exception {
   public void testUnknownFields() throws Exception {
@@ -3462,13 +3572,4 @@ public class NanoTest extends TestCase {
     }
     }
     return sb.toString();
     return sb.toString();
   }
   }
-
-  private <T> List<T> list(T first, T... remaining) {
-    List<T> list = new ArrayList<T>();
-    list.add(first);
-    for (T item : remaining) {
-      list.add(item);
-    }
-    return list;
-  }
 }
 }

+ 82 - 31
src/google/protobuf/compiler/javanano/javanano_extension.cc

@@ -42,28 +42,84 @@ namespace compiler {
 namespace javanano {
 namespace javanano {
 
 
 using internal::WireFormat;
 using internal::WireFormat;
+using internal::WireFormatLite;
+
+namespace {
+
+const char* GetTypeConstantName(const FieldDescriptor::Type type) {
+  switch (type) {
+    case FieldDescriptor::TYPE_INT32   : return "TYPE_INT32"   ;
+    case FieldDescriptor::TYPE_UINT32  : return "TYPE_UINT32"  ;
+    case FieldDescriptor::TYPE_SINT32  : return "TYPE_SINT32"  ;
+    case FieldDescriptor::TYPE_FIXED32 : return "TYPE_FIXED32" ;
+    case FieldDescriptor::TYPE_SFIXED32: return "TYPE_SFIXED32";
+    case FieldDescriptor::TYPE_INT64   : return "TYPE_INT64"   ;
+    case FieldDescriptor::TYPE_UINT64  : return "TYPE_UINT64"  ;
+    case FieldDescriptor::TYPE_SINT64  : return "TYPE_SINT64"  ;
+    case FieldDescriptor::TYPE_FIXED64 : return "TYPE_FIXED64" ;
+    case FieldDescriptor::TYPE_SFIXED64: return "TYPE_SFIXED64";
+    case FieldDescriptor::TYPE_FLOAT   : return "TYPE_FLOAT"   ;
+    case FieldDescriptor::TYPE_DOUBLE  : return "TYPE_DOUBLE"  ;
+    case FieldDescriptor::TYPE_BOOL    : return "TYPE_BOOL"    ;
+    case FieldDescriptor::TYPE_STRING  : return "TYPE_STRING"  ;
+    case FieldDescriptor::TYPE_BYTES   : return "TYPE_BYTES"   ;
+    case FieldDescriptor::TYPE_ENUM    : return "TYPE_ENUM"    ;
+    case FieldDescriptor::TYPE_GROUP   : return "TYPE_GROUP"   ;
+    case FieldDescriptor::TYPE_MESSAGE : return "TYPE_MESSAGE" ;
+
+    // No default because we want the compiler to complain if any new
+    // types are added.
+  }
+
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return NULL;
+}
+
+}  // namespace
 
 
 void SetVariables(const FieldDescriptor* descriptor, const Params params,
 void SetVariables(const FieldDescriptor* descriptor, const Params params,
                   map<string, string>* variables) {
                   map<string, string>* variables) {
-  (*variables)["name"] = 
-    RenameJavaKeywords(UnderscoresToCamelCase(descriptor));
-  (*variables)["number"] = SimpleItoa(descriptor->number());
   (*variables)["extends"] = ClassName(params, descriptor->containing_type());
   (*variables)["extends"] = ClassName(params, descriptor->containing_type());
-
-  string type;
+  (*variables)["name"] = RenameJavaKeywords(UnderscoresToCamelCase(descriptor));
+  bool repeated = descriptor->is_repeated();
+  (*variables)["repeated"] = repeated ? "Repeated" : "";
+  (*variables)["type"] = GetTypeConstantName(descriptor->type());
   JavaType java_type = GetJavaType(descriptor->type());
   JavaType java_type = GetJavaType(descriptor->type());
-  switch (java_type) {
-    case JAVATYPE_ENUM:
-      type = "java.lang.Integer";
-      break;
-    case JAVATYPE_MESSAGE:
-      type = ClassName(params, descriptor->message_type());
-      break;
-    default:
-      type = BoxedPrimitiveTypeName(java_type);
-      break;
+  string tag = SimpleItoa(WireFormat::MakeTag(descriptor));
+  if (java_type == JAVATYPE_MESSAGE) {
+    (*variables)["ext_type"] = "MessageTyped";
+    string message_type = ClassName(params, descriptor->message_type());
+    if (repeated) {
+      message_type += "[]";
+    }
+    (*variables)["class"] = message_type;
+    // For message typed extensions, tags_params contains a single tag
+    // for both singular and repeated cases.
+    (*variables)["tag_params"] = tag;
+  } else {
+    (*variables)["ext_type"] = "PrimitiveTyped";
+    if (!repeated) {
+      (*variables)["class"] = BoxedPrimitiveTypeName(java_type);
+      (*variables)["tag_params"] = tag;
+    } else {
+      (*variables)["class"] = PrimitiveTypeName(java_type) + "[]";
+      if (!descriptor->is_packable()) {
+        // Non-packable: nonPackedTag == tag, packedTag == 0
+        (*variables)["tag_params"] = tag + ", " + tag + ", 0";
+      } else if (descriptor->options().packed()) {
+        // Packable and packed: tag == packedTag
+        string non_packed_tag = SimpleItoa(WireFormatLite::MakeTag(
+            descriptor->number(),
+            WireFormat::WireTypeForFieldType(descriptor->type())));
+        (*variables)["tag_params"] = tag + ", " + non_packed_tag + ", " + tag;
+      } else {
+        // Packable and not packed: tag == nonPackedTag
+        string packed_tag = SimpleItoa(WireFormatLite::MakeTag(
+            descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED));
+        (*variables)["tag_params"] = tag + ", " + tag + ", " + packed_tag;
+      }
+    }
   }
   }
-  (*variables)["type"] = type;
 }
 }
 
 
 ExtensionGenerator::
 ExtensionGenerator::
@@ -75,21 +131,16 @@ ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params)
 ExtensionGenerator::~ExtensionGenerator() {}
 ExtensionGenerator::~ExtensionGenerator() {}
 
 
 void ExtensionGenerator::Generate(io::Printer* printer) const {
 void ExtensionGenerator::Generate(io::Printer* printer) const {
-  if (descriptor_->is_repeated()) {
-    printer->Print(variables_,
-      "\n"
-      "// extends $extends$\n"
-      "public static final com.google.protobuf.nano.Extension<java.util.List<$type$>> $name$ = \n"
-      "    com.google.protobuf.nano.Extension.createRepeated($number$,\n"
-      "        new com.google.protobuf.nano.Extension.TypeLiteral<java.util.List<$type$>>(){});\n");
-  } else {
-    printer->Print(variables_,
-      "\n"
-      "// extends $extends$\n"
-      "public static final com.google.protobuf.nano.Extension<$type$> $name$ =\n"
-      "    com.google.protobuf.nano.Extension.create($number$,\n"
-      "        new com.google.protobuf.nano.Extension.TypeLiteral<$type$>(){});\n");
-  }
+  printer->Print("\n");
+  PrintFieldComment(printer, descriptor_);
+  printer->Print(variables_,
+    "public static final com.google.protobuf.nano.Extension<\n"
+    "    $extends$,\n"
+    "    $class$> $name$ =\n"
+    "        com.google.protobuf.nano.Extension.create$repeated$$ext_type$(\n"
+    "            com.google.protobuf.nano.Extension.$type$,\n"
+    "            $class$.class,\n"
+    "            $tag_params$);\n");
 }
 }
 
 
 }  // namespace javanano
 }  // namespace javanano

+ 37 - 1
src/google/protobuf/compiler/javanano/javanano_helpers.cc

@@ -264,6 +264,22 @@ string FieldDefaultConstantName(const FieldDescriptor *field) {
   return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default";
   return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default";
 }
 }
 
 
+void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
+  // We don't want to print group bodies so we cut off after the first line
+  // (the second line for extensions).
+  string def = field->DebugString();
+  string::size_type first_line_end = def.find_first_of('\n');
+  printer->Print("// $def$\n",
+    "def", def.substr(0, first_line_end));
+  if (field->is_extension()) {
+    string::size_type second_line_start = first_line_end + 1;
+    string::size_type second_line_length =
+        def.find('\n', second_line_start) - second_line_start;
+    printer->Print("// $def$\n",
+      "def", def.substr(second_line_start, second_line_length));
+  }
+}
+
 JavaType GetJavaType(FieldDescriptor::Type field_type) {
 JavaType GetJavaType(FieldDescriptor::Type field_type) {
   switch (field_type) {
   switch (field_type) {
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT32:
@@ -310,7 +326,27 @@ JavaType GetJavaType(FieldDescriptor::Type field_type) {
   return JAVATYPE_INT;
   return JAVATYPE_INT;
 }
 }
 
 
-const char* BoxedPrimitiveTypeName(JavaType type) {
+string PrimitiveTypeName(JavaType type) {
+  switch (type) {
+    case JAVATYPE_INT    : return "int";
+    case JAVATYPE_LONG   : return "long";
+    case JAVATYPE_FLOAT  : return "float";
+    case JAVATYPE_DOUBLE : return "double";
+    case JAVATYPE_BOOLEAN: return "boolean";
+    case JAVATYPE_STRING : return "java.lang.String";
+    case JAVATYPE_BYTES  : return "byte[]";
+    case JAVATYPE_ENUM   : return "int";
+    case JAVATYPE_MESSAGE: return NULL;
+
+    // No default because we want the compiler to complain if any new
+    // JavaTypes are added.
+  }
+
+  GOOGLE_LOG(FATAL) << "Can't get here.";
+  return NULL;
+}
+
+string BoxedPrimitiveTypeName(JavaType type) {
   switch (type) {
   switch (type) {
     case JAVATYPE_INT    : return "java.lang.Integer";
     case JAVATYPE_INT    : return "java.lang.Integer";
     case JAVATYPE_LONG   : return "java.lang.Long";
     case JAVATYPE_LONG   : return "java.lang.Long";

+ 7 - 1
src/google/protobuf/compiler/javanano/javanano_helpers.h

@@ -39,6 +39,7 @@
 #include <google/protobuf/compiler/javanano/javanano_params.h>
 #include <google/protobuf/compiler/javanano/javanano_params.h>
 #include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/descriptor.h>
+#include <google/protobuf/io/printer.h>
 
 
 namespace google {
 namespace google {
 namespace protobuf {
 namespace protobuf {
@@ -111,6 +112,9 @@ string FieldConstantName(const FieldDescriptor *field);
 
 
 string FieldDefaultConstantName(const FieldDescriptor *field);
 string FieldDefaultConstantName(const FieldDescriptor *field);
 
 
+// Print the field's proto-syntax definition as a comment.
+void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field);
+
 enum JavaType {
 enum JavaType {
   JAVATYPE_INT,
   JAVATYPE_INT,
   JAVATYPE_LONG,
   JAVATYPE_LONG,
@@ -129,10 +133,12 @@ inline JavaType GetJavaType(const FieldDescriptor* field) {
   return GetJavaType(field->type());
   return GetJavaType(field->type());
 }
 }
 
 
+string PrimitiveTypeName(JavaType type);
+
 // Get the fully-qualified class name for a boxed primitive type, e.g.
 // Get the fully-qualified class name for a boxed primitive type, e.g.
 // "java.lang.Integer" for JAVATYPE_INT.  Returns NULL for enum and message
 // "java.lang.Integer" for JAVATYPE_INT.  Returns NULL for enum and message
 // types.
 // types.
-const char* BoxedPrimitiveTypeName(JavaType type);
+string BoxedPrimitiveTypeName(JavaType type);
 
 
 string EmptyArrayName(const Params& params, const FieldDescriptor* field);
 string EmptyArrayName(const Params& params, const FieldDescriptor* field);
 
 

+ 33 - 59
src/google/protobuf/compiler/javanano/javanano_message.cc

@@ -54,14 +54,6 @@ using internal::WireFormatLite;
 
 
 namespace {
 namespace {
 
 
-void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) {
-  // Print the field's proto-syntax definition as a comment.  We don't want to
-  // print group bodies so we cut off after the first line.
-  string def = field->DebugString();
-  printer->Print("// $def$\n",
-    "def", def.substr(0, def.find_first_of('\n')));
-}
-
 struct FieldOrderingByNumber {
 struct FieldOrderingByNumber {
   inline bool operator()(const FieldDescriptor* a,
   inline bool operator()(const FieldDescriptor* a,
                          const FieldDescriptor* b) const {
                          const FieldDescriptor* b) const {
@@ -82,13 +74,6 @@ const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) {
   return fields;
   return fields;
 }
 }
 
 
-// Get an identifier that uniquely identifies this type within the file.
-// This is used to declare static variables related to this type at the
-// outermost file scope.
-string UniqueFileScopeIdentifier(const Descriptor* descriptor) {
-  return "static_" + StringReplace(descriptor->full_name(), ".", "_", true);
-}
-
 }  // namespace
 }  // namespace
 
 
 // ===================================================================
 // ===================================================================
@@ -149,7 +134,8 @@ void MessageGenerator::Generate(io::Printer* printer) {
   }
   }
   if (params_.store_unknown_fields()) {
   if (params_.store_unknown_fields()) {
     printer->Print(
     printer->Print(
-      "    com.google.protobuf.nano.ExtendableMessageNano {\n");
+      "    com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n",
+      "classname", descriptor_->name());
   } else {
   } else {
     printer->Print(
     printer->Print(
       "    com.google.protobuf.nano.MessageNano {\n");
       "    com.google.protobuf.nano.MessageNano {\n");
@@ -285,22 +271,20 @@ void MessageGenerator::Generate(io::Printer* printer) {
 
 
 void MessageGenerator::
 void MessageGenerator::
 GenerateMessageSerializationMethods(io::Printer* printer) {
 GenerateMessageSerializationMethods(io::Printer* printer) {
+  // Rely on the parent implementations of writeTo() and getSerializedSize()
+  // if there are no fields to serialize in this message.
+  if (descriptor_->field_count() == 0) {
+    return;
+  }
+
   scoped_array<const FieldDescriptor*> sorted_fields(
   scoped_array<const FieldDescriptor*> sorted_fields(
     SortFieldsByNumber(descriptor_));
     SortFieldsByNumber(descriptor_));
 
 
-  // writeTo only throws an exception if it contains one or more fields to write
-  if (descriptor_->field_count() > 0 || params_.store_unknown_fields()) {
-    printer->Print(
-      "\n"
-      "@Override\n"
-      "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n"
-      "    throws java.io.IOException {\n");
-  } else {
-    printer->Print(
-      "\n"
-      "@Override\n"
-      "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output) {\n");
-  }
+  printer->Print(
+    "\n"
+    "@Override\n"
+    "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n"
+    "    throws java.io.IOException {\n");
   printer->Indent();
   printer->Indent();
 
 
   // Output the fields in sorted order
   // Output the fields in sorted order
@@ -308,36 +292,31 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
     GenerateSerializeOneField(printer, sorted_fields[i]);
     GenerateSerializeOneField(printer, sorted_fields[i]);
   }
   }
 
 
-  // Write unknown fields.
-  if (params_.store_unknown_fields()) {
-    printer->Print(
-      "com.google.protobuf.nano.WireFormatNano.writeUnknownFields(\n"
-      "    unknownFieldData, output);\n");
-  }
+  // The parent implementation will write any unknown fields if necessary.
+  printer->Print(
+    "super.writeTo(output);\n");
 
 
   printer->Outdent();
   printer->Outdent();
   printer->Print("}\n");
   printer->Print("}\n");
 
 
-  // Rely on the parent implementation of getSerializedSize if there are no fields to
-  // serialize in this MessageNano.
-  if (descriptor_->field_count() != 0) {
-    printer->Print(
-      "\n"
-      "@Override\n"
-      "public int getSerializedSize() {\n"
-      "  int size = super.getSerializedSize();\n");
-    printer->Indent();
-
-    for (int i = 0; i < descriptor_->field_count(); i++) {
-      field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
-    }
+  // The parent implementation will get the serialized size for unknown
+  // fields if necessary.
+  printer->Print(
+    "\n"
+    "@Override\n"
+    "public int getSerializedSize() {\n"
+    "  int size = super.getSerializedSize();\n");
+  printer->Indent();
 
 
-    printer->Outdent();
-    printer->Print(
-      "  cachedSize = size;\n"
-      "  return size;\n"
-      "}\n");
+  for (int i = 0; i < descriptor_->field_count(); i++) {
+    field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
   }
   }
+
+  printer->Outdent();
+  printer->Print(
+    "  cachedSize = size;\n"
+    "  return size;\n"
+    "}\n");
 }
 }
 
 
 void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
 void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
@@ -371,12 +350,7 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
   printer->Indent();
   printer->Indent();
   if (params_.store_unknown_fields()) {
   if (params_.store_unknown_fields()) {
     printer->Print(
     printer->Print(
-        "if (unknownFieldData == null) {\n"
-        "  unknownFieldData =\n"
-        "      new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n"
-        "}\n"
-        "if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(\n"
-        "    unknownFieldData, input, tag)) {\n"
+        "if (!storeUnknownField(input, tag)) {\n"
         "  return this;\n"
         "  return this;\n"
         "}\n");
         "}\n");
   } else {
   } else {

+ 0 - 20
src/google/protobuf/compiler/javanano/javanano_primitive_field.cc

@@ -54,26 +54,6 @@ using internal::WireFormatLite;
 
 
 namespace {
 namespace {
 
 
-const char* PrimitiveTypeName(JavaType type) {
-  switch (type) {
-    case JAVATYPE_INT    : return "int";
-    case JAVATYPE_LONG   : return "long";
-    case JAVATYPE_FLOAT  : return "float";
-    case JAVATYPE_DOUBLE : return "double";
-    case JAVATYPE_BOOLEAN: return "boolean";
-    case JAVATYPE_STRING : return "java.lang.String";
-    case JAVATYPE_BYTES  : return "byte[]";
-    case JAVATYPE_ENUM   : return NULL;
-    case JAVATYPE_MESSAGE: return NULL;
-
-    // No default because we want the compiler to complain if any new
-    // JavaTypes are added.
-  }
-
-  GOOGLE_LOG(FATAL) << "Can't get here.";
-  return NULL;
-}
-
 bool IsReferenceType(JavaType type) {
 bool IsReferenceType(JavaType type) {
   switch (type) {
   switch (type) {
     case JAVATYPE_INT    : return false;
     case JAVATYPE_INT    : return false;

+ 2 - 19
src/google/protobuf/unittest_extension_nano.proto

@@ -18,31 +18,14 @@ message AnotherMessage {
   optional bool value = 2;
   optional bool value = 2;
 }
 }
 
 
-extend ExtendableMessage {
-  optional string some_string = 10;
-  optional int32 some_int = 11;
-  optional int64 some_long = 12;
-  optional float some_float = 13;
-  optional double some_double = 14;
-  optional bool some_bool = 15;
-  optional AnEnum some_enum = 16;
-  optional AnotherMessage some_message = 17;
-  repeated string some_repeated_string = 18;
-  repeated int32 some_repeated_int = 19;
-  repeated int64 some_repeated_long = 20;
-  repeated float some_repeated_float = 21;
-  repeated double some_repeated_double = 22;
-  repeated bool some_repeated_bool = 23;
-  repeated AnEnum some_repeated_enum = 24;
-  repeated AnotherMessage some_repeated_message = 25;
-}
-
 message ContainerMessage {
 message ContainerMessage {
   extend ExtendableMessage {
   extend ExtendableMessage {
     optional bool another_thing = 100;
     optional bool another_thing = 100;
   }
   }
 }
 }
 
 
+// For testNanoOptionalGroupWithUnknownFieldsEnabled;
+// not part of the extensions tests.
 message MessageWithGroup {
 message MessageWithGroup {
   optional group Group = 1 {
   optional group Group = 1 {
     optional int32 a = 2;
     optional int32 a = 2;

+ 29 - 0
src/google/protobuf/unittest_extension_packed_nano.proto

@@ -0,0 +1,29 @@
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "com.google.protobuf.nano";
+
+import "google/protobuf/unittest_extension_nano.proto";
+
+// Must be compiled separately due to extension number reuse.
+// The reuse is deliberate, for testing wire compatibility.
+
+message PackedExtensions {
+  extend ExtendableMessage {
+    repeated int32    packed_int32    = 10 [ packed = true ];
+    repeated uint32   packed_uint32   = 11 [ packed = true ];
+    repeated sint32   packed_sint32   = 12 [ packed = true ];
+    repeated int64    packed_int64    = 13 [ packed = true ];
+    repeated uint64   packed_uint64   = 14 [ packed = true ];
+    repeated sint64   packed_sint64   = 15 [ packed = true ];
+    repeated fixed32  packed_fixed32  = 16 [ packed = true ];
+    repeated sfixed32 packed_sfixed32 = 17 [ packed = true ];
+    repeated fixed64  packed_fixed64  = 18 [ packed = true ];
+    repeated sfixed64 packed_sfixed64 = 19 [ packed = true ];
+    repeated bool     packed_bool     = 20 [ packed = true ];
+    repeated float    packed_float    = 21 [ packed = true ];
+    repeated double   packed_double   = 22 [ packed = true ];
+    repeated AnEnum   packed_enum     = 23 [ packed = true ];
+    // Non-packable types omitted.
+  }
+}

+ 34 - 0
src/google/protobuf/unittest_extension_repeated_nano.proto

@@ -0,0 +1,34 @@
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "com.google.protobuf.nano";
+
+import "google/protobuf/unittest_extension_nano.proto";
+
+// Must be compiled separately due to extension number reuse.
+// The reuse is deliberate, for testing wire compatibility.
+
+message RepeatedExtensions {
+  extend ExtendableMessage {
+    repeated int32          repeated_int32    = 10;
+    repeated uint32         repeated_uint32   = 11;
+    repeated sint32         repeated_sint32   = 12;
+    repeated int64          repeated_int64    = 13;
+    repeated uint64         repeated_uint64   = 14;
+    repeated sint64         repeated_sint64   = 15;
+    repeated fixed32        repeated_fixed32  = 16;
+    repeated sfixed32       repeated_sfixed32 = 17;
+    repeated fixed64        repeated_fixed64  = 18;
+    repeated sfixed64       repeated_sfixed64 = 19;
+    repeated bool           repeated_bool     = 20;
+    repeated float          repeated_float    = 21;
+    repeated double         repeated_double   = 22;
+    repeated AnEnum         repeated_enum     = 23;
+    repeated string         repeated_string   = 24;
+    repeated bytes          repeated_bytes    = 25;
+    repeated AnotherMessage repeated_message  = 26;
+    repeated group          RepeatedGroup     = 27 {
+      optional int32 a = 1;
+    }
+  }
+}

+ 34 - 0
src/google/protobuf/unittest_extension_singular_nano.proto

@@ -0,0 +1,34 @@
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "com.google.protobuf.nano";
+
+import "google/protobuf/unittest_extension_nano.proto";
+
+// Must be compiled separately due to extension number reuse.
+// The reuse is deliberate, for testing wire compatibility.
+
+message SingularExtensions {
+  extend ExtendableMessage {
+    optional int32          some_int32    = 10;
+    optional uint32         some_uint32   = 11;
+    optional sint32         some_sint32   = 12;
+    optional int64          some_int64    = 13;
+    optional uint64         some_uint64   = 14;
+    optional sint64         some_sint64   = 15;
+    optional fixed32        some_fixed32  = 16;
+    optional sfixed32       some_sfixed32 = 17;
+    optional fixed64        some_fixed64  = 18;
+    optional sfixed64       some_sfixed64 = 19;
+    optional bool           some_bool     = 20;
+    optional float          some_float    = 21;
+    optional double         some_double   = 22;
+    optional AnEnum         some_enum     = 23;
+    optional string         some_string   = 24;
+    optional bytes          some_bytes    = 25;
+    optional AnotherMessage some_message  = 26;
+    optional group          SomeGroup     = 27 {
+      optional int32 a = 1;
+    }
+  }
+}