Align with main: two ways of parsing repeated packable fields.

It is a requirement for parsing code to handle packed and unpacked
forms on the wire for repeated packable fields. This change aligns
the javanano's behavior with the java's.

Bonus: optimize array length calculation when parsing repeated
fixed-size-element-type fields.

Bonus 2: lose "xMemoizedSerializedSize" for repeated enum fields,
and make the serialized size calculation match that for repeated
int32 fields.

Change-Id: I8a06103d9290234adb46b0971b5ed155544fe86a
diff --git a/java/README.txt b/java/README.txt
index 3a004c7..976ec84 100644
--- a/java/README.txt
+++ b/java/README.txt
@@ -429,6 +429,7 @@
 - Similar rename from CodedOutputStreamMicro to
   CodedOutputByteBufferNano.
 - Repeated fields are in arrays, not ArrayList or Vector.
+- Full support of serializing/deserializing repeated packed fields.
 - Unset messages/groups are null, not an immutable empty default
   instance.
 - Required fields are always serialized.
diff --git a/java/pom.xml b/java/pom.xml
index cfa35a5..2f40b98 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -171,6 +171,7 @@
                   <arg value="--proto_path=src/test/java" />
                   <arg value="../src/google/protobuf/unittest_enum_class_nano.proto" />
                   <arg value="../src/google/protobuf/unittest_enum_class_multiple_nano.proto" />
+                  <arg value="../src/google/protobuf/unittest_repeated_packables_nano.proto" />
                 </exec>
                 <exec executable="../src/protoc">
                   <arg value="--javanano_out=optional_field_style=reftypes,generate_equals=true:target/generated-test-sources" />
diff --git a/java/src/test/java/com/google/protobuf/NanoTest.java b/java/src/test/java/com/google/protobuf/NanoTest.java
index 724e741..0e1c9bc 100644
--- a/java/src/test/java/com/google/protobuf/NanoTest.java
+++ b/java/src/test/java/com/google/protobuf/NanoTest.java
@@ -49,6 +49,7 @@
 import com.google.protobuf.nano.NanoOuterClass;
 import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
 import com.google.protobuf.nano.NanoReferenceTypes;
+import com.google.protobuf.nano.NanoRepeatedPackables;
 import com.google.protobuf.nano.TestRepeatedMergeNano;
 import com.google.protobuf.nano.UnittestImportNano;
 import com.google.protobuf.nano.UnittestMultipleNano;
@@ -3064,6 +3065,77 @@
     assertEquals(30, firstContainer.contained.repeatedInt32[2]);
   }
 
+  public void testRepeatedPackables() throws Exception {
+    // Check that repeated fields with packable types can accept both packed and unpacked
+    // serialized forms.
+    NanoRepeatedPackables.NonPacked nonPacked = new NanoRepeatedPackables.NonPacked();
+    nonPacked.int32S = new int[] {1, 2, 3};
+    nonPacked.int64S = new long[] {4, 5, 6};
+    nonPacked.uint32S = new int[] {7, 8, 9};
+    nonPacked.uint64S = new long[] {10, 11, 12};
+    nonPacked.sint32S = new int[] {13, 14, 15};
+    nonPacked.sint64S = new long[] {16, 17, 18};
+    nonPacked.fixed32S = new int[] {19, 20, 21};
+    nonPacked.fixed64S = new long[] {22, 23, 24};
+    nonPacked.sfixed32S = new int[] {25, 26, 27};
+    nonPacked.sfixed64S = new long[] {28, 29, 30};
+    nonPacked.floats = new float[] {31, 32, 33};
+    nonPacked.doubles = new double[] {34, 35, 36};
+    nonPacked.bools = new boolean[] {false, true};
+    nonPacked.enums = new int[] {
+      NanoRepeatedPackables.Enum.OPTION_ONE,
+      NanoRepeatedPackables.Enum.OPTION_TWO,
+    };
+    nonPacked.noise = 13579;
+
+    byte[] nonPackedSerialized = MessageNano.toByteArray(nonPacked);
+
+    NanoRepeatedPackables.Packed packed =
+        MessageNano.mergeFrom(new NanoRepeatedPackables.Packed(), nonPackedSerialized);
+    assertRepeatedPackablesEqual(nonPacked, packed);
+
+    byte[] packedSerialized = MessageNano.toByteArray(packed);
+    // Just a cautious check that the two serialized forms are different,
+    // to make sure the remaining of this test is useful:
+    assertFalse(Arrays.equals(nonPackedSerialized, packedSerialized));
+
+    nonPacked = MessageNano.mergeFrom(new NanoRepeatedPackables.NonPacked(), packedSerialized);
+    assertRepeatedPackablesEqual(nonPacked, packed);
+
+    // Test mixed serialized form.
+    byte[] mixedSerialized = new byte[nonPackedSerialized.length + packedSerialized.length];
+    System.arraycopy(nonPackedSerialized, 0, mixedSerialized, 0, nonPackedSerialized.length);
+    System.arraycopy(packedSerialized, 0,
+        mixedSerialized, nonPackedSerialized.length, packedSerialized.length);
+
+    nonPacked = MessageNano.mergeFrom(new NanoRepeatedPackables.NonPacked(), mixedSerialized);
+    packed = MessageNano.mergeFrom(new NanoRepeatedPackables.Packed(), mixedSerialized);
+    assertRepeatedPackablesEqual(nonPacked, packed);
+    assertTrue(Arrays.equals(new int[] {1, 2, 3, 1, 2, 3}, nonPacked.int32S));
+    assertTrue(Arrays.equals(new int[] {13, 14, 15, 13, 14, 15}, nonPacked.sint32S));
+    assertTrue(Arrays.equals(new int[] {25, 26, 27, 25, 26, 27}, nonPacked.sfixed32S));
+    assertTrue(Arrays.equals(new boolean[] {false, true, false, true}, nonPacked.bools));
+  }
+
+  private void assertRepeatedPackablesEqual(
+      NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) {
+    // Not using MessageNano.equals() -- that belongs to a separate test.
+    assertTrue(Arrays.equals(nonPacked.int32S, packed.int32S));
+    assertTrue(Arrays.equals(nonPacked.int64S, packed.int64S));
+    assertTrue(Arrays.equals(nonPacked.uint32S, packed.uint32S));
+    assertTrue(Arrays.equals(nonPacked.uint64S, packed.uint64S));
+    assertTrue(Arrays.equals(nonPacked.sint32S, packed.sint32S));
+    assertTrue(Arrays.equals(nonPacked.sint64S, packed.sint64S));
+    assertTrue(Arrays.equals(nonPacked.fixed32S, packed.fixed32S));
+    assertTrue(Arrays.equals(nonPacked.fixed64S, packed.fixed64S));
+    assertTrue(Arrays.equals(nonPacked.sfixed32S, packed.sfixed32S));
+    assertTrue(Arrays.equals(nonPacked.sfixed64S, packed.sfixed64S));
+    assertTrue(Arrays.equals(nonPacked.floats, packed.floats));
+    assertTrue(Arrays.equals(nonPacked.doubles, packed.doubles));
+    assertTrue(Arrays.equals(nonPacked.bools, packed.bools));
+    assertTrue(Arrays.equals(nonPacked.enums, packed.enums));
+  }
+
   private void assertHasWireData(MessageNano message, boolean expected) {
     byte[] bytes = MessageNano.toByteArray(message);
     int wireLength = bytes.length;
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
index b9f4fd0..17f0e26 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc
@@ -294,10 +294,6 @@
 GenerateMembers(io::Printer* printer) const {
   printer->Print(variables_,
     "public $type$[] $name$;\n");
-  if (descriptor_->options().packed()) {
-    printer->Print(variables_,
-      "private int $name$MemoizedSerializedSize;\n");
-  }
 }
 
 void RepeatedEnumFieldGenerator::
@@ -309,45 +305,58 @@
 void RepeatedEnumFieldGenerator::
 GenerateMergingCode(io::Printer* printer) const {
   // First, figure out the length of the array, then parse.
-  if (descriptor_->options().packed()) {
-    printer->Print(variables_,
-      "int length = input.readRawVarint32();\n"
-      "int limit = input.pushLimit(length);\n"
-      "// First pass to compute array length.\n"
-      "int arrayLength = 0;\n"
-      "int startPos = input.getPosition();\n"
-      "while (input.getBytesUntilLimit() > 0) {\n"
-      "  input.readInt32();\n"
-      "  arrayLength++;\n"
-      "}\n"
-      "input.rewindToPosition(startPos);\n"
-      "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
-      "int[] newArray = new int[i + arrayLength];\n"
-      "if (i != 0) {\n"
-      "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
-      "}\n"
-      "for (; i < newArray.length; i++) {\n"
-      "  newArray[i] = input.readInt32();\n"
-      "}\n"
-      "this.$name$ = newArray;\n"
-      "input.popLimit(limit);\n");
-  } else {
-    printer->Print(variables_,
-      "int arrayLength = com.google.protobuf.nano.WireFormatNano\n"
-      "    .getRepeatedFieldArrayLength(input, $tag$);\n"
-      "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
-      "int[] newArray = new int[i + arrayLength];\n"
-      "if (i != 0) {\n"
-      "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
-      "}\n"
-      "for (; i < newArray.length - 1; i++) {\n"
-      "  newArray[i] = input.readInt32();\n"
-      "  input.readTag();\n"
-      "}\n"
-      "// Last one without readTag.\n"
-      "newArray[i] = input.readInt32();\n"
-      "this.$name$ = newArray;\n");
-  }
+  printer->Print(variables_,
+    "int arrayLength = com.google.protobuf.nano.WireFormatNano\n"
+    "    .getRepeatedFieldArrayLength(input, $tag$);\n"
+    "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
+    "int[] newArray = new int[i + arrayLength];\n"
+    "if (i != 0) {\n"
+    "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+    "}\n"
+    "for (; i < newArray.length - 1; i++) {\n"
+    "  newArray[i] = input.readInt32();\n"
+    "  input.readTag();\n"
+    "}\n"
+    "// Last one without readTag.\n"
+    "newArray[i] = input.readInt32();\n"
+    "this.$name$ = newArray;\n");
+}
+
+void RepeatedEnumFieldGenerator::
+GenerateMergingCodeFromPacked(io::Printer* printer) const {
+  printer->Print(variables_,
+    "int length = input.readRawVarint32();\n"
+    "int limit = input.pushLimit(length);\n"
+    "// First pass to compute array length.\n"
+    "int arrayLength = 0;\n"
+    "int startPos = input.getPosition();\n"
+    "while (input.getBytesUntilLimit() > 0) {\n"
+    "  input.readInt32();\n"
+    "  arrayLength++;\n"
+    "}\n"
+    "input.rewindToPosition(startPos);\n"
+    "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
+    "int[] newArray = new int[i + arrayLength];\n"
+    "if (i != 0) {\n"
+    "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+    "}\n"
+    "for (; i < newArray.length; i++) {\n"
+    "  newArray[i] = input.readInt32();\n"
+    "}\n"
+    "this.$name$ = newArray;\n"
+    "input.popLimit(limit);\n");
+}
+
+void RepeatedEnumFieldGenerator::
+GenerateRepeatedDataSizeCode(io::Printer* printer) const {
+  // Creates a variable dataSize and puts the serialized size in there.
+  printer->Print(variables_,
+    "int dataSize = 0;\n"
+    "for (int i = 0; i < this.$name$.length; i++) {\n"
+    "  int element = this.$name$[i];\n"
+    "  dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
+    "      .computeInt32SizeNoTag(element);\n"
+    "}\n");
 }
 
 void RepeatedEnumFieldGenerator::
@@ -357,18 +366,20 @@
   printer->Indent();
 
   if (descriptor_->options().packed()) {
+    GenerateRepeatedDataSizeCode(printer);
     printer->Print(variables_,
       "output.writeRawVarint32($tag$);\n"
-      "output.writeRawVarint32($name$MemoizedSerializedSize);\n"
-      "for (int element : this.$name$) {\n"
-      "  output.writeRawVarint32(element);\n"
+      "output.writeRawVarint32(dataSize);\n"
+      "for (int i = 0; i < this.$name$.length; i++) {\n"
+      "  output.writeRawVarint32(this.$name$[i]);\n"
       "}\n");
   } else {
     printer->Print(variables_,
-      "for (int element : this.$name$) {\n"
-      "  output.writeInt32($number$, element);\n"
+      "for (int i = 0; i < this.$name$.length; i++) {\n"
+      "  output.writeInt32($number$, this.$name$[i]);\n"
       "}\n");
   }
+
   printer->Outdent();
   printer->Print(variables_,
     "}\n");
@@ -380,39 +391,24 @@
     "if (this.$name$ != null && this.$name$.length > 0) {\n");
   printer->Indent();
 
-  printer->Print(variables_,
-    "int dataSize = 0;\n"
-    "for (int element : this.$name$) {\n"
-    "  dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
-    "    .computeInt32SizeNoTag(element);\n"
-    "}\n");
+  GenerateRepeatedDataSizeCode(printer);
 
   printer->Print(
     "size += dataSize;\n");
   if (descriptor_->options().packed()) {
-    // cache the data size for packed fields.
     printer->Print(variables_,
       "size += $tag_size$;\n"
       "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
-      "  .computeRawVarint32Size(dataSize);\n"
-      "$name$MemoizedSerializedSize = dataSize;\n");
+      "    .computeRawVarint32Size(dataSize);\n");
   } else {
     printer->Print(variables_,
-        "size += $tag_size$ * this.$name$.length;\n");
+      "size += $tag_size$ * this.$name$.length;\n");
   }
 
   printer->Outdent();
 
-  // set cached size to 0 for empty packed fields.
-  if (descriptor_->options().packed()) {
-    printer->Print(variables_,
-      "} else {\n"
-      "  $name$MemoizedSerializedSize = 0;\n"
-      "}\n");
-  } else {
-    printer->Print(
-      "}\n");
-  }
+  printer->Print(
+    "}\n");
 }
 
 void RepeatedEnumFieldGenerator::
diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.h b/src/google/protobuf/compiler/javanano/javanano_enum_field.h
index af317d8..9000d20 100644
--- a/src/google/protobuf/compiler/javanano/javanano_enum_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.h
@@ -96,12 +96,15 @@
   void GenerateMembers(io::Printer* printer) const;
   void GenerateClearCode(io::Printer* printer) const;
   void GenerateMergingCode(io::Printer* printer) const;
+  void GenerateMergingCodeFromPacked(io::Printer* printer) const;
   void GenerateSerializationCode(io::Printer* printer) const;
   void GenerateSerializedSizeCode(io::Printer* printer) const;
   void GenerateEqualsCode(io::Printer* printer) const;
   void GenerateHashCodeCode(io::Printer* printer) const;
 
  private:
+  void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
+
   const FieldDescriptor* descriptor_;
   map<string, string> variables_;
 
diff --git a/src/google/protobuf/compiler/javanano/javanano_field.cc b/src/google/protobuf/compiler/javanano/javanano_field.cc
index 6629f96..62e133e 100644
--- a/src/google/protobuf/compiler/javanano/javanano_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_field.cc
@@ -46,6 +46,16 @@
 
 FieldGenerator::~FieldGenerator() {}
 
+void FieldGenerator::GenerateMergingCodeFromPacked(io::Printer* printer) const {
+  // Reaching here indicates a bug. Cases are:
+  //   - This FieldGenerator should support packing, but this method should be
+  //     overridden.
+  //   - This FieldGenerator doesn't support packing, and this method should
+  //     never have been called.
+  GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() "
+             << "called on field generator that does not support packing.";
+}
+
 FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, const Params &params)
   : descriptor_(descriptor),
     field_generators_(
diff --git a/src/google/protobuf/compiler/javanano/javanano_field.h b/src/google/protobuf/compiler/javanano/javanano_field.h
index 150ec19..14b6489 100644
--- a/src/google/protobuf/compiler/javanano/javanano_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_field.h
@@ -60,6 +60,12 @@
   virtual void GenerateMembers(io::Printer* printer) const = 0;
   virtual void GenerateClearCode(io::Printer* printer) const = 0;
   virtual void GenerateMergingCode(io::Printer* printer) const = 0;
+
+  // Generates code to merge from packed serialized form. The default
+  // implementation will fail; subclasses which can handle packed serialized
+  // forms will override this and print appropriate code to the printer.
+  virtual void GenerateMergingCodeFromPacked(io::Printer* printer) const;
+
   virtual void GenerateSerializationCode(io::Printer* printer) const = 0;
   virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
   virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc
index 901870f..f7ab62c 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_message.cc
@@ -320,7 +320,7 @@
   for (int i = 0; i < descriptor_->field_count(); i++) {
     const FieldDescriptor* field = sorted_fields[i];
     uint32 tag = WireFormatLite::MakeTag(field->number(),
-      WireFormat::WireTypeForField(field));
+      WireFormat::WireTypeForFieldType(field->type()));
 
     printer->Print(
       "case $tag$: {\n",
@@ -333,6 +333,24 @@
     printer->Print(
       "  break;\n"
       "}\n");
+
+    if (field->is_packable()) {
+      // To make packed = true wire compatible, we generate parsing code from a
+      // packed version of this field regardless of field->options().packed().
+      uint32 packed_tag = WireFormatLite::MakeTag(field->number(),
+        WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
+      printer->Print(
+        "case $tag$: {\n",
+        "tag", SimpleItoa(packed_tag));
+      printer->Indent();
+
+      field_generators_.get(field).GenerateMergingCodeFromPacked(printer);
+
+      printer->Outdent();
+      printer->Print(
+        "  break;\n"
+        "}\n");
+    }
   }
 
   printer->Outdent();
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
index 5887a7e..c0717e6 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc
@@ -685,10 +685,46 @@
 void RepeatedPrimitiveFieldGenerator::
 GenerateMergingCode(io::Printer* printer) const {
   // First, figure out the length of the array, then parse.
-  if (descriptor_->is_packable() && descriptor_->options().packed()) {
+  printer->Print(variables_,
+    "int arrayLength = com.google.protobuf.nano.WireFormatNano\n"
+    "    .getRepeatedFieldArrayLength(input, $tag$);\n"
+    "int i = this.$name$ == null ? 0 : this.$name$.length;\n");
+
+  if (GetJavaType(descriptor_) == JAVATYPE_BYTES) {
     printer->Print(variables_,
-      "int length = input.readRawVarint32();\n"
-      "int limit = input.pushLimit(length);\n"
+      "byte[][] newArray = new byte[i + arrayLength][];\n");
+  } else {
+    printer->Print(variables_,
+      "$type$[] newArray = new $type$[i + arrayLength];\n");
+  }
+  printer->Print(variables_,
+    "if (i != 0) {\n"
+    "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+    "}\n"
+    "for (; i < newArray.length - 1; i++) {\n"
+    "  newArray[i] = input.read$capitalized_type$();\n"
+    "  input.readTag();\n"
+    "}\n"
+    "// Last one without readTag.\n"
+    "newArray[i] = input.read$capitalized_type$();\n"
+    "this.$name$ = newArray;\n");
+}
+
+void RepeatedPrimitiveFieldGenerator::
+GenerateMergingCodeFromPacked(io::Printer* printer) const {
+  printer->Print(
+    "int length = input.readRawVarint32();\n"
+    "int limit = input.pushLimit(length);\n");
+
+  // If we know the elements will all be of the same size, the arrayLength
+  // can be calculated much more easily. However, FixedSize() returns 1 for
+  // repeated bool fields, which are guaranteed to have the fixed size of
+  // 1 byte per value only if we control the output. On the wire they can
+  // legally appear as variable-size integers, so we need to use the slow
+  // way for repeated bool fields.
+  if (descriptor_->type() == FieldDescriptor::TYPE_BOOL
+      || FixedSize(descriptor_->type()) == -1) {
+    printer->Print(variables_,
       "// First pass to compute array length.\n"
       "int arrayLength = 0;\n"
       "int startPos = input.getPosition();\n"
@@ -696,42 +732,23 @@
       "  input.read$capitalized_type$();\n"
       "  arrayLength++;\n"
       "}\n"
-      "input.rewindToPosition(startPos);\n"
-      "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
-      "$type$[] newArray = new $type$[i + arrayLength];\n"
-      "if (i != 0) {\n"
-      "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
-      "}\n"
-      "for (; i < newArray.length; i++) {\n"
-      "  newArray[i] = input.read$capitalized_type$();\n"
-      "}\n"
-      "this.$name$ = newArray;\n"
-      "input.popLimit(limit);\n");
+      "input.rewindToPosition(startPos);\n");
   } else {
     printer->Print(variables_,
-      "int arrayLength = com.google.protobuf.nano.WireFormatNano\n"
-      "    .getRepeatedFieldArrayLength(input, $tag$);\n"
-      "int i = this.$name$ == null ? 0 : this.$name$.length;\n");
-
-    if (GetJavaType(descriptor_) == JAVATYPE_BYTES) {
-      printer->Print(variables_,
-        "byte[][] newArray = new byte[i + arrayLength][];\n");
-    } else {
-      printer->Print(variables_,
-        "$type$[] newArray = new $type$[i + arrayLength];\n");
-    }
-    printer->Print(variables_,
-      "if (i != 0) {\n"
-      "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
-      "}\n"
-      "for (; i < newArray.length - 1; i++) {\n"
-      "  newArray[i] = input.read$capitalized_type$();\n"
-      "  input.readTag();\n"
-      "}\n"
-      "// Last one without readTag.\n"
-      "newArray[i] = input.read$capitalized_type$();\n"
-      "this.$name$ = newArray;\n");
+      "int arrayLength = length / $fixed_size$;\n");
   }
+
+  printer->Print(variables_,
+    "int i = this.$name$ == null ? 0 : this.$name$.length;\n"
+    "$type$[] newArray = new $type$[i + arrayLength];\n"
+    "if (i != 0) {\n"
+    "  java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n"
+    "}\n"
+    "for (; i < newArray.length; i++) {\n"
+    "  newArray[i] = input.read$capitalized_type$();\n"
+    "}\n"
+    "this.$name$ = newArray;\n"
+    "input.popLimit(limit);\n");
 }
 
 void RepeatedPrimitiveFieldGenerator::
@@ -812,7 +829,7 @@
     printer->Print(variables_,
       "size += $tag_size$;\n"
       "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n"
-      "  .computeRawVarint32Size(dataSize);\n");
+      "    .computeRawVarint32Size(dataSize);\n");
   } else if (IsReferenceType(GetJavaType(descriptor_))) {
     printer->Print(variables_,
       "size += $tag_size$ * dataCount;\n");
diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
index 2d2b268..d207535 100644
--- a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
+++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h
@@ -98,6 +98,7 @@
   void GenerateMembers(io::Printer* printer) const;
   void GenerateClearCode(io::Printer* printer) const;
   void GenerateMergingCode(io::Printer* printer) const;
+  void GenerateMergingCodeFromPacked(io::Printer* printer) const;
   void GenerateSerializationCode(io::Printer* printer) const;
   void GenerateSerializedSizeCode(io::Printer* printer) const;
   void GenerateEqualsCode(io::Printer* printer) const;
diff --git a/src/google/protobuf/unittest_repeated_packables_nano.proto b/src/google/protobuf/unittest_repeated_packables_nano.proto
new file mode 100644
index 0000000..1c78918
--- /dev/null
+++ b/src/google/protobuf/unittest_repeated_packables_nano.proto
@@ -0,0 +1,95 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: maxtroy@google.com (Max Cai)
+
+package protobuf_unittest;
+
+option java_package = "com.google.protobuf.nano";
+option java_outer_classname = "NanoRepeatedPackables";
+
+enum Enum {
+  OPTION_ONE = 1;
+  OPTION_TWO = 2;
+}
+
+// Two almost identical messages with all packable repeated field types.
+// One with none marked as packed and the other all packed. For
+// compatibility, they should be able to parse each other's serialized
+// forms.
+
+message NonPacked {
+
+  // All packable types, none marked as packed.
+
+  repeated    int32 int32s    = 1;
+  repeated    int64 int64s    = 2;
+  repeated   uint32 uint32s   = 3;
+  repeated   uint64 uint64s   = 4;
+  repeated   sint32 sint32s   = 5;
+  repeated   sint64 sint64s   = 6;
+  repeated  fixed32 fixed32s  = 7;
+  repeated  fixed64 fixed64s  = 8;
+  repeated sfixed32 sfixed32s = 9;
+  repeated sfixed64 sfixed64s = 10;
+  repeated    float floats    = 11;
+  repeated   double doubles   = 12;
+  repeated     bool bools     = 13;
+  repeated     Enum enums     = 14;
+
+  // Noise for testing merged deserialization.
+  optional int32 noise = 15;
+
+}
+
+message Packed {
+
+  // All packable types, all matching the field numbers in NonPacked,
+  // all marked as packed.
+
+  repeated    int32 int32s    = 1  [ packed = true ];
+  repeated    int64 int64s    = 2  [ packed = true ];
+  repeated   uint32 uint32s   = 3  [ packed = true ];
+  repeated   uint64 uint64s   = 4  [ packed = true ];
+  repeated   sint32 sint32s   = 5  [ packed = true ];
+  repeated   sint64 sint64s   = 6  [ packed = true ];
+  repeated  fixed32 fixed32s  = 7  [ packed = true ];
+  repeated  fixed64 fixed64s  = 8  [ packed = true ];
+  repeated sfixed32 sfixed32s = 9  [ packed = true ];
+  repeated sfixed64 sfixed64s = 10 [ packed = true ];
+  repeated    float floats    = 11 [ packed = true ];
+  repeated   double doubles   = 12 [ packed = true ];
+  repeated     bool bools     = 13 [ packed = true ];
+  repeated     Enum enums     = 14 [ packed = true ];
+
+  // Noise for testing merged deserialization.
+  optional int32 noise = 15;
+
+}