Przeglądaj źródła

Merge pull request #382 from jtattermusch/integrate_from_master

Integrate changes from latest master branch into csharp branch.
Jie Luo 10 lat temu
rodzic
commit
2d9b1c592f
100 zmienionych plików z 62312 dodań i 81 usunięć
  1. 7 0
      .gitignore
  2. 1 0
      .travis.yml
  3. 123 1
      Makefile.am
  4. 120 0
      conformance/ConformanceJava.java
  5. 22 9
      conformance/Makefile.am
  6. 1 0
      conformance/conformance.proto
  7. 2 2
      conformance/conformance_test_runner.cc
  8. 2 2
      csharp/src/ProtocolBuffers.Test/TestProtos/UnittestDropUnknownFields.cs
  9. 1 1
      examples/add_person.py
  10. 1 1
      examples/list_people.py
  11. 1 0
      generate_descriptor_proto.sh
  12. 23 0
      javanano/README.md
  13. 12 3
      javanano/pom.xml
  14. 2 0
      javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
  15. 241 23
      javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
  16. 5 23
      javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java
  17. 26 7
      javanano/src/main/java/com/google/protobuf/nano/Extension.java
  18. 26 8
      javanano/src/main/java/com/google/protobuf/nano/FieldArray.java
  19. 51 1
      javanano/src/main/java/com/google/protobuf/nano/FieldData.java
  20. 8 0
      javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
  21. 8 0
      javanano/src/main/java/com/google/protobuf/nano/MessageNano.java
  22. 4 0
      javanano/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java
  23. 4 0
      javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java
  24. 80 0
      javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
  25. 4 0
      javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto
  26. 56 0
      objectivec/DevTools/check_version_stamps.sh
  27. 36 0
      objectivec/DevTools/generate_descriptors_proto.sh
  28. 687 0
      objectivec/DevTools/pddm.py
  29. 515 0
      objectivec/DevTools/pddm_tests.py
  30. 535 0
      objectivec/GPBArray.h
  31. 2499 0
      objectivec/GPBArray.m
  32. 130 0
      objectivec/GPBArray_PackagePrivate.h
  33. 84 0
      objectivec/GPBBootstrap.h
  34. 81 0
      objectivec/GPBCodedInputStream.h
  35. 801 0
      objectivec/GPBCodedInputStream.m
  36. 131 0
      objectivec/GPBCodedInputStream_PackagePrivate.h
  37. 340 0
      objectivec/GPBCodedOutputStream.h
  38. 1229 0
      objectivec/GPBCodedOutputStream.m
  39. 143 0
      objectivec/GPBDescriptor.h
  40. 888 0
      objectivec/GPBDescriptor.m
  41. 293 0
      objectivec/GPBDescriptor_PackagePrivate.h
  42. 2233 0
      objectivec/GPBDictionary.h
  43. 12627 0
      objectivec/GPBDictionary.m
  44. 577 0
      objectivec/GPBDictionary_PackagePrivate.h
  45. 51 0
      objectivec/GPBExtensionField.h
  46. 525 0
      objectivec/GPBExtensionField.m
  47. 51 0
      objectivec/GPBExtensionField_PackagePrivate.h
  48. 46 0
      objectivec/GPBExtensionRegistry.h
  49. 98 0
      objectivec/GPBExtensionRegistry.m
  50. 40 0
      objectivec/GPBExtensionRegistry_PackagePrivate.h
  51. 56 0
      objectivec/GPBField.h
  52. 328 0
      objectivec/GPBField.m
  53. 49 0
      objectivec/GPBField_PackagePrivate.h
  54. 151 0
      objectivec/GPBMessage.h
  55. 4735 0
      objectivec/GPBMessage.m
  56. 124 0
      objectivec/GPBMessage_PackagePrivate.h
  57. 45 0
      objectivec/GPBProtocolBuffers.h
  58. 49 0
      objectivec/GPBProtocolBuffers.m
  59. 41 0
      objectivec/GPBProtocolBuffers_RuntimeSupport.h
  60. 42 0
      objectivec/GPBRootObject.h
  61. 177 0
      objectivec/GPBRootObject.m
  62. 42 0
      objectivec/GPBRootObject_PackagePrivate.h
  63. 102 0
      objectivec/GPBTypes.h
  64. 46 0
      objectivec/GPBUnknownFieldSet.h
  65. 422 0
      objectivec/GPBUnknownFieldSet.m
  66. 61 0
      objectivec/GPBUnknownFieldSet_PackagePrivate.h
  67. 181 0
      objectivec/GPBUtilities.h
  68. 1645 0
      objectivec/GPBUtilities.m
  69. 426 0
      objectivec/GPBUtilities_PackagePrivate.h
  70. 48 0
      objectivec/GPBWellKnownTypes.h
  71. 117 0
      objectivec/GPBWellKnownTypes.m
  72. 68 0
      objectivec/GPBWireFormat.h
  73. 78 0
      objectivec/GPBWireFormat.m
  74. 597 0
      objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
  75. 7 0
      objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  76. 8 0
      objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  77. 125 0
      objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
  78. 115 0
      objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
  79. 685 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
  80. 7 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  81. 8 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  82. 62 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcbaselines/8BBEA4A5147C727100C4ADB7.xcbaseline/FFE465CA-0E74-40E8-9F09-500B66B7DCB2.plist
  83. 21 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcbaselines/8BBEA4A5147C727100C4ADB7.xcbaseline/Info.plist
  84. 134 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme
  85. 116 0
      objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme
  86. 40 0
      objectivec/Tests/Filter1.txt
  87. 35 0
      objectivec/Tests/Filter2.txt
  88. 57 0
      objectivec/Tests/GPBARCUnittestProtos.m
  89. 3365 0
      objectivec/Tests/GPBArrayTests.m
  90. 290 0
      objectivec/Tests/GPBCodedInputStreamTests.m
  91. 321 0
      objectivec/Tests/GPBCodedOuputStreamTests.m
  92. 157 0
      objectivec/Tests/GPBConcurrencyTests.m
  93. 232 0
      objectivec/Tests/GPBDescriptorTests.m
  94. 2421 0
      objectivec/Tests/GPBDictionaryTests+Bool.m
  95. 3650 0
      objectivec/Tests/GPBDictionaryTests+Int32.m
  96. 3650 0
      objectivec/Tests/GPBDictionaryTests+Int64.m
  97. 3362 0
      objectivec/Tests/GPBDictionaryTests+String.m
  98. 3650 0
      objectivec/Tests/GPBDictionaryTests+UInt32.m
  99. 3649 0
      objectivec/Tests/GPBDictionaryTests+UInt64.m
  100. 1044 0
      objectivec/Tests/GPBDictionaryTests.pddm

+ 7 - 0
.gitignore

@@ -51,6 +51,7 @@ cpp_test*.pb.*
 *.pyc
 *.egg-info
 *_pb2.py
+python/*.egg
 python/.eggs/
 python/build/
 python/google/protobuf/compiler/
@@ -82,3 +83,9 @@ vsprojects/Release
 # NuGet packages: we want the repository configuration, but not the
 # packages themselves.
 /csharp/src/packages/*/
+
+# Directories created by opening the Objective C Xcode projects.
+objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/xcuserdata/
+objectivec/ProtocolBuffers_OSX.xcodeproj/xcuserdata/
+objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/xcuserdata/
+objectivec/ProtocolBuffers_iOS.xcodeproj/xcuserdata/

+ 1 - 0
.travis.yml

@@ -14,6 +14,7 @@ script:
   - cd python && python setup.py build && python setup.py test && cd ..
   - export LD_LIBRARY_PATH=../src/.libs
   - cd python && python setup.py build --cpp_implementation && python setup.py test --cpp_implementation && cd ..
+  - cd conformance && make test_java && cd ..
   - make distcheck -j2
 notifications:
   email: false

+ 123 - 1
Makefile.am

@@ -197,6 +197,128 @@ javanano_EXTRA_DIST=
   javanano/README.md                                                                      \
   javanano/pom.xml
 
+objectivec_EXTRA_DIST=                                                       \
+  objectivec/DevTools/generate_descriptors_proto.sh                          \
+  objectivec/DevTools/pddm.py                                                \
+  objectivec/DevTools/pddm_tests.py                                          \
+  objectivec/google/protobuf/Descriptor.pbobjc.h                             \
+  objectivec/google/protobuf/Descriptor.pbobjc.m                             \
+  objectivec/google/protobuf/Duration.pbobjc.h                               \
+  objectivec/google/protobuf/Duration.pbobjc.m                               \
+  objectivec/google/protobuf/Timestamp.pbobjc.h                              \
+  objectivec/google/protobuf/Timestamp.pbobjc.m                              \
+  objectivec/GPBArray.h                                                      \
+  objectivec/GPBArray.m                                                      \
+  objectivec/GPBArray_PackagePrivate.h                                       \
+  objectivec/GPBBootstrap.h                                                  \
+  objectivec/GPBCodedInputStream.h                                           \
+  objectivec/GPBCodedInputStream.m                                           \
+  objectivec/GPBCodedInputStream_PackagePrivate.h                            \
+  objectivec/GPBCodedOutputStream.h                                          \
+  objectivec/GPBCodedOutputStream.m                                          \
+  objectivec/GPBDescriptor.h                                                 \
+  objectivec/GPBDescriptor.m                                                 \
+  objectivec/GPBDescriptor_PackagePrivate.h                                  \
+  objectivec/GPBDictionary.h                                                 \
+  objectivec/GPBDictionary.m                                                 \
+  objectivec/GPBDictionary_PackagePrivate.h                                  \
+  objectivec/GPBExtensionField.h                                             \
+  objectivec/GPBExtensionField.m                                             \
+  objectivec/GPBExtensionField_PackagePrivate.h                              \
+  objectivec/GPBExtensionRegistry.h                                          \
+  objectivec/GPBExtensionRegistry.m                                          \
+  objectivec/GPBExtensionRegistry_PackagePrivate.h                           \
+  objectivec/GPBField.h                                                      \
+  objectivec/GPBField.m                                                      \
+  objectivec/GPBField_PackagePrivate.h                                       \
+  objectivec/GPBMessage.h                                                    \
+  objectivec/GPBMessage.m                                                    \
+  objectivec/GPBMessage_PackagePrivate.h                                     \
+  objectivec/GPBProtocolBuffers.h                                            \
+  objectivec/GPBProtocolBuffers.m                                            \
+  objectivec/GPBProtocolBuffers_RuntimeSupport.h                             \
+  objectivec/GPBRootObject.h                                                 \
+  objectivec/GPBRootObject.m                                                 \
+  objectivec/GPBRootObject_PackagePrivate.h                                  \
+  objectivec/GPBTypes.h                                                      \
+  objectivec/GPBUnknownFieldSet.h                                            \
+  objectivec/GPBUnknownFieldSet.m                                            \
+  objectivec/GPBUnknownFieldSet_PackagePrivate.h                             \
+  objectivec/GPBUtilities.h                                                  \
+  objectivec/GPBUtilities.m                                                  \
+  objectivec/GPBUtilities_PackagePrivate.h                                   \
+  objectivec/GPBWellKnownTypes.h                                             \
+  objectivec/GPBWellKnownTypes.m                                             \
+  objectivec/GPBWireFormat.h                                                 \
+  objectivec/GPBWireFormat.m                                                 \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj                   \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcbaselines/8BBEA4A5147C727100C4ADB7.xcbaseline/FFE465CA-0E74-40E8-9F09-500B66B7DCB2.plist \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcbaselines/8BBEA4A5147C727100C4ADB7.xcbaseline/Info.plist \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme \
+  objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme \
+  objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj                   \
+  objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata \
+  objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings \
+  objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme \
+  objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme \
+  objectivec/Tests/Filter1.txt                                               \
+  objectivec/Tests/Filter2.txt                                               \
+  objectivec/Tests/golden_message                                            \
+  objectivec/Tests/golden_packed_fields_message                              \
+  objectivec/Tests/GPBARCUnittestProtos.m                                    \
+  objectivec/Tests/GPBArrayTests.m                                           \
+  objectivec/Tests/GPBCodedInputStreamTests.m                                \
+  objectivec/Tests/GPBCodedOuputStreamTests.m                                \
+  objectivec/Tests/GPBConcurrencyTests.m                                     \
+  objectivec/Tests/GPBDescriptorTests.m                                      \
+  objectivec/Tests/GPBDictionaryTests+Bool.m                                 \
+  objectivec/Tests/GPBDictionaryTests+Int32.m                                \
+  objectivec/Tests/GPBDictionaryTests+Int64.m                                \
+  objectivec/Tests/GPBDictionaryTests+String.m                               \
+  objectivec/Tests/GPBDictionaryTests+UInt32.m                               \
+  objectivec/Tests/GPBDictionaryTests+UInt64.m                               \
+  objectivec/Tests/GPBDictionaryTests.pddm                                   \
+  objectivec/Tests/GPBFilteredMessageTests.m                                 \
+  objectivec/Tests/GPBMessageTests+Merge.m                                   \
+  objectivec/Tests/GPBMessageTests+Runtime.m                                 \
+  objectivec/Tests/GPBMessageTests+Serialization.m                           \
+  objectivec/Tests/GPBMessageTests.m                                         \
+  objectivec/Tests/GPBPerfTests.m                                            \
+  objectivec/Tests/GPBStringTests.m                                          \
+  objectivec/Tests/GPBSwiftTests.swift                                       \
+  objectivec/Tests/GPBTestUtilities.h                                        \
+  objectivec/Tests/GPBTestUtilities.m                                        \
+  objectivec/Tests/GPBUnittestProtos.m                                       \
+  objectivec/Tests/GPBUnknownFieldSetTest.m                                  \
+  objectivec/Tests/GPBUtilitiesTests.m                                       \
+  objectivec/Tests/GPBWellKnownTypesTest.m                                   \
+  objectivec/Tests/GPBWireFormatTests.m                                      \
+  objectivec/Tests/iOSTestHarness/AppDelegate.m                              \
+  objectivec/Tests/iOSTestHarness/en.lproj/InfoPlist.strings                 \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/Contents.json \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPad6.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPad6@2x.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPad7.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPad7@2x.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPhone6.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPhone6@2x.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPhone7@2x.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/AppIcon.appiconset/iPhone7@3x.png \
+  objectivec/Tests/iOSTestHarness/Images.xcassets/LaunchImage.launchimage/Contents.json \
+  objectivec/Tests/iOSTestHarness/Info.plist                                  \
+  objectivec/Tests/iOSTestHarness/LaunchScreen.xib                            \
+  objectivec/Tests/text_format_map_unittest_data.txt                          \
+  objectivec/Tests/text_format_unittest_data.txt                              \
+  objectivec/Tests/unittest_cycle.proto                                       \
+  objectivec/Tests/unittest_filter.proto                                      \
+  objectivec/Tests/unittest_name_mangling.proto                               \
+  objectivec/Tests/unittest_objc.proto                                        \
+  objectivec/Tests/unittest_runtime_proto2.proto                              \
+  objectivec/Tests/unittest_runtime_proto3.proto                              \
+  objectivec/Tests/UnitTests-Bridging-Header.h                                \
+  objectivec/Tests/UnitTests-Info.plist
 
 python_EXTRA_DIST=                                                           \
   python/google/protobuf/internal/api_implementation.cc                      \
@@ -301,7 +423,7 @@ ruby_EXTRA_DIST=                                                             \
   ruby/tests/generated_code.rb                                               \
   ruby/tests/generated_code_test.rb
 
-all_EXTRA_DIST=$(java_EXTRA_DIST) $(javanano_EXTRA_DIST) $(python_EXTRA_DIST) $(ruby_EXTRA_DIST)
+all_EXTRA_DIST=$(java_EXTRA_DIST) $(javanano_EXTRA_DIST) $(objectivec_EXTRA_DIST) $(python_EXTRA_DIST) $(ruby_EXTRA_DIST)
 
 EXTRA_DIST = $(@DIST_LANG@_EXTRA_DIST)                                       \
   autogen.sh                                                                 \

+ 120 - 0
conformance/ConformanceJava.java

@@ -0,0 +1,120 @@
+
+import com.google.protobuf.conformance.Conformance;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+class ConformanceJava {
+  private int testCount = 0;
+
+  private boolean readFromStdin(byte[] buf, int len) throws Exception {
+    int ofs = 0;
+    while (len > 0) {
+      int read = System.in.read(buf, ofs, len);
+      if (read == -1) {
+        return false;  // EOF
+      }
+      ofs += read;
+      len -= read;
+    }
+
+    return true;
+  }
+
+  private void writeToStdout(byte[] buf) throws Exception {
+    System.out.write(buf);
+  }
+
+  // Returns -1 on EOF (the actual values will always be positive).
+  private int readLittleEndianIntFromStdin() throws Exception {
+    byte[] buf = new byte[4];
+    if (!readFromStdin(buf, 4)) {
+      return -1;
+    }
+    return buf[0] | (buf[1] << 1) | (buf[2] << 2) | (buf[3] << 3);
+  }
+
+  private void writeLittleEndianIntToStdout(int val) throws Exception {
+    byte[] buf = new byte[4];
+    buf[0] = (byte)val;
+    buf[1] = (byte)(val >> 8);
+    buf[2] = (byte)(val >> 16);
+    buf[3] = (byte)(val >> 24);
+    writeToStdout(buf);
+  }
+
+  private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
+    Conformance.TestAllTypes testMessage;
+
+    switch (request.getPayloadCase()) {
+      case PROTOBUF_PAYLOAD: {
+        try {
+          testMessage = Conformance.TestAllTypes.parseFrom(request.getProtobufPayload());
+        } catch (InvalidProtocolBufferException e) {
+          return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
+        }
+        break;
+      }
+      case JSON_PAYLOAD: {
+        return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build();
+      }
+      case PAYLOAD_NOT_SET: {
+        throw new RuntimeException("Request didn't have payload.");
+      }
+
+      default: {
+        throw new RuntimeException("Unexpected payload case.");
+      }
+    }
+
+    switch (request.getRequestedOutput()) {
+      case UNSPECIFIED:
+        throw new RuntimeException("Unspecified output format.");
+
+      case PROTOBUF:
+        return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build();
+
+      case JSON:
+        return Conformance.ConformanceResponse.newBuilder().setRuntimeError("JSON not yet supported.").build();
+
+      default: {
+        throw new RuntimeException("Unexpected request output.");
+      }
+    }
+  }
+
+  private boolean doTestIo() throws Exception {
+    int bytes = readLittleEndianIntFromStdin();
+
+    if (bytes == -1) {
+      return false;  // EOF
+    }
+
+    byte[] serializedInput = new byte[bytes];
+
+    if (!readFromStdin(serializedInput, bytes)) {
+      throw new RuntimeException("Unexpected EOF from test program.");
+    }
+
+    Conformance.ConformanceRequest request =
+        Conformance.ConformanceRequest.parseFrom(serializedInput);
+    Conformance.ConformanceResponse response = doTest(request);
+    byte[] serializedOutput = response.toByteArray();
+
+    writeLittleEndianIntToStdout(serializedOutput.length);
+    writeToStdout(serializedOutput);
+
+    return true;
+  }
+
+  public void run() throws Exception {
+    while (doTestIo()) {
+      // Empty.
+    }
+
+    System.err.println("ConformanceJava: received EOF from test runner after " +
+        this.testCount + " tests");
+  }
+
+  public static void main(String[] args) throws Exception {
+    new ConformanceJava().run();
+  }
+}

+ 22 - 9
conformance/Makefile.am

@@ -21,30 +21,43 @@ conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src
 
 if USE_EXTERNAL_PROTOC
 
-unittest_proto_middleman: $(protoc_inputs)
-	$(PROTOC) -I$(srcdir) --cpp_out=. $^
-	touch unittest_proto_middleman
+protoc_middleman: $(protoc_inputs)
+	$(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. $^
+	touch protoc_middleman
 
 else
 
 # We have to cd to $(srcdir) before executing protoc because $(protoc_inputs) is
 # relative to srcdir, which may not be the same as the current directory when
 # building out-of-tree.
-unittest_proto_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs)
-	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd $(protoc_inputs) )
-	touch unittest_proto_middleman
+protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs)
+	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd $(protoc_inputs) )
+	touch protoc_middleman
 
 endif
 
-$(protoc_outputs): unittest_proto_middleman
+$(protoc_outputs): protoc_middleman
 
 BUILT_SOURCES = $(protoc_outputs)
 
-CLEANFILES = $(protoc_outputs) unittest_proto_middleman
+CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java
 
 MAINTAINERCLEANFILES =   \
   Makefile.in
 
+javac_middleman: ConformanceJava.java protoc_middleman
+	javac -classpath ../java/target/classes ConformanceJava.java com/google/protobuf/conformance/Conformance.java
+	@touch javac_middleman
+
+conformance-java: javac_middleman
+	@echo "Writing shortcut script conformance-java..."
+	@echo '#! /bin/sh' > conformance-java
+	@echo 'java -classpath .:../java/target/classes ConformanceJava "$$@"' >> conformance-java
+	@chmod +x conformance-java
+
 # Targets for actually running tests.
-test_cpp: unittest_proto_middleman conformance-test-runner conformance-cpp
+test_cpp: protoc_middleman conformance-test-runner conformance-cpp
 	./conformance-test-runner ./conformance-cpp
+
+test_java: protoc_middleman conformance-test-runner conformance-java
+	./conformance-test-runner ./conformance-java

+ 1 - 0
conformance/conformance.proto

@@ -30,6 +30,7 @@
 
 syntax = "proto3";
 package conformance;
+option java_package = "com.google.protobuf.conformance";
 
 // This defines the conformance testing protocol.  This protocol exists between
 // the conformance test suite itself and the code being tested.  For each test,

+ 2 - 2
conformance/conformance_test_runner.cc

@@ -48,9 +48,9 @@
 // Every test consists of a ConformanceRequest/ConformanceResponse
 // request/reply pair.  The protocol on the pipe is simply:
 //
-//   1. tester sends 4-byte length N
+//   1. tester sends 4-byte length N (little endian)
 //   2. tester sends N bytes representing a ConformanceRequest proto
-//   3. testee sends 4-byte length M
+//   3. testee sends 4-byte length M (little endian)
 //   4. testee sends M bytes representing a ConformanceResponse proto
 
 #include <errno.h>

+ 2 - 2
csharp/src/ProtocolBuffers.Test/TestProtos/UnittestDropUnknownFields.cs

@@ -40,8 +40,8 @@ namespace Google.ProtocolBuffers.TestProtos {
             "X3ZhbHVlGAIgASgOMjsudW5pdHRlc3RfZHJvcF91bmtub3duX2ZpZWxkcy5G", 
             "b29XaXRoRXh0cmFGaWVsZHMuTmVzdGVkRW51bRIZChFleHRyYV9pbnQzMl92", 
             "YWx1ZRgDIAEoBSIwCgpOZXN0ZWRFbnVtEgcKA0ZPTxAAEgcKA0JBUhABEgcK", 
-            "A0JBWhACEgcKA1FVWBADQiSqAiFHb29nbGUuUHJvdG9jb2xCdWZmZXJzLlRl", 
-          "c3RQcm90b3NiBnByb3RvMw=="));
+            "A0JBWhACEgcKA1FVWBADQjOiAgxEcm9wVW5rbm93bnOqAiFHb29nbGUuUHJv", 
+          "dG9jb2xCdWZmZXJzLlRlc3RQcm90b3NiBnByb3RvMw=="));
       pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
         descriptor = root;
         internal__static_unittest_drop_unknown_fields_Foo__Descriptor = Descriptor.MessageTypes[0];

+ 1 - 1
examples/add_person.py

@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#! /usr/bin/env python
 
 # See README.txt for information and build instructions.
 

+ 1 - 1
examples/list_people.py

@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#! /usr/bin/env python
 
 # See README.txt for information and build instructions.
 

+ 1 - 0
generate_descriptor_proto.sh

@@ -27,6 +27,7 @@ __EOF__
 fi
 
 cd src
+make $@ google/protobuf/stubs/pbconfig.h
 CORE_PROTO_IS_CORRECT=0
 while [ $CORE_PROTO_IS_CORRECT -ne 1 ]
 do

+ 23 - 0
javanano/README.md

@@ -145,6 +145,7 @@ optional_field_style   -> default or accessors
 enum_style             -> c or java
 ignore_services        -> true or false
 parcelable_messages    -> true or false
+generate_intdefs       -> true or false
 ```
 
 **java_package=\<file-name\>|\<package-name\>** (no default)
@@ -302,6 +303,28 @@ parcelable_messages    -> true or false
 
   Android-specific option to generate Parcelable messages.
 
+**generate_intdefs={true,false}** (default: false)
+  Android-specific option to generate @IntDef annotations for enums.
+
+  If turned on, an '@IntDef' annotation (a public @interface) will be
+  generated for each enum, and every integer parameter and return
+  value in the generated code meant for this enum will be annotated
+  with it. This interface is generated with the same name and at the
+  same place as the enum members' container interfaces described
+  above under 'enum_style=java', regardless of the enum_style option
+  used. When this is combined with enum_style=java, the interface
+  will be both the '@IntDef' annotation and the container of the enum
+  members; otherwise the interface has an empty body.
+
+  Your app must declare a compile-time dependency on the
+  android-support-annotations library.
+
+  For more information on how these @IntDef annotations help with
+  compile-time type safety, see:
+  https://sites.google.com/a/android.com/tools/tech-docs/support-annotations
+  and
+  https://developer.android.com/reference/android/support/annotation/IntDef.html
+
 
 To use nano protobufs within the Android repo:
 ----------------------------------------------

+ 12 - 3
javanano/pom.xml

@@ -97,19 +97,19 @@
                   <arg value="src/test/java/com/google/protobuf/nano/map_test.proto" />
                 </exec>
                 <exec executable="../src/protoc">
-                  <arg value="--javanano_out=store_unknown_fields=true,generate_equals=true:target/generated-test-sources" />
+                  <arg value="--javanano_out=store_unknown_fields=true,generate_equals=true,generate_clone=true:target/generated-test-sources" />
                   <arg value="--proto_path=src/test/java/com" />
                   <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto" />
                   <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto" />
                   <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" />
                 </exec>
                 <exec executable="../src/protoc">
-                  <arg value="--javanano_out=store_unknown_fields=true:target/generated-test-sources" />
+                  <arg value="--javanano_out=store_unknown_fields=true,generate_clone=true:target/generated-test-sources" />
                   <arg value="--proto_path=src/test/java/com" />
                   <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" />
                 </exec>
                 <exec executable="../src/protoc">
-                  <arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true:target/generated-test-sources" />
+                  <arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true,generate_clone=true:target/generated-test-sources" />
                   <arg value="--proto_path=src/test/java/com" />
                   <arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" />
                 </exec>
@@ -139,6 +139,15 @@
                   <arg value="--proto_path=src/test/java/com" />
                   <arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" />
                 </exec>
+                <exec executable="../src/protoc">
+                  <arg value="--javanano_out=
+                                  optional_field_style=reftypes_compat_mode,
+                                  generate_equals=true,
+                                  java_outer_classname=google/protobuf/nano/unittest_reference_types_nano.proto|NanoReferenceTypesCompat
+                                  :target/generated-test-sources" />
+                  <arg value="--proto_path=src/test/java/com" />
+                  <arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" />
+                </exec>
               </tasks>
               <testSourceRoot>target/generated-test-sources</testSourceRoot>
             </configuration>

+ 2 - 0
javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java

@@ -236,6 +236,8 @@ public final class CodedInputByteBufferNano {
       System.arraycopy(buffer, bufferPos, result, 0, size);
       bufferPos += size;
       return result;
+    } else if (size == 0) {
+      return WireFormatNano.EMPTY_BYTES;
     } else {
       // Slow path:  Build a byte array first then copy it.
       return readRawBytes(size);

+ 241 - 23
javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java

@@ -31,6 +31,9 @@
 package com.google.protobuf.nano;
 
 import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
 
 /**
  * Encodes and writes protocol message fields.
@@ -47,15 +50,17 @@ import java.io.IOException;
  * @author kneton@google.com Kenton Varda
  */
 public final class CodedOutputByteBufferNano {
-  private final byte[] buffer;
-  private final int limit;
-  private int position;
+  /* max bytes per java UTF-16 char in UTF-8 */
+  private static final int MAX_UTF8_EXPANSION = 3;
+  private final ByteBuffer buffer;
 
   private CodedOutputByteBufferNano(final byte[] buffer, final int offset,
                             final int length) {
+    this(ByteBuffer.wrap(buffer, offset, length));
+  }
+
+  private CodedOutputByteBufferNano(final ByteBuffer buffer) {
     this.buffer = buffer;
-    position = offset;
-    limit = offset + length;
   }
 
   /**
@@ -287,14 +292,213 @@ public final class CodedOutputByteBufferNano {
 
   /** Write a {@code string} field to the stream. */
   public void writeStringNoTag(final String value) throws IOException {
-    // Unfortunately there does not appear to be any way to tell Java to encode
-    // UTF-8 directly into our buffer, so we have to let it create its own byte
-    // array and then copy.
-    final byte[] bytes = value.getBytes(InternalNano.UTF_8);
-    writeRawVarint32(bytes.length);
-    writeRawBytes(bytes);
+    // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
+    // and at most 3 times of it. Optimize for the case where we know this length results in a
+    // constant varint length - saves measuring length of the string.
+    try {
+      final int minLengthVarIntSize = computeRawVarint32Size(value.length());
+      final int maxLengthVarIntSize = computeRawVarint32Size(value.length() * MAX_UTF8_EXPANSION);
+      if (minLengthVarIntSize == maxLengthVarIntSize) {
+        int oldPosition = buffer.position();
+        // Buffer.position, when passed a position that is past its limit, throws
+        // IllegalArgumentException, and this class is documented to throw
+        // OutOfSpaceException instead.
+        if (buffer.remaining() < minLengthVarIntSize) {
+          throw new OutOfSpaceException(oldPosition + minLengthVarIntSize, buffer.limit());
+        }
+        buffer.position(oldPosition + minLengthVarIntSize);
+        encode(value, buffer);
+        int newPosition = buffer.position();
+        buffer.position(oldPosition);
+        writeRawVarint32(newPosition - oldPosition - minLengthVarIntSize);
+        buffer.position(newPosition);
+      } else {
+        writeRawVarint32(encodedLength(value));
+        encode(value, buffer);
+      }
+    } catch (BufferOverflowException e) {
+      final OutOfSpaceException outOfSpaceException = new OutOfSpaceException(buffer.position(),
+          buffer.limit());
+      outOfSpaceException.initCause(e);
+      throw outOfSpaceException;
+    }
+  }
+
+  // These UTF-8 handling methods are copied from Guava's Utf8 class.
+  /**
+   * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
+   * this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
+   * both time and space.
+   *
+   * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
+   *     surrogates)
+   */
+  private static int encodedLength(CharSequence sequence) {
+    // Warning to maintainers: this implementation is highly optimized.
+    int utf16Length = sequence.length();
+    int utf8Length = utf16Length;
+    int i = 0;
+
+    // This loop optimizes for pure ASCII.
+    while (i < utf16Length && sequence.charAt(i) < 0x80) {
+      i++;
+    }
+
+    // This loop optimizes for chars less than 0x800.
+    for (; i < utf16Length; i++) {
+      char c = sequence.charAt(i);
+      if (c < 0x800) {
+        utf8Length += ((0x7f - c) >>> 31);  // branch free!
+      } else {
+        utf8Length += encodedLengthGeneral(sequence, i);
+        break;
+      }
+    }
+
+    if (utf8Length < utf16Length) {
+      // Necessary and sufficient condition for overflow because of maximum 3x expansion
+      throw new IllegalArgumentException("UTF-8 length does not fit in int: "
+              + (utf8Length + (1L << 32)));
+    }
+    return utf8Length;
+  }
+
+  private static int encodedLengthGeneral(CharSequence sequence, int start) {
+    int utf16Length = sequence.length();
+    int utf8Length = 0;
+    for (int i = start; i < utf16Length; i++) {
+      char c = sequence.charAt(i);
+      if (c < 0x800) {
+        utf8Length += (0x7f - c) >>> 31; // branch free!
+      } else {
+        utf8Length += 2;
+        // jdk7+: if (Character.isSurrogate(c)) {
+        if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) {
+          // Check that we have a well-formed surrogate pair.
+          int cp = Character.codePointAt(sequence, i);
+          if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+            throw new IllegalArgumentException("Unpaired surrogate at index " + i);
+          }
+          i++;
+        }
+      }
+    }
+    return utf8Length;
   }
 
+  /**
+   * Encodes {@code sequence} into UTF-8, in {@code byteBuffer}. For a string, this method is
+   * equivalent to {@code buffer.put(string.getBytes(UTF_8))}, but is more efficient in both time
+   * and space. Bytes are written starting at the current position. This method requires paired
+   * surrogates, and therefore does not support chunking.
+   *
+   * <p>To ensure sufficient space in the output buffer, either call {@link #encodedLength} to
+   * compute the exact amount needed, or leave room for {@code 3 * sequence.length()}, which is the
+   * largest possible number of bytes that any input can be encoded to.
+   *
+   * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
+   *     surrogates)
+   * @throws BufferOverflowException if {@code sequence} encoded in UTF-8 does not fit in
+   *     {@code byteBuffer}'s remaining space.
+   * @throws ReadOnlyBufferException if {@code byteBuffer} is a read-only buffer.
+   */
+  private static void encode(CharSequence sequence, ByteBuffer byteBuffer) {
+    if (byteBuffer.isReadOnly()) {
+      throw new ReadOnlyBufferException();
+    } else if (byteBuffer.hasArray()) {
+      try {
+        int encoded = encode(sequence,
+                byteBuffer.array(),
+                byteBuffer.arrayOffset() + byteBuffer.position(),
+                byteBuffer.remaining());
+        byteBuffer.position(encoded - byteBuffer.arrayOffset());
+      } catch (ArrayIndexOutOfBoundsException e) {
+        BufferOverflowException boe = new BufferOverflowException();
+        boe.initCause(e);
+        throw boe;
+      }
+    } else {
+      encodeDirect(sequence, byteBuffer);
+    }
+  }
+
+  private static void encodeDirect(CharSequence sequence, ByteBuffer byteBuffer) {
+    int utf16Length = sequence.length();
+    for (int i = 0; i < utf16Length; i++) {
+      final char c = sequence.charAt(i);
+      if (c < 0x80) { // ASCII
+        byteBuffer.put((byte) c);
+      } else if (c < 0x800) { // 11 bits, two UTF-8 bytes
+        byteBuffer.put((byte) ((0xF << 6) | (c >>> 6)));
+        byteBuffer.put((byte) (0x80 | (0x3F & c)));
+      } else if (c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) {
+        // Maximium single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+        byteBuffer.put((byte) ((0xF << 5) | (c >>> 12)));
+        byteBuffer.put((byte) (0x80 | (0x3F & (c >>> 6))));
+        byteBuffer.put((byte) (0x80 | (0x3F & c)));
+      } else {
+        final char low;
+        if (i + 1 == sequence.length()
+                || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) {
+          throw new IllegalArgumentException("Unpaired surrogate at index " + (i - 1));
+        }
+        int codePoint = Character.toCodePoint(c, low);
+        byteBuffer.put((byte) ((0xF << 4) | (codePoint >>> 18)));
+        byteBuffer.put((byte) (0x80 | (0x3F & (codePoint >>> 12))));
+        byteBuffer.put((byte) (0x80 | (0x3F & (codePoint >>> 6))));
+        byteBuffer.put((byte) (0x80 | (0x3F & codePoint)));
+      }
+    }
+  }
+
+  private static int encode(CharSequence sequence, byte[] bytes, int offset, int length) {
+    int utf16Length = sequence.length();
+    int j = offset;
+    int i = 0;
+    int limit = offset + length;
+    // Designed to take advantage of
+    // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+    for (char c; i < utf16Length && i + j < limit && (c = sequence.charAt(i)) < 0x80; i++) {
+      bytes[j + i] = (byte) c;
+    }
+    if (i == utf16Length) {
+      return j + utf16Length;
+    }
+    j += i;
+    for (char c; i < utf16Length; i++) {
+      c = sequence.charAt(i);
+      if (c < 0x80 && j < limit) {
+        bytes[j++] = (byte) c;
+      } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
+        bytes[j++] = (byte) ((0xF << 6) | (c >>> 6));
+        bytes[j++] = (byte) (0x80 | (0x3F & c));
+      } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) {
+        // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+        bytes[j++] = (byte) ((0xF << 5) | (c >>> 12));
+        bytes[j++] = (byte) (0x80 | (0x3F & (c >>> 6)));
+        bytes[j++] = (byte) (0x80 | (0x3F & c));
+      } else if (j <= limit - 4) {
+        // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 bytes
+        final char low;
+        if (i + 1 == sequence.length()
+                || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) {
+          throw new IllegalArgumentException("Unpaired surrogate at index " + (i - 1));
+        }
+        int codePoint = Character.toCodePoint(c, low);
+        bytes[j++] = (byte) ((0xF << 4) | (codePoint >>> 18));
+        bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
+        bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
+        bytes[j++] = (byte) (0x80 | (0x3F & codePoint));
+      } else {
+        throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
+      }
+    }
+    return j;
+  }
+
+  // End guava UTF-8 methods
+
+
   /** Write a {@code group} field to the stream. */
   public void writeGroupNoTag(final MessageNano value) throws IOException {
     value.writeTo(this);
@@ -602,9 +806,8 @@ public final class CodedOutputByteBufferNano {
    * {@code string} field.
    */
   public static int computeStringSizeNoTag(final String value) {
-    final byte[] bytes = value.getBytes(InternalNano.UTF_8);
-    return computeRawVarint32Size(bytes.length) +
-           bytes.length;
+    final int length = encodedLength(value);
+    return computeRawVarint32Size(length) + length;
   }
 
   /**
@@ -687,7 +890,7 @@ public final class CodedOutputByteBufferNano {
    * Otherwise, throws {@code UnsupportedOperationException}.
    */
   public int spaceLeft() {
-    return limit - position;
+    return buffer.remaining();
   }
 
   /**
@@ -704,6 +907,23 @@ public final class CodedOutputByteBufferNano {
     }
   }
 
+  /**
+   * Returns the position within the internal buffer.
+   */
+  public int position() {
+    return buffer.position();
+  }
+
+  /**
+   * Resets the position within the internal buffer to zero.
+   *
+   * @see #position
+   * @see #spaceLeft
+   */
+  public void reset() {
+    buffer.clear();
+  }
+
   /**
    * If you create a CodedOutputStream around a simple flat array, you must
    * not attempt to write more bytes than the array has space.  Otherwise,
@@ -720,12 +940,12 @@ public final class CodedOutputByteBufferNano {
 
   /** Write a single byte. */
   public void writeRawByte(final byte value) throws IOException {
-    if (position == limit) {
+    if (!buffer.hasRemaining()) {
       // We're writing to a single buffer.
-      throw new OutOfSpaceException(position, limit);
+      throw new OutOfSpaceException(buffer.position(), buffer.limit());
     }
 
-    buffer[position++] = value;
+    buffer.put(value);
   }
 
   /** Write a single byte, represented by an integer value. */
@@ -741,13 +961,11 @@ public final class CodedOutputByteBufferNano {
   /** Write part of an array of bytes. */
   public void writeRawBytes(final byte[] value, int offset, int length)
                             throws IOException {
-    if (limit - position >= length) {
-      // We have room in the current buffer.
-      System.arraycopy(value, offset, buffer, position, length);
-      position += length;
+    if (buffer.remaining() >= length) {
+      buffer.put(value, offset, length);
     } else {
       // We're writing to a single buffer.
-      throw new OutOfSpaceException(position, limit);
+      throw new OutOfSpaceException(buffer.position(), buffer.limit());
     }
   }
 

+ 5 - 23
javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java

@@ -160,28 +160,10 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
         return true;
     }
 
-    /**
-     * Returns whether the stored unknown field data in this message is equivalent to that in the
-     * other message.
-     *
-     * @param other the other message.
-     * @return whether the two sets of unknown field data are equal.
-     */
-    protected final boolean unknownFieldDataEquals(M other) {
-        if (unknownFieldData == null || unknownFieldData.isEmpty()) {
-            return other.unknownFieldData == null || other.unknownFieldData.isEmpty();
-        } else {
-            return unknownFieldData.equals(other.unknownFieldData);
-        }
-    }
-
-    /**
-     * Computes the hashcode representing the unknown field data stored in this message.
-     *
-     * @return the hashcode for the unknown field data.
-     */
-    protected final int unknownFieldDataHashCode() {
-        return (unknownFieldData == null || unknownFieldData.isEmpty()
-                ? 0 : unknownFieldData.hashCode());
+    @Override
+    public M clone() throws CloneNotSupportedException {
+        M cloned = (M) super.clone();
+        InternalNano.cloneUnknownFieldData(this, cloned);
+        return cloned;
     }
 }

+ 26 - 7
javanano/src/main/java/com/google/protobuf/nano/Extension.java

@@ -79,12 +79,30 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
      * Should be used by the generated code only.
      *
      * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
+     * @deprecated use {@link #createMessageTyped(int, Class, long)} instead.
      */
+    @Deprecated
     public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
             Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
         return new Extension<M, T>(type, clazz, tag, false);
     }
 
+    // Note: these create...() methods take a long for the tag parameter,
+    // because tags are represented as unsigned ints, and these values exist
+    // in generated code as long values. However, they can fit in 32-bits, so
+    // it's safe to cast them to int without loss of precision.
+
+    /**
+     * Creates an {@code Extension} of the given message type and tag number.
+     * Should be used by the generated code only.
+     *
+     * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
+     */
+    public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
+            Extension<M, T> createMessageTyped(int type, Class<T> clazz, long tag) {
+        return new Extension<M, T>(type, clazz, (int) tag, false);
+    }
+
     /**
      * Creates a repeated {@code Extension} of the given message type and tag number.
      * Should be used by the generated code only.
@@ -92,8 +110,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
      * @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);
+            Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, long tag) {
+        return new Extension<M, T[]>(type, clazz, (int) tag, true);
     }
 
     /**
@@ -104,8 +122,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
      * @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);
+            Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, long tag) {
+        return new PrimitiveExtension<M, T>(type, clazz, (int) tag, false, 0, 0);
     }
 
     /**
@@ -117,8 +135,9 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
      */
     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);
+                    int type, Class<T> clazz, long tag, long nonPackedTag, long packedTag) {
+        return new PrimitiveExtension<M, T>(type, clazz, (int) tag, true,
+            (int) nonPackedTag, (int) packedTag);
     }
 
     /**
@@ -136,7 +155,7 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
     protected final Class<T> clazz;
 
     /**
-     * Tag number of this extension.
+     * Tag number of this extension. The data should be viewed as an unsigned 32-bit value.
      */
     public final int tag;
 

+ 26 - 8
javanano/src/main/java/com/google/protobuf/nano/FieldArray.java

@@ -35,9 +35,12 @@ package com.google.protobuf.nano;
  * A custom version of {@link android.util.SparseArray} with the minimal API
  * for storing {@link FieldData} objects.
  *
+ * <p>This class is an internal implementation detail of nano and should not
+ * be called directly by clients.
+ *
  * Based on {@link android.support.v4.util.SpareArrayCompat}.
  */
-class FieldArray {
+public final class FieldArray implements Cloneable {
     private static final FieldData DELETED = new FieldData();
     private boolean mGarbage = false;
 
@@ -48,7 +51,7 @@ class FieldArray {
     /**
      * Creates a new FieldArray containing no fields.
      */
-    public FieldArray() {
+    FieldArray() {
         this(10);
     }
 
@@ -57,7 +60,7 @@ class FieldArray {
      * require any additional memory allocation to store the specified
      * number of mappings.
      */
-    public FieldArray(int initialCapacity) {
+    FieldArray(int initialCapacity) {
         initialCapacity = idealIntArraySize(initialCapacity);
         mFieldNumbers = new int[initialCapacity];
         mData = new FieldData[initialCapacity];
@@ -68,7 +71,7 @@ class FieldArray {
      * Gets the FieldData mapped from the specified fieldNumber, or <code>null</code>
      * if no such mapping has been made.
      */
-    public FieldData get(int fieldNumber) {
+    FieldData get(int fieldNumber) {
         int i = binarySearch(fieldNumber);
 
         if (i < 0 || mData[i] == DELETED) {
@@ -81,7 +84,7 @@ class FieldArray {
     /**
      * Removes the data from the specified fieldNumber, if there was any.
      */
-    public void remove(int fieldNumber) {
+    void remove(int fieldNumber) {
         int i = binarySearch(fieldNumber);
 
         if (i >= 0 && mData[i] != DELETED) {
@@ -118,7 +121,7 @@ class FieldArray {
      * Adds a mapping from the specified fieldNumber to the specified data,
      * replacing the previous mapping if there was one.
      */
-    public void put(int fieldNumber, FieldData data) {
+    void put(int fieldNumber, FieldData data) {
         int i = binarySearch(fieldNumber);
 
         if (i >= 0) {
@@ -167,7 +170,7 @@ class FieldArray {
      * Returns the number of key-value mappings that this FieldArray
      * currently stores.
      */
-    public int size() {
+    int size() {
         if (mGarbage) {
             gc();
         }
@@ -184,7 +187,7 @@ class FieldArray {
      * the value from the <code>index</code>th key-value mapping that this
      * FieldArray stores.
      */
-    public FieldData dataAt(int index) {
+    FieldData dataAt(int index) {
         if (mGarbage) {
             gc();
         }
@@ -270,4 +273,19 @@ class FieldArray {
         }
         return true;
     }
+
+    @Override
+    public final FieldArray clone() {
+        // Trigger GC so we compact and don't copy DELETED elements.
+        int size = size();
+        FieldArray clone = new FieldArray(size);
+        System.arraycopy(mFieldNumbers, 0, clone.mFieldNumbers, 0, size);
+        for (int i = 0; i < size; i++) {
+            if (mData[i] != null) {
+                clone.mData[i] = mData[i].clone();
+            }
+        }
+        clone.mSize = size;
+        return clone;
+    }
 }

+ 51 - 1
javanano/src/main/java/com/google/protobuf/nano/FieldData.java

@@ -39,7 +39,7 @@ import java.util.List;
  * Stores unknown fields. These might be extensions or fields that the generated API doesn't
  * know about yet.
  */
-class FieldData {
+class FieldData implements Cloneable {
     private Extension<?, ?> cachedExtension;
     private Object value;
     /** The serialised values for this object. Will be cleared if getValue is called */
@@ -187,4 +187,54 @@ class FieldData {
         return result;
     }
 
+    @Override
+    public final FieldData clone() {
+        FieldData clone = new FieldData();
+        try {
+            clone.cachedExtension = cachedExtension;
+            if (unknownFieldData == null) {
+                clone.unknownFieldData = null;
+            } else {
+                clone.unknownFieldData.addAll(unknownFieldData);
+            }
+
+            // Whether we need to deep clone value depends on its type. Primitive reference types
+            // (e.g. Integer, Long etc.) are ok, since they're immutable. We need to clone arrays
+            // and messages.
+            if (value == null) {
+                // No cloning required.
+            } else if (value instanceof MessageNano) {
+                clone.value = ((MessageNano) value).clone();
+            } else if (value instanceof byte[]) {
+                clone.value = ((byte[]) value).clone();
+            } else if (value instanceof byte[][]) {
+                byte[][] valueArray = (byte[][]) value;
+                byte[][] cloneArray = new byte[valueArray.length][];
+                clone.value = cloneArray;
+                for (int i = 0; i < valueArray.length; i++) {
+                    cloneArray[i] = valueArray[i].clone();
+                }
+            } else if (value instanceof boolean[]) {
+                clone.value = ((boolean[]) value).clone();
+            } else if (value instanceof int[]) {
+                clone.value = ((int[]) value).clone();
+            } else if (value instanceof long[]) {
+                clone.value = ((long[]) value).clone();
+            } else if (value instanceof float[]) {
+                clone.value = ((float[]) value).clone();
+            } else if (value instanceof double[]) {
+                clone.value = ((double[]) value).clone();
+            } else if (value instanceof MessageNano[]) {
+                MessageNano[] valueArray = (MessageNano[]) value;
+                MessageNano[] cloneArray = new MessageNano[valueArray.length];
+                clone.value = cloneArray;
+                for (int i = 0; i < valueArray.length; i++) {
+                    cloneArray[i] = valueArray[i].clone();
+                }
+            }
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(e);
+        }
+    }
 }

+ 8 - 0
javanano/src/main/java/com/google/protobuf/nano/InternalNano.java

@@ -536,4 +536,12 @@ public final class InternalNano {
     }
     return o.hashCode();
   }
+
+  // This avoids having to make FieldArray public.
+  public static void cloneUnknownFieldData(ExtendableMessageNano original,
+      ExtendableMessageNano cloned) {
+    if (original.unknownFieldData != null) {
+      cloned.unknownFieldData = (FieldArray) original.unknownFieldData.clone();
+    }
+  }
 }

+ 8 - 0
javanano/src/main/java/com/google/protobuf/nano/MessageNano.java

@@ -187,4 +187,12 @@ public abstract class MessageNano {
     public String toString() {
         return MessageNanoPrinter.print(this);
     }
+
+    /**
+     * Provides support for cloning. This only works if you specify the generate_clone method.
+     */
+    @Override
+    public MessageNano clone() throws CloneNotSupportedException {
+        return (MessageNano) super.clone();
+    }
 }

+ 4 - 0
javanano/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java

@@ -109,6 +109,10 @@ public final class MessageNanoPrinter {
             for (Field field : clazz.getFields()) {
                 int modifiers = field.getModifiers();
                 String fieldName = field.getName();
+                if ("cachedSize".equals(fieldName)) {
+                    // TODO(bduff): perhaps cachedSize should have a more obscure name.
+                    continue;
+                }
 
                 if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC
                         && (modifiers & Modifier.STATIC) != Modifier.STATIC

+ 4 - 0
javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java

@@ -42,6 +42,10 @@ import java.util.Arrays;
 final class UnknownFieldData {
 
     final int tag;
+    /**
+     * Important: this should be treated as immutable, even though it's possible
+     * to change the array values.
+     */
     final byte[] bytes;
 
     UnknownFieldData(int tag, byte[] bytes) {

+ 80 - 0
javanano/src/test/java/com/google/protobuf/nano/NanoTest.java

@@ -31,11 +31,13 @@
 package com.google.protobuf.nano;
 
 import com.google.protobuf.nano.MapTestProto.TestMap;
+import com.google.protobuf.nano.CodedOutputByteBufferNano;
 import com.google.protobuf.nano.MapTestProto.TestMap.MessageValue;
 import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors;
 import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
 import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
 import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano;
+import com.google.protobuf.nano.NanoReferenceTypesCompat;
 import com.google.protobuf.nano.UnittestSimpleNano.SimpleMessageNano;
 import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
 import com.google.protobuf.nano.testext.Extensions;
@@ -2300,6 +2302,59 @@ public class NanoTest extends TestCase {
     }
   }
 
+  public void testDifferentStringLengthsNano() throws Exception {
+    // Test string serialization roundtrip using strings of the following lengths,
+    // with ASCII and Unicode characters requiring different UTF-8 byte counts per
+    // char, hence causing the length delimiter varint to sometimes require more
+    // bytes for the Unicode strings than the ASCII string of the same length.
+    int[] lengths = new int[] {
+            0,
+            1,
+            (1 << 4) - 1,  // 1 byte for ASCII and Unicode
+            (1 << 7) - 1,  // 1 byte for ASCII, 2 bytes for Unicode
+            (1 << 11) - 1, // 2 bytes for ASCII and Unicode
+            (1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode
+            (1 << 17) - 1, // 3 bytes for ASCII and Unicode
+    };
+    for (int i : lengths) {
+      testEncodingOfString('q', i);      // 1 byte per char
+      testEncodingOfString('\u07FF', i); // 2 bytes per char
+      testEncodingOfString('\u0981', i); // 3 bytes per char
+    }
+  }
+
+  /** Regression test for https://github.com/google/protobuf/issues/292 */
+  public void testCorrectExceptionThrowWhenEncodingStringsWithoutEnoughSpace() throws Exception {
+    String testCase = "Foooooooo";
+    assertEquals(CodedOutputByteBufferNano.computeRawVarint32Size(testCase.length()),
+            CodedOutputByteBufferNano.computeRawVarint32Size(testCase.length() * 3));
+    assertEquals(11, CodedOutputByteBufferNano.computeStringSize(1, testCase));
+    // Tag is one byte, varint describing string length is 1 byte, string length is 9 bytes.
+    // An array of size 1 will cause a failure when trying to write the varint.
+    for (int i = 0; i < 11; i++) {
+      CodedOutputByteBufferNano bufferNano = CodedOutputByteBufferNano.newInstance(new byte[i]);
+      try {
+        bufferNano.writeString(1, testCase);
+        fail("Should have thrown an out of space exception");
+      } catch (CodedOutputByteBufferNano.OutOfSpaceException expected) {}
+    }
+  }
+
+  private void testEncodingOfString(char c, int length) throws InvalidProtocolBufferNanoException {
+    TestAllTypesNano testAllTypesNano = new TestAllTypesNano();
+    final String fullString = fullString(c, length);
+    testAllTypesNano.optionalString = fullString;
+    final TestAllTypesNano resultNano = new TestAllTypesNano();
+    MessageNano.mergeFrom(resultNano, MessageNano.toByteArray(testAllTypesNano));
+    assertEquals(fullString, resultNano.optionalString);
+  }
+
+  private String fullString(char c, int length) {
+    char[] result = new char[length];
+    Arrays.fill(result, c);
+    return new String(result);
+  }
+
   public void testNanoWithHasParseFrom() throws Exception {
     TestAllTypesNanoHas msg = null;
     // Test false on creation, after clear and upon empty parse.
@@ -2986,6 +3041,10 @@ public class NanoTest extends TestCase {
     assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat)));
     assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble)));
     assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum)));
+
+    // Clone the message and ensure it's still equal.
+    Extensions.ExtendableMessage clone = message.clone();
+    assertEquals(clone, message);
   }
 
   public void testNullExtensions() throws Exception {
@@ -4345,6 +4404,11 @@ public class NanoTest extends TestCase {
     assertMapSet(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
   }
 
+  public void testRepeatedFieldInitializedInReftypesCompatMode() {
+    NanoReferenceTypesCompat.TestAllTypesNano proto = new NanoReferenceTypesCompat.TestAllTypesNano();
+    assertNotNull(proto.repeatedString);
+  }
+
   private void assertRepeatedPackablesEqual(
       NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) {
     // Not using MessageNano.equals() -- that belongs to a separate test.
@@ -4364,6 +4428,22 @@ public class NanoTest extends TestCase {
     assertTrue(Arrays.equals(nonPacked.enums, packed.enums));
   }
 
+  public void testClone() throws Exception {
+    // A simple message.
+    AnotherMessage anotherMessage = new AnotherMessage();
+    anotherMessage.string = "Hello";
+    anotherMessage.value = true;
+    anotherMessage.integers = new int[] { 1, 2, 3 };
+
+    AnotherMessage clone = anotherMessage.clone();
+    assertEquals(clone, anotherMessage);
+
+    // Verify it was a deep clone - changes to the clone shouldn't affect the
+    // original.
+    clone.integers[1] = 100;
+    assertFalse(clone.equals(anotherMessage));
+  }
+
   private void assertHasWireData(MessageNano message, boolean expected) {
     byte[] bytes = MessageNano.toByteArray(message);
     int wireLength = bytes.length;

+ 4 - 0
javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto

@@ -16,11 +16,15 @@ enum AnEnum {
 message AnotherMessage {
   optional string string = 1;
   optional bool value = 2;
+  repeated int32 integers = 3;
 }
 
 message ContainerMessage {
   extend ExtendableMessage {
     optional bool another_thing = 100;
+    // The largest permitted field number, per
+    // https://developers.google.com/protocol-buffers/docs/proto#simple
+    optional bool large_field_number = 536870911;
   }
 }
 

+ 56 - 0
objectivec/DevTools/check_version_stamps.sh

@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# This script checks that the runtime version number constant in the compiler
+# source and in the runtime source is the same.
+#
+# A distro can be made of the protobuf sources with only a subset of the
+# languages, so if the compiler depended on the Objective C runtime, those
+# builds would break. At the same time, we don't want the runtime source
+# depending on the compiler sources; so two copies of the constant are needed.
+
+set -eu
+
+readonly ScriptDir=$(dirname "$(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")")
+readonly ProtoRootDir="${ScriptDir}/../.."
+
+die() {
+    echo "Error: $1"
+    exit 1
+}
+
+readonly ConstantName=GOOGLE_PROTOBUF_OBJC_GEN_VERSION
+
+# Collect version from plugin sources.
+
+readonly PluginSrc="${ProtoRootDir}/src/google/protobuf/compiler/objectivec/objectivec_file.cc"
+readonly PluginVersion=$( \
+    cat "${PluginSrc}" \
+        | sed -n -e "s:const int32_t ${ConstantName} = \([0-9]*\);:\1:p"
+)
+
+if [[ -z "${PluginVersion}" ]] ; then
+    die "Failed to fine ${ConstantName} in the plugin source (${PluginSrc})."
+fi
+
+# Collect version from runtime sources.
+
+readonly RuntimeSrc="${ProtoRootDir}/objectivec/GPBBootstrap.h"
+readonly RuntimeVersion=$( \
+    cat "${RuntimeSrc}" \
+        | sed -n -e "s:#define ${ConstantName} \([0-9]*\):\1:p"
+)
+
+if [[ -z "${RuntimeVersion}" ]] ; then
+    die "Failed to fine ${ConstantName} in the runtime source (${RuntimeSrc})."
+fi
+
+# Compare them.
+
+if [[ "${PluginVersion}" != "${RuntimeVersion}" ]] ; then
+    die "Versions don't match!
+   Plugin: ${PluginVersion} from ${PluginSrc}
+  Runtime: ${RuntimeVersion} from ${RuntimeSrc}
+"
+fi
+
+# Success

+ 36 - 0
objectivec/DevTools/generate_descriptors_proto.sh

@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# This script will generate the common descriptors needed by the Objective C
+# runtime.
+
+# HINT:  Flags passed to generate_descriptor_proto.sh will be passed directly
+#   to make when building protoc.  This is particularly useful for passing
+#   -j4 to run 4 jobs simultaneously.
+
+set -eu
+
+readonly ScriptDir=$(dirname "$(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")")
+readonly ProtoRootDir="${ScriptDir}/../.."
+readonly ProtoC="${ProtoRootDir}/src/protoc"
+
+pushd "${ProtoRootDir}" > /dev/null
+
+# Compiler build fails if config.h hasn't been made yet (even if configure/etc.
+# have been run, so get that made first).
+make $@ config.h
+
+# Make sure the compiler is current.
+cd src
+make $@ protoc
+
+# These really should only be run when the inputs or compiler are newer than
+# the outputs.
+
+# Needed by the runtime.
+./protoc --objc_out="${ProtoRootDir}/objectivec" google/protobuf/descriptor.proto
+
+# Well known types that the library provides helpers for.
+./protoc --objc_out="${ProtoRootDir}/objectivec" google/protobuf/timestamp.proto
+./protoc --objc_out="${ProtoRootDir}/objectivec" google/protobuf/duration.proto
+
+popd > /dev/null

+ 687 - 0
objectivec/DevTools/pddm.py

@@ -0,0 +1,687 @@
+#! /usr/bin/python
+#
+# Protocol Buffers - Google's data interchange format
+# Copyright 2015 Google Inc.  All rights reserved.
+# https://developers.google.com/protocol-buffers/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""PDDM - Poor Developers' Debug-able Macros
+
+A simple markup that can be added in comments of source so they can then be
+expanded out into code. Most of this could be done with CPP macros, but then
+developers can't really step through them in the debugger, this way they are
+expanded to the same code, but you can debug them.
+
+Any file can be processed, but the syntax is designed around a C based compiler.
+Processed lines start with "//%".  There are three types of sections you can
+create: Text (left alone), Macro Definitions, and Macro Expansions.  There is
+no order required between definitions and expansions, all definitions are read
+before any expansions are processed (thus, if desired, definitions can be put
+at the end of the file to keep them out of the way of the code).
+
+Macro Definitions are started with "//%PDDM-DEFINE Name(args)" and all lines
+afterwards that start with "//%" are included in the definition.  Multiple
+macros can be defined in one block by just using a another "//%PDDM-DEFINE"
+line to start the next macro.  Optionally, a macro can be ended with
+"//%PDDM-DEFINE-END", this can be useful when you want to make it clear that
+trailing blank lines are included in the macro.  You can also end a definition
+with an expansion.
+
+Macro Expansions are started by single lines containing
+"//%PDDM-EXPAND Name(args)" and then with "//%PDDM-EXPAND-END" or another
+expansions.  All lines in-between are replaced by the result of the expansion.
+The first line of the expansion is always a blank like just for readability.
+
+Expansion itself is pretty simple, one macro can invoke another macro, but
+you cannot nest the invoke of a macro in another macro (i.e. - can't do
+"foo(bar(a))", but you can define foo(a) and bar(b) where bar invokes foo()
+within its expansion.
+
+When macros are expanded, the arg references can also add "$O" suffix to the
+name (i.e. - "NAME$O") to specify an option to be applied. The options are:
+
+    $S - Replace each character in the value with a space.
+    $l - Lowercase the first letter of the value.
+    $L - Lowercase the whole value.
+    $u - Uppercase the first letter of the value.
+    $U - Uppercase the whole value.
+
+Within a macro you can use ## to cause things to get joined together after
+expansion (i.e. - "a##b" within a macro will become "ab").
+
+Example:
+
+    int foo(MyEnum x) {
+    switch (x) {
+    //%PDDM-EXPAND case(Enum_Left, 1)
+    //%PDDM-EXPAND case(Enum_Center, 2)
+    //%PDDM-EXPAND case(Enum_Right, 3)
+    //%PDDM-EXPAND-END
+    }
+
+    //%PDDM-DEFINE case(_A, _B)
+    //%  case _A:
+    //%    return _B;
+
+  A macro ends at the start of the next one, or an optional %PDDM-DEFINE-END
+  can be used to avoid adding extra blank lines/returns (or make it clear when
+  it is desired).
+
+  One macro can invoke another by simply using its name NAME(ARGS). You cannot
+  nest an invoke inside another (i.e. - NAME1(NAME2(ARGS)) isn't supported).
+
+  Within a macro you can use ## to cause things to get joined together after
+  processing (i.e. - "a##b" within a macro will become "ab").
+
+
+"""
+
+import optparse
+import os
+import re
+import sys
+
+
+# Regex for macro definition.
+_MACRO_RE = re.compile(r'(?P<name>\w+)\((?P<args>.*?)\)')
+# Regex for macro's argument definition.
+_MACRO_ARG_NAME_RE = re.compile(r'^\w+$')
+
+# Line inserted after each EXPAND.
+_GENERATED_CODE_LINE = (
+  '// This block of code is generated, do not edit it directly.'
+)
+
+
+def _MacroRefRe(macro_names):
+  # Takes in a list of macro names and makes a regex that will match invokes
+  # of those macros.
+  return re.compile(r'\b(?P<macro_ref>(?P<name>(%s))\((?P<args>.*?)\))' %
+                    '|'.join(macro_names))
+
+def _MacroArgRefRe(macro_arg_names):
+  # Takes in a list of macro arg names and makes a regex that will match
+  # uses of those args.
+  return re.compile(r'\b(?P<name>(%s))(\$(?P<option>.))?\b' %
+                    '|'.join(macro_arg_names))
+
+
+class PDDMError(Exception):
+  """Error thrown by pddm."""
+  pass
+
+
+class MacroCollection(object):
+  """Hold a set of macros and can resolve/expand them."""
+
+  def __init__(self, a_file=None):
+    """Initializes the collection.
+
+    Args:
+      a_file: The file like stream to parse.
+
+    Raises:
+      PDDMError if there are any issues.
+    """
+    self._macros = dict()
+    if a_file:
+      self.ParseInput(a_file)
+
+  class MacroDefinition(object):
+    """Holds a macro definition."""
+
+    def __init__(self, name, arg_names):
+      self._name = name
+      self._args = tuple(arg_names)
+      self._body = ''
+      self._needNewLine = False
+
+    def AppendLine(self, line):
+      if self._needNewLine:
+        self._body += '\n'
+      self._body += line
+      self._needNewLine = not line.endswith('\n')
+
+    @property
+    def name(self):
+      return self._name
+
+    @property
+    def args(self):
+      return self._args
+
+    @property
+    def body(self):
+      return self._body
+
+  def ParseInput(self, a_file):
+    """Consumes input extracting definitions.
+
+    Args:
+      a_file: The file like stream to parse.
+
+    Raises:
+      PDDMError if there are any issues.
+    """
+    input_lines = a_file.read().splitlines()
+    self.ParseLines(input_lines)
+
+  def ParseLines(self, input_lines):
+    """Parses list of lines.
+
+    Args:
+      input_lines: A list of strings of input to parse (no newlines on the
+                   strings).
+
+    Raises:
+      PDDMError if there are any issues.
+    """
+    current_macro = None
+    for line in input_lines:
+      if line.startswith('PDDM-'):
+        directive = line.split(' ', 1)[0]
+        if directive == 'PDDM-DEFINE':
+          name, args = self._ParseDefineLine(line)
+          if self._macros.get(name):
+            raise PDDMError('Attempt to redefine macro: "%s"' % line)
+          current_macro = self.MacroDefinition(name, args)
+          self._macros[name] = current_macro
+          continue
+        if directive == 'PDDM-DEFINE-END':
+          if not current_macro:
+            raise PDDMError('Got DEFINE-END directive without an active macro:'
+                            ' "%s"' % line)
+          current_macro = None
+          continue
+        raise PDDMError('Hit a line with an unknown directive: "%s"' % line)
+
+      if current_macro:
+        current_macro.AppendLine(line)
+        continue
+
+      # Allow blank lines between macro definitions.
+      if line.strip() == '':
+        continue
+
+      raise PDDMError('Hit a line that wasn\'t a directive and no open macro'
+                      ' definition: "%s"' % line)
+
+  def _ParseDefineLine(self, input_line):
+    assert input_line.startswith('PDDM-DEFINE')
+    line = input_line[12:].strip()
+    match = _MACRO_RE.match(line)
+    # Must match full line
+    if match is None or match.group(0) != line:
+      raise PDDMError('Failed to parse macro definition: "%s"' % input_line)
+    name = match.group('name')
+    args_str = match.group('args').strip()
+    args = []
+    if args_str:
+      for part in args_str.split(','):
+        arg = part.strip()
+        if arg == '':
+          raise PDDMError('Empty arg name in macro definition: "%s"'
+                          % input_line)
+        if not _MACRO_ARG_NAME_RE.match(arg):
+          raise PDDMError('Invalid arg name "%s" in macro definition: "%s"'
+                          % (arg, input_line))
+        if arg in args:
+          raise PDDMError('Arg name "%s" used more than once in macro'
+                          ' definition: "%s"' % (arg, input_line))
+        args.append(arg)
+    return (name, tuple(args))
+
+  def Expand(self, macro_ref_str):
+    """Expands the macro reference.
+
+    Args:
+      macro_ref_str: String of a macro reference (i.e. foo(a, b)).
+
+    Returns:
+      The text from the expansion.
+
+    Raises:
+      PDDMError if there are any issues.
+    """
+    match = _MACRO_RE.match(macro_ref_str)
+    if match is None or match.group(0) != macro_ref_str:
+      raise PDDMError('Failed to parse macro reference: "%s"' % macro_ref_str)
+    if match.group('name') not in self._macros:
+      raise PDDMError('No macro named "%s".' % match.group('name'))
+    return self._Expand(match, [], macro_ref_str)
+
+  def _FormatStack(self, macro_ref_stack):
+    result = ''
+    for _, macro_ref in reversed(macro_ref_stack):
+      result += '\n...while expanding "%s".' % macro_ref
+    return result
+
+  def _Expand(self, macro_ref_match, macro_stack, macro_ref_str=None):
+    if macro_ref_str is None:
+      macro_ref_str = macro_ref_match.group('macro_ref')
+    name = macro_ref_match.group('name')
+    for prev_name, prev_macro_ref in macro_stack:
+      if name == prev_name:
+        raise PDDMError('Found macro recusion, invoking "%s":%s' %
+                        (macro_ref_str, self._FormatStack(macro_stack)))
+    macro = self._macros[name]
+    args_str = macro_ref_match.group('args').strip()
+    args = []
+    if args_str or len(macro.args):
+      args = [x.strip() for x in args_str.split(',')]
+    if len(args) != len(macro.args):
+      raise PDDMError('Expected %d args, got: "%s".%s' %
+                      (len(macro.args), macro_ref_str,
+                       self._FormatStack(macro_stack)))
+    # Replace args usages.
+    result = self._ReplaceArgValues(macro, args, macro_ref_str, macro_stack)
+    # Expand any macro invokes.
+    new_macro_stack = macro_stack + [(name, macro_ref_str)]
+    while True:
+      eval_result = self._EvalMacrosRefs(result, new_macro_stack)
+      # Consume all ## directives to glue things together.
+      eval_result = eval_result.replace('##', '')
+      if eval_result == result:
+        break
+      result = eval_result
+    return result
+
+  def _ReplaceArgValues(self,
+                        macro, arg_values, macro_ref_to_report, macro_stack):
+    if len(arg_values) == 0:
+      # Nothing to do
+      return macro.body
+    assert len(arg_values) == len(macro.args)
+    args = dict(zip(macro.args, arg_values))
+    def _lookupArg(match):
+      val = args[match.group('name')]
+      opt = match.group('option')
+      if opt:
+        if opt == 'S': # Spaces for the length
+          return ' ' * len(val)
+        elif opt == 'l': # Lowercase first character
+          if val:
+            return val[0].lower() + val[1:]
+          else:
+            return val
+        elif opt == 'L': # All Lowercase
+          return val.lower()
+        elif opt == 'u': # Uppercase first character
+          if val:
+            return val[0].upper() + val[1:]
+          else:
+            return val
+        elif opt == 'U': # All Uppercase
+          return val.upper()
+        else:
+          raise PDDMError('Unknown arg option "%s$%s" while expanding "%s".%s'
+                          % (match.group('name'), match.group('option'),
+                             macro_ref_to_report,
+                             self._FormatStack(macro_stack)))
+      return val
+    # Let the regex do the work!
+    macro_arg_ref_re = _MacroArgRefRe(macro.args)
+    return macro_arg_ref_re.sub(_lookupArg, macro.body)
+
+  def _EvalMacrosRefs(self, text, macro_stack):
+    macro_ref_re = _MacroRefRe(self._macros.keys())
+    def _resolveMacro(match):
+      return self._Expand(match, macro_stack)
+    return macro_ref_re.sub(_resolveMacro, text)
+
+
+class SourceFile(object):
+  """Represents a source file with PDDM directives in it."""
+
+  def __init__(self, a_file, import_resolver=None):
+    """Initializes the file reading in the file.
+
+    Args:
+      a_file: The file to read in.
+      import_resolver: a function that given a path will return a stream for
+        the contents.
+
+    Raises:
+      PDDMError if there are any issues.
+    """
+    self._sections = []
+    self._original_content = a_file.read()
+    self._import_resolver = import_resolver
+    self._processed_content = None
+
+  class SectionBase(object):
+
+    def __init__(self, first_line_num):
+      self._lines = []
+      self._first_line_num = first_line_num
+
+    def TryAppend(self, line, line_num):
+      """Try appending a line.
+
+      Args:
+        line: The line to append.
+        line_num: The number of the line.
+
+      Returns:
+        A tuple of (SUCCESS, CAN_ADD_MORE).  If SUCCESS if False, the line
+        wasn't append.  If SUCCESS is True, then CAN_ADD_MORE is True/False to
+        indicate if more lines can be added after this one.
+      """
+      assert False, "sublcass should have overridden"
+      return (False, False)
+
+    def HitEOF(self):
+      """Called when the EOF was reached for for a given section."""
+      pass
+
+    def BindMacroCollection(self, macro_collection):
+      """Binds the chunk to a macro collection.
+
+      Args:
+        macro_collection: The collection to bind too.
+      """
+      pass
+
+    def Append(self, line):
+      self._lines.append(line)
+
+    @property
+    def lines(self):
+      return self._lines
+
+    @property
+    def num_lines_captured(self):
+      return len(self._lines)
+
+    @property
+    def first_line_num(self):
+      return self._first_line_num
+
+    @property
+    def first_line(self):
+      if not self._lines:
+        return ''
+      return self._lines[0]
+
+    @property
+    def text(self):
+      return '\n'.join(self.lines) + '\n'
+
+  class TextSection(SectionBase):
+    """Text section that is echoed out as is."""
+
+    def TryAppend(self, line, line_num):
+      if line.startswith('//%PDDM'):
+        return (False, False)
+      self.Append(line)
+      return (True, True)
+
+  class ExpansionSection(SectionBase):
+    """Section that is the result of an macro expansion."""
+
+    def __init__(self, first_line_num):
+      SourceFile.SectionBase.__init__(self, first_line_num)
+      self._macro_collection = None
+
+    def TryAppend(self, line, line_num):
+      if line.startswith('//%PDDM'):
+        directive = line.split(' ', 1)[0]
+        if directive == '//%PDDM-EXPAND':
+          self.Append(line)
+          return (True, True)
+        if directive == '//%PDDM-EXPAND-END':
+          assert self.num_lines_captured > 0
+          return (True, False)
+        raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
+                        (directive, line_num, self.first_line))
+      # Eat other lines.
+      return (True, True)
+
+    def HitEOF(self):
+      raise PDDMError('Hit the end of the file while in "%s".' %
+                      self.first_line)
+
+    def BindMacroCollection(self, macro_collection):
+      self._macro_collection = macro_collection
+
+    @property
+    def lines(self):
+      captured_lines = SourceFile.SectionBase.lines.fget(self)
+      directive_len = len('//%PDDM-EXPAND')
+      result = []
+      for line in captured_lines:
+        result.append(line)
+        if self._macro_collection:
+          # Always add a blank line, seems to read better. (If need be, add an
+          # option to the EXPAND to indicate if this should be done.)
+          result.extend([_GENERATED_CODE_LINE, ''])
+          macro = line[directive_len:].strip()
+          try:
+            expand_result = self._macro_collection.Expand(macro)
+            # Since expansions are line oriented, strip trailing whitespace
+            # from the lines.
+            lines = [x.rstrip() for x in expand_result.split('\n')]
+            result.append('\n'.join(lines))
+          except PDDMError as e:
+            raise PDDMError('%s\n...while expanding "%s" from the section'
+                            ' that started:\n   Line %d: %s' %
+                            (e.message, macro,
+                             self.first_line_num, self.first_line))
+
+      # Add the ending marker.
+      if len(captured_lines) == 1:
+        result.append('//%%PDDM-EXPAND-END %s' %
+                       captured_lines[0][directive_len:].strip())
+      else:
+        result.append('//%%PDDM-EXPAND-END (%s expansions)' % len(captured_lines))
+
+      return result
+
+  class DefinitionSection(SectionBase):
+    """Section containing macro definitions"""
+
+    def TryAppend(self, line, line_num):
+      if not line.startswith('//%'):
+        return (False, False)
+      if line.startswith('//%PDDM'):
+        directive = line.split(' ', 1)[0]
+        if directive == "//%PDDM-EXPAND":
+          return False, False
+        if directive not in ('//%PDDM-DEFINE', '//%PDDM-DEFINE-END'):
+          raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
+                          (directive, line_num, self.first_line))
+      self.Append(line)
+      return (True, True)
+
+    def BindMacroCollection(self, macro_collection):
+      if macro_collection:
+        try:
+          # Parse the lines after stripping the prefix.
+          macro_collection.ParseLines([x[3:] for x in self.lines])
+        except PDDMError as e:
+          raise PDDMError('%s\n...while parsing section that started:\n'
+                          '  Line %d: %s' %
+                          (e.message, self.first_line_num, self.first_line))
+
+  class ImportDefinesSection(SectionBase):
+    """Section containing an import of PDDM-DEFINES from an external file."""
+
+    def __init__(self, first_line_num, import_resolver):
+      SourceFile.SectionBase.__init__(self, first_line_num)
+      self._import_resolver = import_resolver
+
+    def TryAppend(self, line, line_num):
+      if not line.startswith('//%PDDM-IMPORT-DEFINES '):
+        return (False, False)
+      assert self.num_lines_captured == 0
+      self.Append(line)
+      return (True, False)
+
+    def BindMacroCollection(self, macro_colletion):
+      if not macro_colletion:
+        return
+      if self._import_resolver is None:
+        raise PDDMError('Got an IMPORT-DEFINES without a resolver (line %d):'
+                        ' "%s".' % (self.first_line_num, self.first_line))
+      import_name = self.first_line.split(' ', 1)[1].strip()
+      imported_file = self._import_resolver(import_name)
+      if imported_file is None:
+        raise PDDMError('Resolver failed to find "%s" (line %d):'
+                        ' "%s".' %
+                        (import_name, self.first_line_num, self.first_line))
+      try:
+        imported_src_file = SourceFile(imported_file, self._import_resolver)
+        imported_src_file._ParseFile()
+        for section in imported_src_file._sections:
+          section.BindMacroCollection(macro_colletion)
+      except PDDMError as e:
+        raise PDDMError('%s\n...while importing defines:\n'
+                        '  Line %d: %s' %
+                        (e.message, self.first_line_num, self.first_line))
+
+  def _ParseFile(self):
+    self._sections = []
+    lines = self._original_content.splitlines()
+    cur_section = None
+    for line_num, line in enumerate(lines, 1):
+      if not cur_section:
+        cur_section = self._MakeSection(line, line_num)
+      was_added, accept_more = cur_section.TryAppend(line, line_num)
+      if not was_added:
+        cur_section = self._MakeSection(line, line_num)
+        was_added, accept_more = cur_section.TryAppend(line, line_num)
+        assert was_added
+      if not accept_more:
+        cur_section = None
+
+    if cur_section:
+      cur_section.HitEOF()
+
+  def _MakeSection(self, line, line_num):
+    if not line.startswith('//%PDDM'):
+      section = self.TextSection(line_num)
+    else:
+      directive = line.split(' ', 1)[0]
+      if directive == '//%PDDM-EXPAND':
+        section = self.ExpansionSection(line_num)
+      elif directive == '//%PDDM-DEFINE':
+        section = self.DefinitionSection(line_num)
+      elif directive == '//%PDDM-IMPORT-DEFINES':
+        section = self.ImportDefinesSection(line_num, self._import_resolver)
+      else:
+        raise PDDMError('Unexpected line %d: "%s".' % (line_num, line))
+    self._sections.append(section)
+    return section
+
+  def ProcessContent(self, strip_expansion=False):
+    """Processes the file contents."""
+    self._ParseFile()
+    if strip_expansion:
+      # Without a collection the expansions become blank, removing them.
+      collection = None
+    else:
+      collection = MacroCollection()
+    for section in self._sections:
+      section.BindMacroCollection(collection)
+    result = ''
+    for section in self._sections:
+      result += section.text
+    self._processed_content = result
+
+  @property
+  def original_content(self):
+    return self._original_content
+
+  @property
+  def processed_content(self):
+    return self._processed_content
+
+
+def main(args):
+  usage = '%prog [OPTIONS] PATH ...'
+  description = (
+      'Processes PDDM directives in the the given paths and write them back'
+      ' out.'
+  )
+  parser = optparse.OptionParser(usage=usage, description=description)
+  parser.add_option('--dry-run',
+                    default=False, action='store_true',
+                    help='Don\'t write back to the file(s), just report if the'
+                    ' contents needs an update and exit with a value of 1.')
+  parser.add_option('--verbose',
+                    default=False, action='store_true',
+                    help='Reports is a file is already current.')
+  parser.add_option('--collapse',
+                    default=False, action='store_true',
+                    help='Removes all the generated code.')
+  opts, extra_args = parser.parse_args(args)
+
+  if not extra_args:
+    parser.error('Need atleast one file to process')
+
+  result = 0
+  for a_path in extra_args:
+    if not os.path.exists(a_path):
+      sys.stderr.write('ERROR: File not found: %s\n' % a_path)
+      return 100
+
+    def _ImportResolver(name):
+      # resolve based on the file being read.
+      a_dir = os.path.dirname(a_path)
+      import_path = os.path.join(a_dir, name)
+      if not os.path.exists(import_path):
+        return None
+      return open(import_path, 'r')
+
+    with open(a_path, 'r') as f:
+      src_file = SourceFile(f, _ImportResolver)
+
+    try:
+      src_file.ProcessContent(strip_expansion=opts.collapse)
+    except PDDMError as e:
+      sys.stderr.write('ERROR: %s\n...While processing "%s"\n' %
+                       (e.message, a_path))
+      return 101
+
+    if src_file.processed_content != src_file.original_content:
+      if not opts.dry_run:
+        print 'Updating for "%s".' % a_path
+        with open(a_path, 'w') as f:
+          f.write(src_file.processed_content)
+      else:
+        # Special result to indicate things need updating.
+        print 'Update needed for "%s".' % a_path
+        result = 1
+    elif opts.verbose:
+      print 'No update for "%s".' % a_path
+
+  return result
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))

+ 515 - 0
objectivec/DevTools/pddm_tests.py

@@ -0,0 +1,515 @@
+#! /usr/bin/python
+#
+# Protocol Buffers - Google's data interchange format
+# Copyright 2015 Google Inc.  All rights reserved.
+# https://developers.google.com/protocol-buffers/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests for pddm.py."""
+
+import io
+import unittest
+
+import pddm
+
+
+class TestParsingMacros(unittest.TestCase):
+
+  def testParseEmpty(self):
+    f = io.StringIO(u'')
+    result = pddm.MacroCollection(f)
+    self.assertEqual(len(result._macros), 0)
+
+  def testParseOne(self):
+    f = io.StringIO(u"""PDDM-DEFINE foo( )
+body""")
+    result = pddm.MacroCollection(f)
+    self.assertEqual(len(result._macros), 1)
+    macro = result._macros.get('foo')
+    self.assertIsNotNone(macro)
+    self.assertEquals(macro.name, 'foo')
+    self.assertEquals(macro.args, tuple())
+    self.assertEquals(macro.body, 'body')
+
+  def testParseGeneral(self):
+    # Tests multiple defines, spaces in all places, etc.
+    f = io.StringIO(u"""
+PDDM-DEFINE noArgs( )
+body1
+body2
+
+PDDM-DEFINE-END
+
+PDDM-DEFINE oneArg(foo)
+body3
+PDDM-DEFINE  twoArgs( bar_ , baz )
+body4
+body5""")
+    result = pddm.MacroCollection(f)
+    self.assertEqual(len(result._macros), 3)
+    macro = result._macros.get('noArgs')
+    self.assertIsNotNone(macro)
+    self.assertEquals(macro.name, 'noArgs')
+    self.assertEquals(macro.args, tuple())
+    self.assertEquals(macro.body, 'body1\nbody2\n')
+    macro = result._macros.get('oneArg')
+    self.assertIsNotNone(macro)
+    self.assertEquals(macro.name, 'oneArg')
+    self.assertEquals(macro.args, ('foo',))
+    self.assertEquals(macro.body, 'body3')
+    macro = result._macros.get('twoArgs')
+    self.assertIsNotNone(macro)
+    self.assertEquals(macro.name, 'twoArgs')
+    self.assertEquals(macro.args, ('bar_', 'baz'))
+    self.assertEquals(macro.body, 'body4\nbody5')
+    # Add into existing collection
+    f = io.StringIO(u"""
+PDDM-DEFINE another(a,b,c)
+body1
+body2""")
+    result.ParseInput(f)
+    self.assertEqual(len(result._macros), 4)
+    macro = result._macros.get('another')
+    self.assertIsNotNone(macro)
+    self.assertEquals(macro.name, 'another')
+    self.assertEquals(macro.args, ('a', 'b', 'c'))
+    self.assertEquals(macro.body, 'body1\nbody2')
+
+  def testParseDirectiveIssues(self):
+    test_list = [
+      # Unknown directive
+      (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINED foo\nbaz',
+       'Hit a line with an unknown directive: '),
+      # End without begin
+      (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINE-END\nPDDM-DEFINE-END\n',
+       'Got DEFINE-END directive without an active macro: '),
+      # Line not in macro block
+      (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINE-END\nmumble\n',
+       'Hit a line that wasn\'t a directive and no open macro definition: '),
+      # Redefine macro
+      (u'PDDM-DEFINE foo()\nbody\nPDDM-DEFINE foo(a)\nmumble\n',
+       'Attempt to redefine macro: '),
+    ]
+    for idx, (input_str, expected_prefix) in enumerate(test_list, 1):
+      f = io.StringIO(input_str)
+      try:
+        result = pddm.MacroCollection(f)
+        self.fail('Should throw exception, entry %d' % idx)
+      except pddm.PDDMError as e:
+        self.assertTrue(e.message.startswith(expected_prefix),
+                        'Entry %d failed: %r' % (idx, e))
+
+  def testParseBeginIssues(self):
+    test_list = [
+      # 1. No name
+      (u'PDDM-DEFINE\nmumble',
+       'Failed to parse macro definition: '),
+      # 2. No name (with spaces)
+      (u'PDDM-DEFINE  \nmumble',
+       'Failed to parse macro definition: '),
+      # 3. No open paren
+      (u'PDDM-DEFINE  foo\nmumble',
+       'Failed to parse macro definition: '),
+      # 4. No close paren
+      (u'PDDM-DEFINE foo(\nmumble',
+       'Failed to parse macro definition: '),
+      # 5. No close paren (with args)
+      (u'PDDM-DEFINE foo(a, b\nmumble',
+       'Failed to parse macro definition: '),
+      # 6. No name before args
+      (u'PDDM-DEFINE  (a, b)\nmumble',
+       'Failed to parse macro definition: '),
+      # 7. No name before args
+      (u'PDDM-DEFINE foo bar(a, b)\nmumble',
+       'Failed to parse macro definition: '),
+      # 8. Empty arg name
+      (u'PDDM-DEFINE foo(a, ,b)\nmumble',
+       'Empty arg name in macro definition: '),
+      (u'PDDM-DEFINE foo(a,,b)\nmumble',
+       'Empty arg name in macro definition: '),
+      # 10. Duplicate name
+      (u'PDDM-DEFINE foo(a,b,a,c)\nmumble',
+       'Arg name "a" used more than once in macro definition: '),
+      # 11. Invalid arg name
+      (u'PDDM-DEFINE foo(a b,c)\nmumble',
+       'Invalid arg name "a b" in macro definition: '),
+      (u'PDDM-DEFINE foo(a.b,c)\nmumble',
+       'Invalid arg name "a.b" in macro definition: '),
+      (u'PDDM-DEFINE foo(a-b,c)\nmumble',
+       'Invalid arg name "a-b" in macro definition: '),
+      (u'PDDM-DEFINE foo(a,b,c.)\nmumble',
+       'Invalid arg name "c." in macro definition: '),
+      # 15. Extra stuff after the name
+      (u'PDDM-DEFINE foo(a,c) foo\nmumble',
+       'Failed to parse macro definition: '),
+      (u'PDDM-DEFINE foo(a,c) foo)\nmumble',
+       'Failed to parse macro definition: '),
+    ]
+    for idx, (input_str, expected_prefix) in enumerate(test_list, 1):
+      f = io.StringIO(input_str)
+      try:
+        result = pddm.MacroCollection(f)
+        self.fail('Should throw exception, entry %d' % idx)
+      except pddm.PDDMError as e:
+        self.assertTrue(e.message.startswith(expected_prefix),
+                        'Entry %d failed: %r' % (idx, e))
+
+
+class TestExpandingMacros(unittest.TestCase):
+
+  def testExpandBasics(self):
+    f = io.StringIO(u"""
+PDDM-DEFINE noArgs( )
+body1
+body2
+
+PDDM-DEFINE-END
+
+PDDM-DEFINE oneArg(a)
+body3 a
+
+PDDM-DEFINE-END
+
+PDDM-DEFINE twoArgs(b,c)
+body4 b c
+body5
+PDDM-DEFINE-END
+
+""")
+    mc = pddm.MacroCollection(f)
+    test_list = [
+      (u'noArgs()',
+       'body1\nbody2\n'),
+      (u'oneArg(wee)',
+       'body3 wee\n'),
+      (u'twoArgs(having some, fun)',
+       'body4 having some fun\nbody5'),
+      # One arg, pass empty.
+      (u'oneArg()',
+       'body3 \n'),
+      # Two args, gets empty in each slot.
+      (u'twoArgs(, empty)',
+       'body4  empty\nbody5'),
+      (u'twoArgs(empty, )',
+       'body4 empty \nbody5'),
+      (u'twoArgs(, )',
+       'body4  \nbody5'),
+    ]
+    for idx, (input_str, expected) in enumerate(test_list, 1):
+      result = mc.Expand(input_str)
+      self.assertEqual(result, expected,
+                       'Entry %d --\n       Result: %r\n     Expected: %r' %
+                       (idx, result, expected))
+
+  def testExpandArgOptions(self):
+    f = io.StringIO(u"""
+PDDM-DEFINE bar(a)
+a-a$S-a$l-a$L-a$u-a$U
+PDDM-DEFINE-END
+""")
+    mc = pddm.MacroCollection(f)
+
+    self.assertEqual(mc.Expand('bar(xYz)'), 'xYz-   -xYz-xyz-XYz-XYZ')
+    self.assertEqual(mc.Expand('bar(MnoP)'), 'MnoP-    -mnoP-mnop-MnoP-MNOP')
+    # Test empty
+    self.assertEqual(mc.Expand('bar()'), '-----')
+
+  def testExpandSimpleMacroErrors(self):
+    f = io.StringIO(u"""
+PDDM-DEFINE foo(a, b)
+<a-z>
+PDDM-DEFINE baz(a)
+a - a$z
+""")
+    mc = pddm.MacroCollection(f)
+    test_list = [
+      # 1. Unknown macro
+      (u'bar()',
+       'No macro named "bar".'),
+      (u'bar(a)',
+       'No macro named "bar".'),
+      # 3. Arg mismatch
+      (u'foo()',
+       'Expected 2 args, got: "foo()".'),
+      (u'foo(a b)',
+       'Expected 2 args, got: "foo(a b)".'),
+      (u'foo(a,b,c)',
+       'Expected 2 args, got: "foo(a,b,c)".'),
+      # 6. Unknown option in expansion
+      (u'baz(mumble)',
+       'Unknown arg option "a$z" while expanding "baz(mumble)".'),
+    ]
+    for idx, (input_str, expected_err) in enumerate(test_list, 1):
+      try:
+        result = mc.Expand(input_str)
+        self.fail('Should throw exception, entry %d' % idx)
+      except pddm.PDDMError as e:
+        self.assertEqual(e.message, expected_err,
+                        'Entry %d failed: %r' % (idx, e))
+
+  def testExpandReferences(self):
+    f = io.StringIO(u"""
+PDDM-DEFINE StartIt()
+foo(abc, def)
+foo(ghi, jkl)
+PDDM-DEFINE foo(a, b)
+bar(a, int)
+bar(b, NSString *)
+PDDM-DEFINE bar(n, t)
+- (t)n;
+- (void)set##n$u##:(t)value;
+
+""")
+    mc = pddm.MacroCollection(f)
+    expected = """- (int)abc;
+- (void)setAbc:(int)value;
+
+- (NSString *)def;
+- (void)setDef:(NSString *)value;
+
+- (int)ghi;
+- (void)setGhi:(int)value;
+
+- (NSString *)jkl;
+- (void)setJkl:(NSString *)value;
+"""
+    self.assertEqual(mc.Expand('StartIt()'), expected)
+
+  def testCatchRecursion(self):
+    f = io.StringIO(u"""
+PDDM-DEFINE foo(a, b)
+bar(1, a)
+bar(2, b)
+PDDM-DEFINE bar(x, y)
+foo(x, y)
+""")
+    mc = pddm.MacroCollection(f)
+    try:
+      result = mc.Expand('foo(A,B)')
+      self.fail('Should throw exception, entry %d' % idx)
+    except pddm.PDDMError as e:
+      self.assertEqual(e.message,
+                       'Found macro recusion, invoking "foo(1, A)":\n...while expanding "bar(1, A)".\n...while expanding "foo(A,B)".')
+
+
+class TestParsingSource(unittest.TestCase):
+
+  def testBasicParse(self):
+    test_list = [
+      # 1. no directives
+      (u'a\nb\nc',
+       (3,) ),
+      # 2. One define
+      (u'a\n//%PDDM-DEFINE foo()\n//%body\nc',
+       (1, 2, 1) ),
+      # 3. Two defines
+      (u'a\n//%PDDM-DEFINE foo()\n//%body\n//%PDDM-DEFINE bar()\n//%body2\nc',
+       (1, 4, 1) ),
+      # 4. Two defines with ends
+      (u'a\n//%PDDM-DEFINE foo()\n//%body\n//%PDDM-DEFINE-END\n'
+       u'//%PDDM-DEFINE bar()\n//%body2\n//%PDDM-DEFINE-END\nc',
+       (1, 6, 1) ),
+      # 5. One expand, one define (that runs to end of file)
+      (u'a\n//%PDDM-EXPAND foo()\nbody\n//%PDDM-EXPAND-END\n'
+       u'//%PDDM-DEFINE bar()\n//%body2\n',
+       (1, 1, 2) ),
+      # 6. One define ended with an expand.
+      (u'a\nb\n//%PDDM-DEFINE bar()\n//%body2\n'
+       u'//%PDDM-EXPAND bar()\nbody2\n//%PDDM-EXPAND-END\n',
+       (2, 2, 1) ),
+      # 7. Two expands (one end), one define.
+      (u'a\n//%PDDM-EXPAND foo(1)\nbody\n//%PDDM-EXPAND foo(2)\nbody2\n//%PDDM-EXPAND-END\n'
+       u'//%PDDM-DEFINE foo()\n//%body2\n',
+       (1, 2, 2) ),
+    ]
+    for idx, (input_str, line_counts) in enumerate(test_list, 1):
+      f = io.StringIO(input_str)
+      sf = pddm.SourceFile(f)
+      sf._ParseFile()
+      self.assertEqual(len(sf._sections), len(line_counts),
+                       'Entry %d -- %d != %d' %
+                       (idx, len(sf._sections), len(line_counts)))
+      for idx2, (sec, expected) in enumerate(zip(sf._sections, line_counts), 1):
+        self.assertEqual(sec.num_lines_captured, expected,
+                         'Entry %d, section %d -- %d != %d' %
+                         (idx, idx2, sec.num_lines_captured, expected))
+
+  def testErrors(self):
+    test_list = [
+      # 1. Directive within expansion
+      (u'//%PDDM-EXPAND a()\n//%PDDM-BOGUS',
+       'Ran into directive ("//%PDDM-BOGUS", line 2) while in "//%PDDM-EXPAND a()".'),
+      (u'//%PDDM-EXPAND a()\n//%PDDM-DEFINE a()\n//%body\n',
+       'Ran into directive ("//%PDDM-DEFINE", line 2) while in "//%PDDM-EXPAND a()".'),
+      # 3. Expansion ran off end of file
+      (u'//%PDDM-EXPAND a()\na\nb\n',
+       'Hit the end of the file while in "//%PDDM-EXPAND a()".'),
+      # 4. Directive within define
+      (u'//%PDDM-DEFINE a()\n//%body\n//%PDDM-BOGUS',
+       'Ran into directive ("//%PDDM-BOGUS", line 3) while in "//%PDDM-DEFINE a()".'),
+      (u'//%PDDM-DEFINE a()\n//%body\n//%PDDM-EXPAND-END a()',
+       'Ran into directive ("//%PDDM-EXPAND-END", line 3) while in "//%PDDM-DEFINE a()".'),
+      # 6. Directives that shouldn't start sections
+      (u'a\n//%PDDM-DEFINE-END a()\n//a\n',
+       'Unexpected line 2: "//%PDDM-DEFINE-END a()".'),
+      (u'a\n//%PDDM-EXPAND-END a()\n//a\n',
+       'Unexpected line 2: "//%PDDM-EXPAND-END a()".'),
+      (u'//%PDDM-BOGUS\n//a\n',
+       'Unexpected line 1: "//%PDDM-BOGUS".'),
+    ]
+    for idx, (input_str, expected_err) in enumerate(test_list, 1):
+      f = io.StringIO(input_str)
+      try:
+        pddm.SourceFile(f)._ParseFile()
+        self.fail('Should throw exception, entry %d' % idx)
+      except pddm.PDDMError as e:
+        self.assertEqual(e.message, expected_err,
+                        'Entry %d failed: %r' % (idx, e))
+
+class TestProcessingSource(unittest.TestCase):
+
+  def testBasics(self):
+    input_str = u"""
+//%PDDM-IMPORT-DEFINES ImportFile
+foo
+//%PDDM-EXPAND mumble(abc)
+//%PDDM-EXPAND-END
+bar
+//%PDDM-EXPAND mumble(def)
+//%PDDM-EXPAND mumble(ghi)
+//%PDDM-EXPAND-END
+baz
+//%PDDM-DEFINE mumble(a_)
+//%a_: getName(a_)
+"""
+    input_str2 = u"""
+//%PDDM-DEFINE getName(x_)
+//%do##x_$u##(int x_);
+
+"""
+    expected = u"""
+//%PDDM-IMPORT-DEFINES ImportFile
+foo
+//%PDDM-EXPAND mumble(abc)
+// This block of code is generated, do not edit it directly.
+
+abc: doAbc(int abc);
+//%PDDM-EXPAND-END mumble(abc)
+bar
+//%PDDM-EXPAND mumble(def)
+// This block of code is generated, do not edit it directly.
+
+def: doDef(int def);
+//%PDDM-EXPAND mumble(ghi)
+// This block of code is generated, do not edit it directly.
+
+ghi: doGhi(int ghi);
+//%PDDM-EXPAND-END (2 expansions)
+baz
+//%PDDM-DEFINE mumble(a_)
+//%a_: getName(a_)
+"""
+    expected_stripped = u"""
+//%PDDM-IMPORT-DEFINES ImportFile
+foo
+//%PDDM-EXPAND mumble(abc)
+//%PDDM-EXPAND-END mumble(abc)
+bar
+//%PDDM-EXPAND mumble(def)
+//%PDDM-EXPAND mumble(ghi)
+//%PDDM-EXPAND-END (2 expansions)
+baz
+//%PDDM-DEFINE mumble(a_)
+//%a_: getName(a_)
+"""
+    def _Resolver(name):
+      self.assertEqual(name, 'ImportFile')
+      return io.StringIO(input_str2)
+    f = io.StringIO(input_str)
+    sf = pddm.SourceFile(f, _Resolver)
+    sf.ProcessContent()
+    self.assertEqual(sf.processed_content, expected)
+    # Feed it through and nothing should change.
+    f2 = io.StringIO(sf.processed_content)
+    sf2 = pddm.SourceFile(f2, _Resolver)
+    sf2.ProcessContent()
+    self.assertEqual(sf2.processed_content, expected)
+    self.assertEqual(sf2.processed_content, sf.processed_content)
+    # Test stripping (with the original input and expanded version).
+    f2 = io.StringIO(input_str)
+    sf2 = pddm.SourceFile(f2)
+    sf2.ProcessContent(strip_expansion=True)
+    self.assertEqual(sf2.processed_content, expected_stripped)
+    f2 = io.StringIO(sf.processed_content)
+    sf2 = pddm.SourceFile(f2, _Resolver)
+    sf2.ProcessContent(strip_expansion=True)
+    self.assertEqual(sf2.processed_content, expected_stripped)
+
+  def testProcessFileWithMacroParseError(self):
+    input_str = u"""
+foo
+//%PDDM-DEFINE mumble(a_)
+//%body
+//%PDDM-DEFINE mumble(x_)
+//%body2
+
+"""
+    f = io.StringIO(input_str)
+    sf = pddm.SourceFile(f)
+    try:
+      sf.ProcessContent()
+      self.fail('Should throw exception, entry %d' % idx)
+    except pddm.PDDMError as e:
+      self.assertEqual(e.message,
+                       'Attempt to redefine macro: "PDDM-DEFINE mumble(x_)"\n'
+                       '...while parsing section that started:\n'
+                       '  Line 3: //%PDDM-DEFINE mumble(a_)')
+
+  def testProcessFileWithExpandError(self):
+    input_str = u"""
+foo
+//%PDDM-DEFINE mumble(a_)
+//%body
+//%PDDM-EXPAND foobar(x_)
+//%PDDM-EXPAND-END
+
+"""
+    f = io.StringIO(input_str)
+    sf = pddm.SourceFile(f)
+    try:
+      sf.ProcessContent()
+      self.fail('Should throw exception, entry %d' % idx)
+    except pddm.PDDMError as e:
+      self.assertEqual(e.message,
+                       'No macro named "foobar".\n'
+                       '...while expanding "foobar(x_)" from the section that'
+                       ' started:\n   Line 5: //%PDDM-EXPAND foobar(x_)')
+
+
+if __name__ == '__main__':
+  unittest.main()

+ 535 - 0
objectivec/GPBArray.h

@@ -0,0 +1,535 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBTypes.h"
+
+// These classes are used for repeated fields of basic data types. They are used because
+// they perform better than boxing into NSNumbers in NSArrays.
+
+// Note: These are not meant to be subclassed.
+
+//%PDDM-EXPAND DECLARE_ARRAYS()
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+@interface GPBInt32Array : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(int32_t)value;
++ (instancetype)arrayWithValueArray:(GPBInt32Array *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const int32_t [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBInt32Array *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (int32_t)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(int32_t)value;
+- (void)addValues:(const int32_t [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBInt32Array *)array;
+
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - UInt32
+
+@interface GPBUInt32Array : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(uint32_t)value;
++ (instancetype)arrayWithValueArray:(GPBUInt32Array *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const uint32_t [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBUInt32Array *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (uint32_t)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(uint32_t)value;
+- (void)addValues:(const uint32_t [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBUInt32Array *)array;
+
+- (void)insertValue:(uint32_t)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint32_t)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Int64
+
+@interface GPBInt64Array : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(int64_t)value;
++ (instancetype)arrayWithValueArray:(GPBInt64Array *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const int64_t [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBInt64Array *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (int64_t)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(int64_t)value;
+- (void)addValues:(const int64_t [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBInt64Array *)array;
+
+- (void)insertValue:(int64_t)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int64_t)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - UInt64
+
+@interface GPBUInt64Array : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(uint64_t)value;
++ (instancetype)arrayWithValueArray:(GPBUInt64Array *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const uint64_t [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBUInt64Array *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (uint64_t)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(uint64_t)value;
+- (void)addValues:(const uint64_t [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBUInt64Array *)array;
+
+- (void)insertValue:(uint64_t)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint64_t)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Float
+
+@interface GPBFloatArray : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(float)value;
++ (instancetype)arrayWithValueArray:(GPBFloatArray *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const float [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBFloatArray *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (float)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(float)value;
+- (void)addValues:(const float [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBFloatArray *)array;
+
+- (void)insertValue:(float)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(float)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Double
+
+@interface GPBDoubleArray : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(double)value;
++ (instancetype)arrayWithValueArray:(GPBDoubleArray *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const double [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBDoubleArray *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (double)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(double)value;
+- (void)addValues:(const double [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBDoubleArray *)array;
+
+- (void)insertValue:(double)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(double)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Bool
+
+@interface GPBBoolArray : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)array;
++ (instancetype)arrayWithValue:(BOOL)value;
++ (instancetype)arrayWithValueArray:(GPBBoolArray *)array;
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValues:(const BOOL [])values
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBBoolArray *)array;
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+- (BOOL)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block;
+
+- (void)addValue:(BOOL)value;
+- (void)addValues:(const BOOL [])values count:(NSUInteger)count;
+- (void)addValuesFromArray:(GPBBoolArray *)array;
+
+- (void)insertValue:(BOOL)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(BOOL)value;
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Enum
+
+@interface GPBEnumArray : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)array;
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+                                   rawValue:(int32_t)value;
++ (instancetype)arrayWithValueArray:(GPBEnumArray *)array;
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+                                   capacity:(NSUInteger)count;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+
+// Initializes the array, copying the values.
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithValueArray:(GPBEnumArray *)array;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)count;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value at index is not a
+// valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (int32_t)valueAtIndex:(NSUInteger)index;
+
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (int32_t)rawValueAtIndex:(NSUInteger)index;
+
+- (void)enumerateRawValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
+                           usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)addValue:(int32_t)value;
+- (void)addValues:(const int32_t [])values count:(NSUInteger)count;
+
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value;
+
+// These methods bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+
+- (void)addRawValue:(int32_t)value;
+- (void)addRawValuesFromArray:(GPBEnumArray *)array;
+- (void)addRawValues:(const int32_t [])values count:(NSUInteger)count;
+
+- (void)insertRawValue:(int32_t)value atIndex:(NSUInteger)index;
+
+- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(int32_t)value;
+
+// No validation applies to these methods.
+
+- (void)removeValueAtIndex:(NSUInteger)index;
+- (void)removeAll;
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+//%PDDM-EXPAND-END DECLARE_ARRAYS()
+
+//%PDDM-DEFINE DECLARE_ARRAYS()
+//%ARRAY_INTERFACE_SIMPLE(Int32, int32_t)
+//%ARRAY_INTERFACE_SIMPLE(UInt32, uint32_t)
+//%ARRAY_INTERFACE_SIMPLE(Int64, int64_t)
+//%ARRAY_INTERFACE_SIMPLE(UInt64, uint64_t)
+//%ARRAY_INTERFACE_SIMPLE(Float, float)
+//%ARRAY_INTERFACE_SIMPLE(Double, double)
+//%ARRAY_INTERFACE_SIMPLE(Bool, BOOL)
+//%ARRAY_INTERFACE_ENUM(Enum, int32_t)
+
+//
+// The common case (everything but Enum)
+//
+
+//%PDDM-DEFINE ARRAY_INTERFACE_SIMPLE(NAME, TYPE)
+//%#pragma mark - NAME
+//%
+//%@interface GPB##NAME##Array : NSObject <NSCopying>
+//%
+//%@property(nonatomic, readonly) NSUInteger count;
+//%
+//%+ (instancetype)array;
+//%+ (instancetype)arrayWithValue:(TYPE)value;
+//%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array;
+//%+ (instancetype)arrayWithCapacity:(NSUInteger)count;
+//%
+//%// Initializes the array, copying the values.
+//%- (instancetype)initWithValues:(const TYPE [])values
+//%                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+//%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array;
+//%- (instancetype)initWithCapacity:(NSUInteger)count;
+//%
+//%ARRAY_IMMUTABLE_INTERFACE(NAME, TYPE, Basic)
+//%
+//%ARRAY_MUTABLE_INTERFACE(NAME, TYPE, Basic)
+//%
+//%@end
+//%
+
+//
+// Macros specific to Enums (to tweak their interface).
+//
+
+//%PDDM-DEFINE ARRAY_INTERFACE_ENUM(NAME, TYPE)
+//%#pragma mark - NAME
+//%
+//%@interface GPB##NAME##Array : NSObject <NSCopying>
+//%
+//%@property(nonatomic, readonly) NSUInteger count;
+//%@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+//%
+//%+ (instancetype)array;
+//%+ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func;
+//%+ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                   rawValue:(TYPE)value;
+//%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array;
+//%+ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                   capacity:(NSUInteger)count;
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+//%
+//%// Initializes the array, copying the values.
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                 rawValues:(const TYPE [])values
+//%                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+//%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array;
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                  capacity:(NSUInteger)count;
+//%
+//%// These will return kGPBUnrecognizedEnumeratorValue if the value at index is not a
+//%// valid enumerator as defined by validationFunc. If the actual value is
+//%// desired, use "raw" version of the method.
+//%
+//%ARRAY_IMMUTABLE_INTERFACE(NAME, TYPE, NAME)
+//%
+//%// These methods bypass the validationFunc to provide access to values that were not
+//%// known at the time the binary was compiled.
+//%
+//%- (TYPE)rawValueAtIndex:(NSUInteger)index;
+//%
+//%- (void)enumerateRawValuesWithBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+//%- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
+//%                           usingBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+//%
+//%// If value is not a valid enumerator as defined by validationFunc, these
+//%// methods will assert in debug, and will log in release and assign the value
+//%// to the default value. Use the rawValue methods below to assign non enumerator
+//%// values.
+//%
+//%ARRAY_MUTABLE_INTERFACE(NAME, TYPE, NAME)
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE ARRAY_IMMUTABLE_INTERFACE(NAME, TYPE, HELPER_NAME)
+//%- (TYPE)valueAtIndex:(NSUInteger)index;
+//%
+//%- (void)enumerateValuesWithBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+//%- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+//%                        usingBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+
+//%PDDM-DEFINE ARRAY_MUTABLE_INTERFACE(NAME, TYPE, HELPER_NAME)
+//%- (void)addValue:(TYPE)value;
+//%- (void)addValues:(const TYPE [])values count:(NSUInteger)count;
+//%ARRAY_EXTRA_MUTABLE_METHODS1_##HELPER_NAME(NAME, TYPE)
+//%- (void)insertValue:(TYPE)value atIndex:(NSUInteger)index;
+//%
+//%- (void)replaceValueAtIndex:(NSUInteger)index withValue:(TYPE)value;
+//%ARRAY_EXTRA_MUTABLE_METHODS2_##HELPER_NAME(NAME, TYPE)
+//%- (void)removeValueAtIndex:(NSUInteger)index;
+//%- (void)removeAll;
+//%
+//%- (void)exchangeValueAtIndex:(NSUInteger)idx1
+//%            withValueAtIndex:(NSUInteger)idx2;
+
+//
+// These are hooks invoked by the above to do insert as needed.
+//
+
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS1_Basic(NAME, TYPE)
+//%- (void)addValuesFromArray:(GPB##NAME##Array *)array;
+//%
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS2_Basic(NAME, TYPE)
+// Empty
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS1_Enum(NAME, TYPE)
+// Empty
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS2_Enum(NAME, TYPE)
+//%
+//%// These methods bypass the validationFunc to provide setting of values that were not
+//%// known at the time the binary was compiled.
+//%
+//%- (void)addRawValue:(TYPE)value;
+//%- (void)addRawValuesFromArray:(GPB##NAME##Array *)array;
+//%- (void)addRawValues:(const TYPE [])values count:(NSUInteger)count;
+//%
+//%- (void)insertRawValue:(TYPE)value atIndex:(NSUInteger)index;
+//%
+//%- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(TYPE)value;
+//%
+//%// No validation applies to these methods.
+//%

+ 2499 - 0
objectivec/GPBArray.m

@@ -0,0 +1,2499 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBArray_PackagePrivate.h"
+
+#import "GPBMessage_PackagePrivate.h"
+
+// Mutable arrays use an internal buffer that can always hold a multiple of this elements.
+#define kChunkSize 16
+#define CapacityFromCount(x) (((x / kChunkSize) + 1) * kChunkSize)
+
+static BOOL ArrayDefault_IsValidValue(int32_t value) {
+  // Anything but the bad value marker is allowed.
+  return (value != kGPBUnrecognizedEnumeratorValue);
+}
+
+//%PDDM-DEFINE VALIDATE_RANGE(INDEX, COUNT)
+//%  if (INDEX >= COUNT) {
+//%    [NSException raise:NSRangeException
+//%                format:@"Index (%lu) beyond bounds (%lu)",
+//%                       (unsigned long)INDEX, (unsigned long)COUNT];
+//%  }
+//%PDDM-DEFINE MAYBE_GROW_TO_SET_COUNT(NEW_COUNT)
+//%  if (NEW_COUNT > _capacity) {
+//%    [self internalResizeToCapacity:CapacityFromCount(NEW_COUNT)];
+//%  }
+//%  _count = NEW_COUNT;
+//%PDDM-DEFINE SET_COUNT_AND_MAYBE_SHRINK(NEW_COUNT)
+//%  _count = NEW_COUNT;
+//%  if ((NEW_COUNT + (2 * kChunkSize)) < _capacity) {
+//%    [self internalResizeToCapacity:CapacityFromCount(NEW_COUNT)];
+//%  }
+
+//
+// Macros for the common basic cases.
+//
+
+//%PDDM-DEFINE ARRAY_INTERFACE_SIMPLE(NAME, TYPE, FORMAT)
+//%#pragma mark - NAME
+//%
+//%@implementation GPB##NAME##Array {
+//% @package
+//%  TYPE *_values;
+//%  NSUInteger _count;
+//%  NSUInteger _capacity;
+//%}
+//%
+//%@synthesize count = _count;
+//%
+//%+ (instancetype)array {
+//%  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+//%}
+//%
+//%+ (instancetype)arrayWithValue:(TYPE)value {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+//%  // the type correct.
+//%  return [[(GPB##NAME##Array*)[self alloc] initWithValues:&value count:1] autorelease];
+//%}
+//%
+//%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array {
+//%  return [[(GPB##NAME##Array*)[self alloc] initWithValueArray:array] autorelease];
+//%}
+//%
+//%+ (instancetype)arrayWithCapacity:(NSUInteger)count {
+//%  return [[[self alloc] initWithCapacity:count] autorelease];
+//%}
+//%
+//%- (instancetype)init {
+//%  return [self initWithValues:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array {
+//%  return [self initWithValues:array->_values count:array->_count];
+//%}
+//%
+//%- (instancetype)initWithValues:(const TYPE [])values count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    if (count && values) {
+//%      _values = malloc(count * sizeof(TYPE));
+//%      if (values != NULL) {
+//%        _capacity = count;
+//%        memcpy(_values, values, count * sizeof(TYPE));
+//%        _count = count;
+//%      } else {
+//%        [self release];
+//%        [NSException raise:NSMallocException
+//%                    format:@"Failed to allocate %lu bytes",
+//%                           (unsigned long)(count * sizeof(TYPE))];
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithCapacity:(NSUInteger)count {
+//%  self = [self initWithValues:NULL count:0];
+//%  if (self && count) {
+//%    [self internalResizeToCapacity:count];
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)copyWithZone:(NSZone *)zone {
+//%  return [[GPB##NAME##Array allocWithZone:zone] initWithValues:_values count:_count];
+//%}
+//%
+//%ARRAY_IMMUTABLE_CORE(NAME, TYPE, , FORMAT)
+//%
+//%- (TYPE)valueAtIndex:(NSUInteger)index {
+//%VALIDATE_RANGE(index, _count)
+//%  return _values[index];
+//%}
+//%
+//%ARRAY_MUTABLE_CORE(NAME, TYPE, , FORMAT)
+//%@end
+//%
+
+//
+// Some core macros used for both the simple types and Enums.
+//
+
+//%PDDM-DEFINE ARRAY_IMMUTABLE_CORE(NAME, TYPE, ACCESSOR_NAME, FORMAT)
+//%- (void)dealloc {
+//%  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+//%  free(_values);
+//%  [super dealloc];
+//%}
+//%
+//%- (BOOL)isEqual:(GPB##NAME##Array *)other {
+//%  if (self == other) {
+//%    return YES;
+//%  }
+//%  if (![other isKindOfClass:[GPB##NAME##Array class]]) {
+//%    return NO;
+//%  }
+//%  return (_count == other->_count
+//%          && memcmp(_values, other->_values, (_count * sizeof(TYPE))) == 0);
+//%}
+//%
+//%- (NSUInteger)hash {
+//%  // Follow NSArray's lead, and use the count as the hash.
+//%  return _count;
+//%}
+//%
+//%- (NSString *)description {
+//%  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+//%  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+//%    if (i == 0) {
+//%      [result appendFormat:@"##FORMAT##", _values[i]];
+//%    } else {
+//%      [result appendFormat:@", ##FORMAT##", _values[i]];
+//%    }
+//%  }
+//%  [result appendFormat:@" }"];
+//%  return result;
+//%}
+//%
+//%- (void)enumerate##ACCESSOR_NAME##ValuesWithBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block {
+//%  [self enumerate##ACCESSOR_NAME##ValuesWithOptions:0 usingBlock:block];
+//%}
+//%
+//%- (void)enumerate##ACCESSOR_NAME##ValuesWithOptions:(NSEnumerationOptions)opts
+//%                  ACCESSOR_NAME$S      usingBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block {
+//%  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+//%  BOOL stop = NO;
+//%  if ((opts & NSEnumerationReverse) == 0) {
+//%    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+//%      block(_values[i], i, &stop);
+//%      if (stop) break;
+//%    }
+//%  } else if (_count > 0) {
+//%    for (NSUInteger i = _count; i > 0; --i) {
+//%      block(_values[i - 1], (i - 1), &stop);
+//%      if (stop) break;
+//%    }
+//%  }
+//%}
+
+//%PDDM-DEFINE MUTATION_HOOK_None()
+//%PDDM-DEFINE MUTATION_METHODS(NAME, TYPE, ACCESSOR_NAME, HOOK_1, HOOK_2)
+//%- (void)add##ACCESSOR_NAME##Value:(TYPE)value {
+//%  [self add##ACCESSOR_NAME##Values:&value count:1];
+//%}
+//%
+//%- (void)add##ACCESSOR_NAME##Values:(const TYPE [])values count:(NSUInteger)count {
+//%  if (values == NULL || count == 0) return;
+//%MUTATION_HOOK_##HOOK_1()  NSUInteger initialCount = _count;
+//%  NSUInteger newCount = initialCount + count;
+//%MAYBE_GROW_TO_SET_COUNT(newCount);
+//%  memcpy(&_values[initialCount], values, count * sizeof(TYPE));
+//%  if (_autocreator) {
+//%    GPBAutocreatedArrayModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)insert##ACCESSOR_NAME##Value:(TYPE)value atIndex:(NSUInteger)index {
+//%VALIDATE_RANGE(index, _count + 1)
+//%MUTATION_HOOK_##HOOK_2()  NSUInteger initialCount = _count;
+//%  NSUInteger newCount = initialCount + 1;
+//%MAYBE_GROW_TO_SET_COUNT(newCount);
+//%  if (index != initialCount) {
+//%    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(TYPE));
+//%  }
+//%  _values[index] = value;
+//%  if (_autocreator) {
+//%    GPBAutocreatedArrayModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)replaceValueAtIndex:(NSUInteger)index with##ACCESSOR_NAME##Value:(TYPE)value {
+//%VALIDATE_RANGE(index, _count)
+//%MUTATION_HOOK_##HOOK_2()  _values[index] = value;
+//%}
+
+//%PDDM-DEFINE ARRAY_MUTABLE_CORE(NAME, TYPE, ACCESSOR_NAME, FORMAT)
+//%- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+//%  _values = reallocf(_values, newCapacity * sizeof(TYPE));
+//%  if (_values == NULL) {
+//%    _capacity = 0;
+//%    _count = 0;
+//%    [NSException raise:NSMallocException
+//%                format:@"Failed to allocate %lu bytes",
+//%                       (unsigned long)(newCapacity * sizeof(TYPE))];
+//%  }
+//%  _capacity = newCapacity;
+//%}
+//%
+//%MUTATION_METHODS(NAME, TYPE, ACCESSOR_NAME, None, None)
+//%
+//%- (void)add##ACCESSOR_NAME##ValuesFromArray:(GPB##NAME##Array *)array {
+//%  [self add##ACCESSOR_NAME##Values:array->_values count:array->_count];
+//%}
+//%
+//%- (void)removeValueAtIndex:(NSUInteger)index {
+//%VALIDATE_RANGE(index, _count)
+//%  NSUInteger newCount = _count - 1;
+//%  if (index != newCount) {
+//%    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(TYPE));
+//%  }
+//%SET_COUNT_AND_MAYBE_SHRINK(newCount)
+//%}
+//%
+//%- (void)removeAll {
+//%SET_COUNT_AND_MAYBE_SHRINK(0)
+//%}
+//%
+//%- (void)exchangeValueAtIndex:(NSUInteger)idx1
+//%            withValueAtIndex:(NSUInteger)idx2 {
+//%VALIDATE_RANGE(idx1, _count)
+//%VALIDATE_RANGE(idx2, _count)
+//%  TYPE temp = _values[idx1];
+//%  _values[idx1] = _values[idx2];
+//%  _values[idx2] = temp;
+//%}
+//%
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Int32, int32_t, %d)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+@implementation GPBInt32Array {
+ @package
+  int32_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(int32_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBInt32Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBInt32Array *)array {
+  return [[(GPBInt32Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBInt32Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(int32_t));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(int32_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(int32_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBInt32Array *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32Array class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(int32_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%d", _values[i]];
+    } else {
+      [result appendFormat:@", %d", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (int32_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(int32_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(int32_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(int32_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const int32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(int32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBInt32Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(int32_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  int32_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(UInt32, uint32_t, %u)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32
+
+@implementation GPBUInt32Array {
+ @package
+  uint32_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(uint32_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBUInt32Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBUInt32Array *)array {
+  return [[(GPBUInt32Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBUInt32Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(uint32_t));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(uint32_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(uint32_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBUInt32Array *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32Array class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(uint32_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%u", _values[i]];
+    } else {
+      [result appendFormat:@", %u", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (uint32_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(uint32_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(uint32_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(uint32_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const uint32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(uint32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(uint32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(uint32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBUInt32Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(uint32_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  uint32_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Int64, int64_t, %lld)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int64
+
+@implementation GPBInt64Array {
+ @package
+  int64_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(int64_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBInt64Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBInt64Array *)array {
+  return [[(GPBInt64Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBInt64Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(int64_t));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(int64_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(int64_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBInt64Array *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64Array class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(int64_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%lld", _values[i]];
+    } else {
+      [result appendFormat:@", %lld", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (int64_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(int64_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(int64_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(int64_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const int64_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(int64_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(int64_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int64_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int64_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBInt64Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(int64_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  int64_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(UInt64, uint64_t, %llu)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt64
+
+@implementation GPBUInt64Array {
+ @package
+  uint64_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(uint64_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBUInt64Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBUInt64Array *)array {
+  return [[(GPBUInt64Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBUInt64Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(uint64_t));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(uint64_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(uint64_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBUInt64Array *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64Array class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(uint64_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%llu", _values[i]];
+    } else {
+      [result appendFormat:@", %llu", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (uint64_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(uint64_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(uint64_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(uint64_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const uint64_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(uint64_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(uint64_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(uint64_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint64_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBUInt64Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(uint64_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  uint64_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Float, float, %f)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Float
+
+@implementation GPBFloatArray {
+ @package
+  float *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(float)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBFloatArray*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBFloatArray *)array {
+  return [[(GPBFloatArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBFloatArray *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const float [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(float));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(float));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(float))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBFloatArray allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBFloatArray *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBFloatArray class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(float))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%f", _values[i]];
+    } else {
+      [result appendFormat:@", %f", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (float)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(float));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(float))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(float)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const float [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(float));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(float)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(float));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(float)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBFloatArray *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(float));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  float temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Double, double, %lf)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Double
+
+@implementation GPBDoubleArray {
+ @package
+  double *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(double)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBDoubleArray*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBDoubleArray *)array {
+  return [[(GPBDoubleArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBDoubleArray *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const double [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(double));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(double));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(double))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBDoubleArray allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBDoubleArray *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBDoubleArray class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(double))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%lf", _values[i]];
+    } else {
+      [result appendFormat:@", %lf", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (double)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(double));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(double))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(double)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const double [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(double));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(double)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(double));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(double)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBDoubleArray *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(double));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  double temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Bool, BOOL, %d)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool
+
+@implementation GPBBoolArray {
+ @package
+  BOOL *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValues:NULL count:0] autorelease];
+}
+
++ (instancetype)arrayWithValue:(BOOL)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBBoolArray*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBBoolArray *)array {
+  return [[(GPBBoolArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBBoolArray *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    if (count && values) {
+      _values = malloc(count * sizeof(BOOL));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(BOOL));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(BOOL))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolArray allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBBoolArray *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolArray class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(BOOL))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%d", _values[i]];
+    } else {
+      [result appendFormat:@", %d", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (BOOL)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(BOOL));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(BOOL))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(BOOL)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const BOOL [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(BOOL));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(BOOL)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(BOOL));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(BOOL)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBBoolArray *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(BOOL));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  BOOL temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND-END (7 expansions)
+
+#pragma mark - Enum
+
+@implementation GPBEnumArray {
+ @package
+  GPBEnumValidationFunc _validationFunc;
+  int32_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+                                   rawValue:(int32_t)value {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:&value
+                                             count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBEnumArray *)array {
+  return [[(GPBEnumArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+                                   capacity:(NSUInteger)count {
+  return [[[self alloc] initWithValidationFunction:func capacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL count:0];
+}
+
+- (instancetype)initWithValueArray:(GPBEnumArray *)array {
+  return [self initWithValidationFunction:array->_validationFunc
+                                rawValues:array->_values
+                                    count:array->_count];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _validationFunc = (func != NULL ? func : ArrayDefault_IsValidValue);
+    if (count && values) {
+      _values = malloc(count * sizeof(int32_t));
+      if (values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(int32_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(int32_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)count {
+  self = [self initWithValidationFunction:func rawValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBEnumArray allocWithZone:zone]
+             initWithValidationFunction:_validationFunc
+                              rawValues:_values
+                                  count:_count];
+}
+
+//%PDDM-EXPAND ARRAY_IMMUTABLE_CORE(Enum, int32_t, Raw, %d)
+// This block of code is generated, do not edit it directly.
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(GPBEnumArray *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBEnumArray class]]) {
+    return NO;
+  }
+  return (_count == other->_count
+          && memcmp(_values, other->_values, (_count * sizeof(int32_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%d", _values[i]];
+    } else {
+      [result appendFormat:@", %d", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateRawValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateRawValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
+                           usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+//%PDDM-EXPAND-END ARRAY_IMMUTABLE_CORE(Enum, int32_t, Raw, %d)
+
+- (int32_t)valueAtIndex:(NSUInteger)index {
+//%PDDM-EXPAND VALIDATE_RANGE(index, _count)
+// This block of code is generated, do not edit it directly.
+
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+//%PDDM-EXPAND-END VALIDATE_RANGE(index, _count)
+  int32_t result = _values[index];
+  if (!_validationFunc(result)) {
+    result = kGPBUnrecognizedEnumeratorValue;
+  }
+  return result;
+}
+
+- (int32_t)rawValueAtIndex:(NSUInteger)index {
+//%PDDM-EXPAND VALIDATE_RANGE(index, _count)
+// This block of code is generated, do not edit it directly.
+
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+//%PDDM-EXPAND-END VALIDATE_RANGE(index, _count)
+  return _values[index];
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  GPBEnumValidationFunc func = _validationFunc;
+  if ((opts & NSEnumerationReverse) == 0) {
+    int32_t *scan = _values;
+    int32_t *end = scan + _count;
+    for (NSUInteger i = 0; scan < end; ++i, ++scan) {
+      int32_t value = *scan;
+      if (!func(value)) {
+        value = kGPBUnrecognizedEnumeratorValue;
+      }
+      block(value, i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    int32_t *end = _values;
+    int32_t *scan = end + (_count - 1);
+    for (NSUInteger i = (_count - 1); scan >= end; --i, --scan) {
+      int32_t value = *scan;
+      if (!func(value)) {
+        value = kGPBUnrecognizedEnumeratorValue;
+      }
+      block(value, i, &stop);
+      if (stop) break;
+    }
+  }
+}
+
+//%PDDM-EXPAND ARRAY_MUTABLE_CORE(Enum, int32_t, Raw, %d)
+// This block of code is generated, do not edit it directly.
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(int32_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(int32_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addRawValue:(int32_t)value {
+  [self addRawValues:&value count:1];
+}
+
+- (void)addRawValues:(const int32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(int32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertRawValue:(int32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(int32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addRawValuesFromArray:(GPBEnumArray *)array {
+  [self addRawValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(int32_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  int32_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+//%PDDM-EXPAND MUTATION_METHODS(Enum, int32_t, , EnumValidationList, EnumValidationOne)
+// This block of code is generated, do not edit it directly.
+
+- (void)addValue:(int32_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const int32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  GPBEnumValidationFunc func = _validationFunc;
+  for (NSUInteger i = 0; i < count; ++i) {
+    if (!func(values[i])) {
+      [NSException raise:NSInvalidArgumentException
+                  format:@"%@: Attempt to set an unknown enum value (%d)",
+                         [self class], values[i]];
+    }
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  memcpy(&_values[initialCount], values, count * sizeof(int32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@: Attempt to set an unknown enum value (%d)",
+                       [self class], value];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@: Attempt to set an unknown enum value (%d)",
+                       [self class], value];
+  }
+  _values[index] = value;
+}
+//%PDDM-EXPAND-END (2 expansions)
+
+//%PDDM-DEFINE MUTATION_HOOK_EnumValidationList()
+//%  GPBEnumValidationFunc func = _validationFunc;
+//%  for (NSUInteger i = 0; i < count; ++i) {
+//%    if (!func(values[i])) {
+//%      [NSException raise:NSInvalidArgumentException
+//%                  format:@"%@: Attempt to set an unknown enum value (%d)",
+//%                         [self class], values[i]];
+//%    }
+//%  }
+//%
+//%PDDM-DEFINE MUTATION_HOOK_EnumValidationOne()
+//%  if (!_validationFunc(value)) {
+//%    [NSException raise:NSInvalidArgumentException
+//%                format:@"%@: Attempt to set an unknown enum value (%d)",
+//%                       [self class], value];
+//%  }
+//%
+
+@end
+
+#pragma mark - NSArray Subclass
+
+@implementation GPBAutocreatedArray {
+  NSMutableArray *_array;
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator, @"Autocreator must be cleared before release.");
+  [_array release];
+  [super dealloc];
+}
+
+#pragma mark Required NSArray overrides
+
+- (NSUInteger)count {
+  return [_array count];
+}
+
+- (id)objectAtIndex:(NSUInteger)idx {
+  return [_array objectAtIndex:idx];
+}
+
+#pragma mark Required NSMutableArray overrides
+
+// Only need to call GPBAutocreatedArrayModified() when adding things since
+// we only autocreate empty arrays.
+
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)idx {
+  if (_array == nil) {
+    _array = [[NSMutableArray alloc] init];
+  }
+  [_array insertObject:anObject atIndex:idx];
+
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)removeObject:(id)anObject {
+  [_array removeObject:anObject];
+}
+
+- (void)removeObjectAtIndex:(NSUInteger)idx {
+  [_array removeObjectAtIndex:idx];
+}
+
+- (void)addObject:(id)anObject {
+  if (_array == nil) {
+    _array = [[NSMutableArray alloc] init];
+  }
+  [_array addObject:anObject];
+
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)removeLastObject {
+  [_array removeLastObject];
+}
+
+- (void)replaceObjectAtIndex:(NSUInteger)idx withObject:(id)anObject {
+  [_array replaceObjectAtIndex:idx withObject:anObject];
+}
+
+#pragma mark Extra things hooked
+
+- (id)copyWithZone:(NSZone *)zone {
+  if (_array == nil) {
+    _array = [[NSMutableArray alloc] init];
+  }
+  return [_array copyWithZone:zone];
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone {
+  if (_array == nil) {
+    _array = [[NSMutableArray alloc] init];
+  }
+  return [_array mutableCopyWithZone:zone];
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+                                  objects:(id __unsafe_unretained [])buffer
+                                    count:(NSUInteger)len {
+  return [_array countByEnumeratingWithState:state objects:buffer count:len];
+}
+
+- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
+  [_array enumerateObjectsUsingBlock:block];
+}
+
+- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
+                         usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block {
+  [_array enumerateObjectsWithOptions:opts usingBlock:block];
+}
+
+@end

+ 130 - 0
objectivec/GPBArray_PackagePrivate.h

@@ -0,0 +1,130 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBArray.h"
+
+@class GPBMessage;
+
+//%PDDM-DEFINE DECLARE_ARRAY_EXTRAS()
+//%ARRAY_INTERFACE_EXTRAS(Int32, int32_t)
+//%ARRAY_INTERFACE_EXTRAS(UInt32, uint32_t)
+//%ARRAY_INTERFACE_EXTRAS(Int64, int64_t)
+//%ARRAY_INTERFACE_EXTRAS(UInt64, uint64_t)
+//%ARRAY_INTERFACE_EXTRAS(Float, float)
+//%ARRAY_INTERFACE_EXTRAS(Double, double)
+//%ARRAY_INTERFACE_EXTRAS(Bool, BOOL)
+//%ARRAY_INTERFACE_EXTRAS(Enum, int32_t)
+
+//%PDDM-DEFINE ARRAY_INTERFACE_EXTRAS(NAME, TYPE)
+//%#pragma mark - NAME
+//%
+//%@interface GPB##NAME##Array () {
+//% @package
+//%  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+//%}
+//%@end
+//%
+
+//%PDDM-EXPAND DECLARE_ARRAY_EXTRAS()
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+@interface GPBInt32Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - UInt32
+
+@interface GPBUInt32Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Int64
+
+@interface GPBInt64Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - UInt64
+
+@interface GPBUInt64Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Float
+
+@interface GPBFloatArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Double
+
+@interface GPBDoubleArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Bool
+
+@interface GPBBoolArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Enum
+
+@interface GPBEnumArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+//%PDDM-EXPAND-END DECLARE_ARRAY_EXTRAS()
+
+#pragma mark - NSArray Subclass
+
+@interface GPBAutocreatedArray : NSMutableArray {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end

+ 84 - 0
objectivec/GPBBootstrap.h

@@ -0,0 +1,84 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The Objective C runtime has complete enough info that most protos don’t end
+// up using this, so leaving it on is no cost or very little cost.  If you
+// happen to see it causing bloat, this is the way to disable it. If you do
+// need to disable it, try only disabling it for Release builds as having
+// full TextFormat can be useful for debugging.
+#ifndef GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+#define GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS 0
+#endif
+
+// Most uses of protocol buffers don't need field options, by default the
+// static data will be compiled out, define this to 1 to include it. The only
+// time you need this is if you are doing introspection of the protocol buffers.
+#ifndef GPBOBJC_INCLUDE_FIELD_OPTIONS
+#define GPBOBJC_INCLUDE_FIELD_OPTIONS 0
+#endif
+
+// Used in the generated code to give sizes to enums. int32_t was chosen based
+// on the fact that Protocol Buffers enums are limited to this range.
+// The complexity and double definition here are so we get the nice name
+// for objective C, but also define the name with a trailing underscore so
+// the Swift bridge will have one where the names line up to support short
+// names since they are scoped to the enum.
+// https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_11
+#define GPB_ENUM(X) enum X##_ : int32_t X; typedef NS_ENUM(int32_t, X##_)
+// GPB_ENUM_FWD_DECLARE is used for forward declaring enums ex:
+//   GPB_ENUM_FWD_DECLARE(Foo_Enum)
+//   @property (nonatomic) Foo_Enum value;
+#define GPB_ENUM_FWD_DECLARE(_name) enum _name : int32_t
+
+// Based upon CF_INLINE. Forces inlining in release.
+#if !defined(DEBUG)
+#define GPB_INLINE static __inline__ __attribute__((always_inline))
+#else
+#define GPB_INLINE static __inline__
+#endif
+
+// For use in public headers that might need to deal with ARC.
+#ifndef GPB_UNSAFE_UNRETAINED
+#if __has_feature(objc_arc)
+#define GPB_UNSAFE_UNRETAINED __unsafe_unretained
+#else
+#define GPB_UNSAFE_UNRETAINED
+#endif
+#endif
+
+// If property name starts with init we need to annotate it to get past ARC.
+// http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227
+#define GPB_METHOD_FAMILY_NONE __attribute__((objc_method_family(none)))
+
+// The protoc-gen-objc version which works with the current version of the
+// generated Objective C sources.  In general we don't want to change the
+// runtime interfaces (or this version) as it means everything has to be
+// regenerated.
+#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30000

+ 81 - 0
objectivec/GPBCodedInputStream.h

@@ -0,0 +1,81 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+@class GPBMessage;
+@class GPBExtensionRegistry;
+
+// Reads and decodes protocol message fields.
+// Subclassing of GPBCodedInputStream is NOT supported.
+@interface GPBCodedInputStream : NSObject
+
++ (instancetype)streamWithData:(NSData *)data;
+- (instancetype)initWithData:(NSData *)data;
+
+// Attempt to read a field tag, returning zero if we have reached EOF.
+// Protocol message parsers use this to read tags, since a protocol message
+// may legally end wherever a tag occurs, and zero is not a valid tag number.
+- (int32_t)readTag;
+
+- (double)readDouble;
+- (float)readFloat;
+- (uint64_t)readUInt64;
+- (uint32_t)readUInt32;
+- (int64_t)readInt64;
+- (int32_t)readInt32;
+- (uint64_t)readFixed64;
+- (uint32_t)readFixed32;
+- (int32_t)readEnum;
+- (int32_t)readSFixed32;
+- (int64_t)readSFixed64;
+- (int32_t)readSInt32;
+- (int64_t)readSInt64;
+- (BOOL)readBool;
+- (NSString *)readString;
+- (NSData *)readData;
+
+// Read an embedded message field value from the stream.
+- (void)readMessage:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+
+// Reads and discards a single field, given its tag value.
+- (BOOL)skipField:(int32_t)tag;
+
+// Reads and discards an entire message.  This will read either until EOF
+// or until an endgroup tag, whichever comes first.
+- (void)skipMessage;
+
+// Verifies that the last call to readTag() returned the given tag value.
+// This is used to verify that a nested group ended with the correct
+// end tag.
+- (void)checkLastTagWas:(int32_t)value;
+
+@end

+ 801 - 0
objectivec/GPBCodedInputStream.m

@@ -0,0 +1,801 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+#import "GPBWireFormat.h"
+
+static const NSUInteger kDefaultRecursionLimit = 64;
+
+static inline void CheckSize(GPBCodedInputStreamState *state, size_t size) {
+  size_t newSize = state->bufferPos + size;
+  if (newSize > state->bufferSize) {
+    [NSException raise:NSParseErrorException format:@""];
+  }
+  if (newSize > state->currentLimit) {
+    // Fast forward to end of currentLimit;
+    state->bufferPos = state->currentLimit;
+    [NSException raise:NSParseErrorException format:@""];
+  }
+}
+
+static inline int8_t ReadRawByte(GPBCodedInputStreamState *state) {
+  CheckSize(state, sizeof(int8_t));
+  return ((int8_t *)state->bytes)[state->bufferPos++];
+}
+
+static inline int32_t ReadRawLittleEndian32(GPBCodedInputStreamState *state) {
+  CheckSize(state, sizeof(int32_t));
+  int32_t value = OSReadLittleInt32(state->bytes, state->bufferPos);
+  state->bufferPos += sizeof(int32_t);
+  return value;
+}
+
+static inline int64_t ReadRawLittleEndian64(GPBCodedInputStreamState *state) {
+  CheckSize(state, sizeof(int64_t));
+  int64_t value = OSReadLittleInt64(state->bytes, state->bufferPos);
+  state->bufferPos += sizeof(int64_t);
+  return value;
+}
+
+static inline int32_t ReadRawVarint32(GPBCodedInputStreamState *state) {
+  int8_t tmp = ReadRawByte(state);
+  if (tmp >= 0) {
+    return tmp;
+  }
+  int32_t result = tmp & 0x7f;
+  if ((tmp = ReadRawByte(state)) >= 0) {
+    result |= tmp << 7;
+  } else {
+    result |= (tmp & 0x7f) << 7;
+    if ((tmp = ReadRawByte(state)) >= 0) {
+      result |= tmp << 14;
+    } else {
+      result |= (tmp & 0x7f) << 14;
+      if ((tmp = ReadRawByte(state)) >= 0) {
+        result |= tmp << 21;
+      } else {
+        result |= (tmp & 0x7f) << 21;
+        result |= (tmp = ReadRawByte(state)) << 28;
+        if (tmp < 0) {
+          // Discard upper 32 bits.
+          for (int i = 0; i < 5; i++) {
+            if (ReadRawByte(state) >= 0) {
+              return result;
+            }
+          }
+          [NSException raise:NSParseErrorException
+                      format:@"Unable to read varint32"];
+        }
+      }
+    }
+  }
+  return result;
+}
+
+static inline int64_t ReadRawVarint64(GPBCodedInputStreamState *state) {
+  int32_t shift = 0;
+  int64_t result = 0;
+  while (shift < 64) {
+    int8_t b = ReadRawByte(state);
+    result |= (int64_t)(b & 0x7F) << shift;
+    if ((b & 0x80) == 0) {
+      return result;
+    }
+    shift += 7;
+  }
+  [NSException raise:NSParseErrorException format:@"Unable to read varint64"];
+  return 0;
+}
+
+static inline void SkipRawData(GPBCodedInputStreamState *state, size_t size) {
+  CheckSize(state, size);
+  state->bufferPos += size;
+}
+
+double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state) {
+  int64_t value = ReadRawLittleEndian64(state);
+  return GPBConvertInt64ToDouble(value);
+}
+
+float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawLittleEndian32(state);
+  return GPBConvertInt32ToFloat(value);
+}
+
+uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state) {
+  uint64_t value = ReadRawVarint64(state);
+  return value;
+}
+
+uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state) {
+  uint32_t value = ReadRawVarint32(state);
+  return value;
+}
+
+int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state) {
+  int64_t value = ReadRawVarint64(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawVarint32(state);
+  return value;
+}
+
+uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state) {
+  uint64_t value = ReadRawLittleEndian64(state);
+  return value;
+}
+
+uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state) {
+  uint32_t value = ReadRawLittleEndian32(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawVarint32(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawLittleEndian32(state);
+  return value;
+}
+
+int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state) {
+  int64_t value = ReadRawLittleEndian64(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state) {
+  int32_t value = GPBDecodeZigZag32(ReadRawVarint32(state));
+  return value;
+}
+
+int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state) {
+  int64_t value = GPBDecodeZigZag64(ReadRawVarint64(state));
+  return value;
+}
+
+BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state) {
+  return ReadRawVarint32(state) != 0;
+}
+
+int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
+  if (GPBCodedInputStreamIsAtEnd(state)) {
+    state->lastTag = 0;
+    return 0;
+  }
+
+  state->lastTag = ReadRawVarint32(state);
+  if (state->lastTag == 0) {
+    // If we actually read zero, that's not a valid tag.
+    [NSException raise:NSParseErrorException
+                format:@"Invalid last tag %d", state->lastTag];
+  }
+  return state->lastTag;
+}
+
+NSString *GPBCodedInputStreamReadRetainedString(
+    GPBCodedInputStreamState *state) {
+  int32_t size = ReadRawVarint32(state);
+  NSString *result;
+  if (size == 0) {
+    result = @"";
+  } else {
+    CheckSize(state, size);
+    result = GPBCreateGPBStringWithUTF8(&state->bytes[state->bufferPos], size);
+    state->bufferPos += size;
+  }
+  return result;
+}
+
+NSData *GPBCodedInputStreamReadRetainedData(GPBCodedInputStreamState *state) {
+  int32_t size = ReadRawVarint32(state);
+  if (size < 0) return nil;
+  CheckSize(state, size);
+  NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos
+                                          length:size];
+  state->bufferPos += size;
+  return result;
+}
+
+NSData *GPBCodedInputStreamReadRetainedDataNoCopy(
+    GPBCodedInputStreamState *state) {
+  int32_t size = ReadRawVarint32(state);
+  if (size < 0) return nil;
+  CheckSize(state, size);
+  // Cast is safe because freeWhenDone is NO.
+  NSData *result = [[NSData alloc]
+      initWithBytesNoCopy:(void *)(state->bytes + state->bufferPos)
+                   length:size
+             freeWhenDone:NO];
+  state->bufferPos += size;
+  return result;
+}
+
+size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state,
+                                    size_t byteLimit) {
+  byteLimit += state->bufferPos;
+  size_t oldLimit = state->currentLimit;
+  if (byteLimit > oldLimit) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"byteLimit > oldLimit: %tu > %tu", byteLimit, oldLimit];
+  }
+  state->currentLimit = byteLimit;
+  return oldLimit;
+}
+
+void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state,
+                                 size_t oldLimit) {
+  state->currentLimit = oldLimit;
+}
+
+size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state) {
+  if (state->currentLimit == SIZE_T_MAX) {
+    return state->currentLimit;
+  }
+
+  return state->currentLimit - state->bufferPos;
+}
+
+BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) {
+  return (state->bufferPos == state->bufferSize) ||
+         (state->bufferPos == state->currentLimit);
+}
+
+void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
+                                        int32_t value) {
+  if (state->lastTag != value) {
+    [NSException raise:NSParseErrorException
+                format:@"Last tag: %d should be %d", state->lastTag, value];
+  }
+}
+
+@implementation GPBCodedInputStream
+
++ (instancetype)streamWithData:(NSData *)data {
+  return [[[self alloc] initWithData:data] autorelease];
+}
+
+- (instancetype)initWithData:(NSData *)data {
+  if ((self = [super init])) {
+#ifdef DEBUG
+    NSCAssert([self class] == [GPBCodedInputStream class],
+              @"Subclassing of GPBCodedInputStream is not allowed.");
+#endif
+    buffer_ = [data retain];
+    state_.bytes = (const uint8_t *)[data bytes];
+    state_.bufferSize = [data length];
+    state_.currentLimit = NSUIntegerMax;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [buffer_ release];
+  [super dealloc];
+}
+
+- (int32_t)readTag {
+  return GPBCodedInputStreamReadTag(&state_);
+}
+
+- (void)checkLastTagWas:(int32_t)value {
+  GPBCodedInputStreamCheckLastTagWas(&state_, value);
+}
+
+- (BOOL)skipField:(int32_t)tag {
+  switch (GPBWireFormatGetTagWireType(tag)) {
+    case GPBWireFormatVarint:
+      GPBCodedInputStreamReadInt32(&state_);
+      return YES;
+    case GPBWireFormatFixed64:
+      SkipRawData(&state_, sizeof(int64_t));
+      return YES;
+    case GPBWireFormatLengthDelimited:
+      SkipRawData(&state_, ReadRawVarint32(&state_));
+      return YES;
+    case GPBWireFormatStartGroup:
+      [self skipMessage];
+      GPBCodedInputStreamCheckLastTagWas(
+          &state_, GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag),
+                                        GPBWireFormatEndGroup));
+      return YES;
+    case GPBWireFormatEndGroup:
+      return NO;
+    case GPBWireFormatFixed32:
+      SkipRawData(&state_, sizeof(int32_t));
+      return YES;
+  }
+  [NSException raise:NSParseErrorException format:@"Invalid tag %d", tag];
+  return NO;
+}
+
+- (void)skipMessage {
+  while (YES) {
+    int32_t tag = GPBCodedInputStreamReadTag(&state_);
+    if (tag == 0 || ![self skipField:tag]) {
+      return;
+    }
+  }
+}
+
+- (double)readDouble {
+  return GPBCodedInputStreamReadDouble(&state_);
+}
+
+- (float)readFloat {
+  return GPBCodedInputStreamReadFloat(&state_);
+}
+
+- (uint64_t)readUInt64 {
+  return GPBCodedInputStreamReadUInt64(&state_);
+}
+
+- (int64_t)readInt64 {
+  return GPBCodedInputStreamReadInt64(&state_);
+}
+
+- (int32_t)readInt32 {
+  return GPBCodedInputStreamReadInt32(&state_);
+}
+
+- (uint64_t)readFixed64 {
+  return GPBCodedInputStreamReadFixed64(&state_);
+}
+
+- (uint32_t)readFixed32 {
+  return GPBCodedInputStreamReadFixed32(&state_);
+}
+
+- (BOOL)readBool {
+  return GPBCodedInputStreamReadBool(&state_);
+}
+
+- (NSString *)readString {
+  return [GPBCodedInputStreamReadRetainedString(&state_) autorelease];
+}
+
+- (void)readGroup:(int32_t)fieldNumber
+              message:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  if (state_.recursionDepth >= kDefaultRecursionLimit) {
+    [NSException raise:NSParseErrorException
+                format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
+                       kDefaultRecursionLimit];
+  }
+  ++state_.recursionDepth;
+  [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
+  GPBCodedInputStreamCheckLastTagWas(
+      &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup));
+  --state_.recursionDepth;
+}
+
+- (void)readUnknownGroup:(int32_t)fieldNumber
+                 message:(GPBUnknownFieldSet *)message {
+  if (state_.recursionDepth >= kDefaultRecursionLimit) {
+    [NSException raise:NSParseErrorException
+                format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
+                       kDefaultRecursionLimit];
+  }
+  ++state_.recursionDepth;
+  [message mergeFromCodedInputStream:self];
+  GPBCodedInputStreamCheckLastTagWas(
+      &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup));
+  --state_.recursionDepth;
+}
+
+- (void)readMessage:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  int32_t length = ReadRawVarint32(&state_);
+  if (state_.recursionDepth >= kDefaultRecursionLimit) {
+    [NSException raise:NSParseErrorException
+                format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
+                       kDefaultRecursionLimit];
+  }
+  size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
+  ++state_.recursionDepth;
+  [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
+  GPBCodedInputStreamCheckLastTagWas(&state_, 0);
+  --state_.recursionDepth;
+  GPBCodedInputStreamPopLimit(&state_, oldLimit);
+}
+
+- (void)readMapEntry:(id)mapDictionary
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                field:(GPBFieldDescriptor *)field
+        parentMessage:(GPBMessage *)parentMessage {
+  int32_t length = ReadRawVarint32(&state_);
+  if (state_.recursionDepth >= kDefaultRecursionLimit) {
+    [NSException raise:NSParseErrorException
+                format:@"recursionDepth(%tu) >= %tu", state_.recursionDepth,
+                       kDefaultRecursionLimit];
+  }
+  size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
+  ++state_.recursionDepth;
+  GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field,
+                         parentMessage);
+  GPBCodedInputStreamCheckLastTagWas(&state_, 0);
+  --state_.recursionDepth;
+  GPBCodedInputStreamPopLimit(&state_, oldLimit);
+}
+
+- (NSData *)readData {
+  return [GPBCodedInputStreamReadRetainedData(&state_) autorelease];
+}
+
+- (uint32_t)readUInt32 {
+  return GPBCodedInputStreamReadUInt32(&state_);
+}
+
+- (int32_t)readEnum {
+  return GPBCodedInputStreamReadEnum(&state_);
+}
+
+- (int32_t)readSFixed32 {
+  return GPBCodedInputStreamReadSFixed32(&state_);
+}
+
+- (int64_t)readSFixed64 {
+  return GPBCodedInputStreamReadSFixed64(&state_);
+}
+
+- (int32_t)readSInt32 {
+  return GPBCodedInputStreamReadSInt32(&state_);
+}
+
+- (int64_t)readSInt64 {
+  return GPBCodedInputStreamReadSInt64(&state_);
+}
+
+@end
+
+@implementation GPBString {
+ @package
+  CFStringRef string_;
+  unsigned char *utf8_;
+  NSUInteger utf8Len_;
+
+  // This lock is used to gate access to utf8_.  Once GPBStringInitStringValue()
+  // has been called, string_ will be filled in, and utf8_ will be NULL.
+  OSSpinLock lock_;
+
+  BOOL hasBOM_;
+  BOOL is7BitAscii_;
+}
+
+// Returns true if the passed in bytes are 7 bit ascii.
+// This routine needs to be fast.
+static inline bool AreBytesIn7BitASCII(const uint8_t *bytes, NSUInteger len) {
+// In the loops below, it's more efficient to collect rather than do
+// conditional at every step.
+#if __LP64__
+  // Align bytes. This is especially important in case of 3 byte BOM.
+  while (len > 0 && ((size_t)bytes & 0x07)) {
+    if (*bytes++ & 0x80) return false;
+    len--;
+  }
+  while (len >= 32) {
+    uint64_t val = *(const uint64_t *)bytes;
+    uint64_t hiBits = (val & 0x8080808080808080ULL);
+    bytes += 8;
+    val = *(const uint64_t *)bytes;
+    hiBits |= (val & 0x8080808080808080ULL);
+    bytes += 8;
+    val = *(const uint64_t *)bytes;
+    hiBits |= (val & 0x8080808080808080ULL);
+    bytes += 8;
+    val = *(const uint64_t *)bytes;
+    if (hiBits | (val & 0x8080808080808080ULL)) return false;
+    bytes += 8;
+    len -= 32;
+  }
+
+  while (len >= 16) {
+    uint64_t val = *(const uint64_t *)bytes;
+    uint64_t hiBits = (val & 0x8080808080808080ULL);
+    bytes += 8;
+    val = *(const uint64_t *)bytes;
+    if (hiBits | (val & 0x8080808080808080ULL)) return false;
+    bytes += 8;
+    len -= 16;
+  }
+
+  while (len >= 8) {
+    uint64_t val = *(const uint64_t *)bytes;
+    if (val & 0x8080808080808080ULL) return false;
+    bytes += 8;
+    len -= 8;
+  }
+#else   // __LP64__
+  // Align bytes. This is especially important in case of 3 byte BOM.
+  while (len > 0 && ((size_t)bytes & 0x03)) {
+    if (*bytes++ & 0x80) return false;
+    len--;
+  }
+  while (len >= 16) {
+    uint32_t val = *(const uint32_t *)bytes;
+    uint32_t hiBits = (val & 0x80808080U);
+    bytes += 4;
+    val = *(const uint32_t *)bytes;
+    hiBits |= (val & 0x80808080U);
+    bytes += 4;
+    val = *(const uint32_t *)bytes;
+    hiBits |= (val & 0x80808080U);
+    bytes += 4;
+    val = *(const uint32_t *)bytes;
+    if (hiBits | (val & 0x80808080U)) return false;
+    bytes += 4;
+    len -= 16;
+  }
+
+  while (len >= 8) {
+    uint32_t val = *(const uint32_t *)bytes;
+    uint32_t hiBits = (val & 0x80808080U);
+    bytes += 4;
+    val = *(const uint32_t *)bytes;
+    if (hiBits | (val & 0x80808080U)) return false;
+    bytes += 4;
+    len -= 8;
+  }
+#endif  // __LP64__
+
+  while (len >= 4) {
+    uint32_t val = *(const uint32_t *)bytes;
+    if (val & 0x80808080U) return false;
+    bytes += 4;
+    len -= 4;
+  }
+
+  while (len--) {
+    if (*bytes++ & 0x80) return false;
+  }
+
+  return true;
+}
+
+static inline void GPBStringInitStringValue(GPBString *string) {
+  OSSpinLockLock(&string->lock_);
+  GPBStringInitStringValueAlreadyLocked(string);
+  OSSpinLockUnlock(&string->lock_);
+}
+
+static void GPBStringInitStringValueAlreadyLocked(GPBString *string) {
+  if (string->string_ == NULL && string->utf8_ != NULL) {
+    // Using kCFAllocatorMalloc for contentsDeallocator, as buffer in
+    // string->utf8_ is being handed off.
+    string->string_ = CFStringCreateWithBytesNoCopy(
+        NULL, string->utf8_, string->utf8Len_, kCFStringEncodingUTF8, false,
+        kCFAllocatorMalloc);
+    if (!string->string_) {
+#ifdef DEBUG
+      // https://developers.google.com/protocol-buffers/docs/proto#scalar
+      NSLog(@"UTF8 failure, is some field type 'string' when it should be "
+            @"'bytes'?");
+#endif
+      string->string_ = CFSTR("");
+      string->utf8Len_ = 0;
+      // On failure, we have to clean up the buffer.
+      free(string->utf8_);
+    }
+    string->utf8_ = NULL;
+  }
+}
+
+GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length) {
+  GPBString *result = [[GPBString alloc] initWithBytes:bytes length:length];
+  return result;
+}
+
+- (instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)length {
+  self = [super init];
+  if (self) {
+    utf8_ = malloc(length);
+    memcpy(utf8_, bytes, length);
+    utf8Len_ = length;
+    lock_ = OS_SPINLOCK_INIT;
+    is7BitAscii_ = AreBytesIn7BitASCII(bytes, length);
+    if (length >= 3 && memcmp(utf8_, "\xef\xbb\xbf", 3) == 0) {
+      // We can't just remove the BOM from the string here, because in the case
+      // where we have > 1 BOM at the beginning of the string, we will remove one,
+      // and the internal NSString we create will remove the next one, and we will
+      // end up with a GPBString != NSString issue.
+      // We also just can't remove all the BOMs because then we would end up with
+      // potential cases where a GPBString and an NSString made with the same
+      // UTF8 buffer would in fact be different.
+      // We record the fact we have a BOM, and use it as necessary to simulate
+      // what NSString would return for various calls.
+      hasBOM_ = YES;
+#if DEBUG
+      // Sending BOMs across the line is just wasting bits.
+      NSLog(@"Bad data? String should not have BOM!");
+#endif  // DEBUG
+    }
+  }
+  return self;
+}
+
+- (void)dealloc {
+  if (string_ != NULL) {
+    CFRelease(string_);
+  }
+  if (utf8_ != NULL) {
+    free(utf8_);
+  }
+  [super dealloc];
+}
+
+// Required NSString overrides.
+- (NSUInteger)length {
+  if (is7BitAscii_) {
+    return utf8Len_;
+  } else {
+    GPBStringInitStringValue(self);
+    return CFStringGetLength(string_);
+  }
+}
+
+- (unichar)characterAtIndex:(NSUInteger)anIndex {
+  OSSpinLockLock(&lock_);
+  if (is7BitAscii_ && utf8_) {
+    unichar result = utf8_[anIndex];
+    OSSpinLockUnlock(&lock_);
+    return result;
+  } else {
+    GPBStringInitStringValueAlreadyLocked(self);
+    OSSpinLockUnlock(&lock_);
+    return CFStringGetCharacterAtIndex(string_, anIndex);
+  }
+}
+
+// Override a couple of methods that typically want high performance.
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBStringInitStringValue(self);
+  return [(NSString *)string_ copyWithZone:zone];
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone {
+  GPBStringInitStringValue(self);
+  return [(NSString *)string_ mutableCopyWithZone:zone];
+}
+
+- (NSUInteger)hash {
+  // Must convert to string here to make sure that the hash is always
+  // consistent no matter what state the GPBString is in.
+  GPBStringInitStringValue(self);
+  return CFHash(string_);
+}
+
+- (BOOL)isEqual:(id)object {
+  if (self == object) {
+    return YES;
+  }
+  if ([object isKindOfClass:[NSString class]]) {
+    GPBStringInitStringValue(self);
+    return CFStringCompare(string_, (CFStringRef)object, 0) ==
+           kCFCompareEqualTo;
+  }
+  return NO;
+}
+
+- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange {
+  OSSpinLockLock(&lock_);
+  if (is7BitAscii_ && utf8_) {
+    unsigned char *bytes = &(utf8_[aRange.location]);
+    for (NSUInteger i = 0; i < aRange.length; ++i) {
+      buffer[i] = bytes[i];
+    }
+    OSSpinLockUnlock(&lock_);
+  } else {
+    GPBStringInitStringValueAlreadyLocked(self);
+    OSSpinLockUnlock(&lock_);
+    CFStringGetCharacters(string_, CFRangeMake(aRange.location, aRange.length),
+                          buffer);
+  }
+}
+
+- (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)encoding {
+  if ((encoding == NSUTF8StringEncoding) ||
+      (encoding == NSASCIIStringEncoding && is7BitAscii_)) {
+    return utf8Len_ - (hasBOM_ ? 3 : 0);
+  } else {
+    GPBStringInitStringValue(self);
+    return [(NSString *)string_ lengthOfBytesUsingEncoding:encoding];
+  }
+}
+
+- (BOOL)getBytes:(void *)buffer
+         maxLength:(NSUInteger)maxLength
+        usedLength:(NSUInteger *)usedLength
+          encoding:(NSStringEncoding)encoding
+           options:(NSStringEncodingConversionOptions)options
+             range:(NSRange)range
+    remainingRange:(NSRangePointer)remainingRange {
+  // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
+  // does not return reliable results if the maxLength argument is 0
+  // (Radar 16385183). Therefore we have special cased it as a slow case so
+  // that it behaves however Apple intends it to behave. It should be a rare
+  // case.
+  //
+  // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
+  // does not return reliable results if the range is outside of the strings
+  // length (Radar 16396177). Therefore we have special cased it as a slow
+  // case so that it behaves however Apple intends it to behave.  It should
+  // be a rare case.
+  //
+  // We can optimize the UTF8StringEncoding and NSASCIIStringEncoding with no
+  // options cases.
+  if ((options == 0) &&
+      (encoding == NSUTF8StringEncoding || encoding == NSASCIIStringEncoding) &&
+      (maxLength != 0) &&
+      (NSMaxRange(range) <= utf8Len_)) {
+    // Might be able to optimize it.
+    OSSpinLockLock(&lock_);
+    if (is7BitAscii_ && utf8_) {
+      NSUInteger length = range.length;
+      length = (length < maxLength) ? length : maxLength;
+      memcpy(buffer, utf8_ + range.location, length);
+      if (usedLength) {
+        *usedLength = length;
+      }
+      if (remainingRange) {
+        remainingRange->location = range.location + length;
+        remainingRange->length = range.length - length;
+      }
+      OSSpinLockUnlock(&lock_);
+      if (length > 0) {
+        return YES;
+      } else {
+        return NO;
+      }
+    } else {
+      GPBStringInitStringValueAlreadyLocked(self);
+      OSSpinLockUnlock(&lock_);
+    }
+  } else {
+    GPBStringInitStringValue(self);
+  }
+  return [(NSString *)string_ getBytes:buffer
+                             maxLength:maxLength
+                            usedLength:usedLength
+                              encoding:encoding
+                               options:options
+                                 range:range
+                        remainingRange:remainingRange];
+}
+
+@end

+ 131 - 0
objectivec/GPBCodedInputStream_PackagePrivate.h

@@ -0,0 +1,131 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This header is private to the ProtobolBuffers library and must NOT be
+// included by any sources outside this library. The contents of this file are
+// subject to change at any time without notice.
+
+#import "GPBCodedInputStream.h"
+
+#import <libkern/OSAtomic.h>
+
+@class GPBUnknownFieldSet;
+@class GPBFieldDescriptor;
+
+// GPBString is a string subclass that avoids the overhead of initializing
+// a full NSString until it is actually needed. Lots of protocol buffers contain
+// strings, and instantiating all of those strings and having them parsed to
+// verify correctness when the message was being read was expensive, when many
+// of the strings were never being used.
+//
+// Note for future-self. I tried implementing this using a NSProxy.
+// Turned out the performance was horrible in client apps because folks
+// like to use libraries like SBJSON that grab characters one at a time.
+// The proxy overhead was a killer.
+@interface GPBString : NSString
+@end
+
+typedef struct GPBCodedInputStreamState {
+  const uint8_t *bytes;
+  size_t bufferSize;
+  size_t bufferPos;
+
+  // For parsing subsections of an input stream you can put a hard limit on
+  // how much should be read. Normally the limit is the end of the stream,
+  // but you can adjust it to anywhere, and if you hit it you will be at the
+  // end of the stream, until you adjust the limit.
+  size_t currentLimit;
+  int32_t lastTag;
+  NSUInteger recursionDepth;
+} GPBCodedInputStreamState;
+
+@interface GPBCodedInputStream () {
+ @package
+  struct GPBCodedInputStreamState state_;
+  NSData *buffer_;
+}
+
+// Group support is deprecated, so we hide this interface from users, but
+// support for older data.
+- (void)readGroup:(int32_t)fieldNumber
+              message:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+
+// Reads a group field value from the stream and merges it into the given
+// UnknownFieldSet.
+- (void)readUnknownGroup:(int32_t)fieldNumber
+                 message:(GPBUnknownFieldSet *)message;
+
+// Reads a map entry.
+- (void)readMapEntry:(id)mapDictionary
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                field:(GPBFieldDescriptor *)field
+        parentMessage:(GPBMessage *)parentMessage;
+@end
+
+CF_EXTERN_C_BEGIN
+
+// Returns a GPBString with a +1 retain count.
+GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length)
+    __attribute__((ns_returns_retained));
+
+int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state);
+
+double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state);
+float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state);
+uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state);
+uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state);
+int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state);
+uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state);
+uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state);
+int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state);
+int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state);
+BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state);
+NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state)
+    __attribute((ns_returns_retained));
+NSData *GPBCodedInputStreamReadRetainedData(GPBCodedInputStreamState *state)
+    __attribute((ns_returns_retained));
+NSData *GPBCodedInputStreamReadRetainedDataNoCopy(
+    GPBCodedInputStreamState *state) __attribute((ns_returns_retained));
+
+size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state,
+                                    size_t byteLimit);
+void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state,
+                                 size_t oldLimit);
+size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state);
+BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state);
+void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
+                                        int32_t value);
+
+CF_EXTERN_C_END

+ 340 - 0
objectivec/GPBCodedOutputStream.h

@@ -0,0 +1,340 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBTypes.h"
+#import "GPBWireFormat.h"
+
+@class GPBBoolArray;
+@class GPBDoubleArray;
+@class GPBEnumArray;
+@class GPBFloatArray;
+@class GPBMessage;
+@class GPBInt32Array;
+@class GPBInt64Array;
+@class GPBUInt32Array;
+@class GPBUInt64Array;
+@class GPBUnknownFieldSet;
+
+@interface GPBCodedOutputStream : NSObject
+
+// Creates a new stream to write into data.  Data must be sized to fit or it
+// will error when it runs out of space.
++ (instancetype)streamWithData:(NSMutableData *)data;
++ (instancetype)streamWithOutputStream:(NSOutputStream *)output;
++ (instancetype)streamWithOutputStream:(NSOutputStream *)output
+                            bufferSize:(size_t)bufferSize;
+
+- (instancetype)initWithOutputStream:(NSOutputStream *)output;
+- (instancetype)initWithData:(NSMutableData *)data;
+- (instancetype)initWithOutputStream:(NSOutputStream *)output
+                          bufferSize:(size_t)bufferSize;
+- (instancetype)initWithOutputStream:(NSOutputStream *)output
+                                data:(NSMutableData *)data;
+
+- (void)flush;
+
+- (void)writeRawByte:(uint8_t)value;
+
+- (void)writeTag:(uint32_t)fieldNumber format:(GPBWireFormat)format;
+
+- (void)writeRawLittleEndian32:(int32_t)value;
+- (void)writeRawLittleEndian64:(int64_t)value;
+
+- (void)writeRawVarint32:(int32_t)value;
+- (void)writeRawVarint64:(int64_t)value;
+
+// Note that this will truncate 64 bit values to 32.
+- (void)writeRawVarintSizeTAs32:(size_t)value;
+
+- (void)writeRawData:(NSData *)data;
+- (void)writeRawPtr:(const void *)data
+             offset:(size_t)offset
+             length:(size_t)length;
+
+//%PDDM-EXPAND _WRITE_DECLS()
+// This block of code is generated, do not edit it directly.
+
+- (void)writeDouble:(int32_t)fieldNumber value:(double)value;
+- (void)writeDoubles:(int32_t)fieldNumber
+              values:(GPBDoubleArray *)values
+                 tag:(uint32_t)tag;
+- (void)writeDoubleNoTag:(double)value;
+
+- (void)writeFloat:(int32_t)fieldNumber value:(float)value;
+- (void)writeFloats:(int32_t)fieldNumber
+             values:(GPBFloatArray *)values
+                tag:(uint32_t)tag;
+- (void)writeFloatNoTag:(float)value;
+
+- (void)writeUInt64:(int32_t)fieldNumber value:(uint64_t)value;
+- (void)writeUInt64s:(int32_t)fieldNumber
+              values:(GPBUInt64Array *)values
+                 tag:(uint32_t)tag;
+- (void)writeUInt64NoTag:(uint64_t)value;
+
+- (void)writeInt64:(int32_t)fieldNumber value:(int64_t)value;
+- (void)writeInt64s:(int32_t)fieldNumber
+             values:(GPBInt64Array *)values
+                tag:(uint32_t)tag;
+- (void)writeInt64NoTag:(int64_t)value;
+
+- (void)writeInt32:(int32_t)fieldNumber value:(int32_t)value;
+- (void)writeInt32s:(int32_t)fieldNumber
+             values:(GPBInt32Array *)values
+                tag:(uint32_t)tag;
+- (void)writeInt32NoTag:(int32_t)value;
+
+- (void)writeUInt32:(int32_t)fieldNumber value:(uint32_t)value;
+- (void)writeUInt32s:(int32_t)fieldNumber
+              values:(GPBUInt32Array *)values
+                 tag:(uint32_t)tag;
+- (void)writeUInt32NoTag:(uint32_t)value;
+
+- (void)writeFixed64:(int32_t)fieldNumber value:(uint64_t)value;
+- (void)writeFixed64s:(int32_t)fieldNumber
+               values:(GPBUInt64Array *)values
+                  tag:(uint32_t)tag;
+- (void)writeFixed64NoTag:(uint64_t)value;
+
+- (void)writeFixed32:(int32_t)fieldNumber value:(uint32_t)value;
+- (void)writeFixed32s:(int32_t)fieldNumber
+               values:(GPBUInt32Array *)values
+                  tag:(uint32_t)tag;
+- (void)writeFixed32NoTag:(uint32_t)value;
+
+- (void)writeSInt32:(int32_t)fieldNumber value:(int32_t)value;
+- (void)writeSInt32s:(int32_t)fieldNumber
+              values:(GPBInt32Array *)values
+                 tag:(uint32_t)tag;
+- (void)writeSInt32NoTag:(int32_t)value;
+
+- (void)writeSInt64:(int32_t)fieldNumber value:(int64_t)value;
+- (void)writeSInt64s:(int32_t)fieldNumber
+              values:(GPBInt64Array *)values
+                 tag:(uint32_t)tag;
+- (void)writeSInt64NoTag:(int64_t)value;
+
+- (void)writeSFixed64:(int32_t)fieldNumber value:(int64_t)value;
+- (void)writeSFixed64s:(int32_t)fieldNumber
+                values:(GPBInt64Array *)values
+                   tag:(uint32_t)tag;
+- (void)writeSFixed64NoTag:(int64_t)value;
+
+- (void)writeSFixed32:(int32_t)fieldNumber value:(int32_t)value;
+- (void)writeSFixed32s:(int32_t)fieldNumber
+                values:(GPBInt32Array *)values
+                   tag:(uint32_t)tag;
+- (void)writeSFixed32NoTag:(int32_t)value;
+
+- (void)writeBool:(int32_t)fieldNumber value:(BOOL)value;
+- (void)writeBools:(int32_t)fieldNumber
+            values:(GPBBoolArray *)values
+               tag:(uint32_t)tag;
+- (void)writeBoolNoTag:(BOOL)value;
+
+- (void)writeEnum:(int32_t)fieldNumber value:(int32_t)value;
+- (void)writeEnums:(int32_t)fieldNumber
+            values:(GPBEnumArray *)values
+               tag:(uint32_t)tag;
+- (void)writeEnumNoTag:(int32_t)value;
+
+- (void)writeString:(int32_t)fieldNumber value:(NSString *)value;
+- (void)writeStrings:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeStringNoTag:(NSString *)value;
+
+- (void)writeMessage:(int32_t)fieldNumber value:(GPBMessage *)value;
+- (void)writeMessages:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeMessageNoTag:(GPBMessage *)value;
+
+- (void)writeData:(int32_t)fieldNumber value:(NSData *)value;
+- (void)writeDatas:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeDataNoTag:(NSData *)value;
+
+- (void)writeGroup:(int32_t)fieldNumber
+             value:(GPBMessage *)value;
+- (void)writeGroups:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeGroupNoTag:(int32_t)fieldNumber
+                  value:(GPBMessage *)value;
+
+- (void)writeUnknownGroup:(int32_t)fieldNumber
+                    value:(GPBUnknownFieldSet *)value;
+- (void)writeUnknownGroups:(int32_t)fieldNumber values:(NSArray *)values;
+- (void)writeUnknownGroupNoTag:(int32_t)fieldNumber
+                         value:(GPBUnknownFieldSet *)value;
+
+//%PDDM-EXPAND-END _WRITE_DECLS()
+
+// Write a MessageSet extension field to the stream.  For historical reasons,
+// the wire format differs from normal fields.
+- (void)writeMessageSetExtension:(int32_t)fieldNumber value:(GPBMessage *)value;
+
+// Write an unparsed MessageSet extension field to the stream.  For
+// historical reasons, the wire format differs from normal fields.
+- (void)writeRawMessageSetExtension:(int32_t)fieldNumber value:(NSData *)value;
+
+@end
+
+CF_EXTERN_C_BEGIN
+
+size_t GPBComputeDoubleSize(int32_t fieldNumber, double value)
+    __attribute__((const));
+size_t GPBComputeFloatSize(int32_t fieldNumber, float value)
+    __attribute__((const));
+size_t GPBComputeUInt64Size(int32_t fieldNumber, uint64_t value)
+    __attribute__((const));
+size_t GPBComputeInt64Size(int32_t fieldNumber, int64_t value)
+    __attribute__((const));
+size_t GPBComputeInt32Size(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+size_t GPBComputeFixed64Size(int32_t fieldNumber, uint64_t value)
+    __attribute__((const));
+size_t GPBComputeFixed32Size(int32_t fieldNumber, uint32_t value)
+    __attribute__((const));
+size_t GPBComputeBoolSize(int32_t fieldNumber, BOOL value)
+    __attribute__((const));
+size_t GPBComputeStringSize(int32_t fieldNumber, NSString *value)
+    __attribute__((const));
+size_t GPBComputeGroupSize(int32_t fieldNumber, GPBMessage *value)
+    __attribute__((const));
+size_t GPBComputeUnknownGroupSize(int32_t fieldNumber,
+                                  GPBUnknownFieldSet *value)
+    __attribute__((const));
+size_t GPBComputeMessageSize(int32_t fieldNumber, GPBMessage *value)
+    __attribute__((const));
+size_t GPBComputeDataSize(int32_t fieldNumber, NSData *value)
+    __attribute__((const));
+size_t GPBComputeUInt32Size(int32_t fieldNumber, uint32_t value)
+    __attribute__((const));
+size_t GPBComputeSFixed32Size(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+size_t GPBComputeSFixed64Size(int32_t fieldNumber, int64_t value)
+    __attribute__((const));
+size_t GPBComputeSInt32Size(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+size_t GPBComputeSInt64Size(int32_t fieldNumber, int64_t value)
+    __attribute__((const));
+size_t GPBComputeTagSize(int32_t fieldNumber) __attribute__((const));
+size_t GPBComputeWireFormatTagSize(int field_number, GPBType type)
+    __attribute__((const));
+
+size_t GPBComputeDoubleSizeNoTag(double value) __attribute__((const));
+size_t GPBComputeFloatSizeNoTag(float value) __attribute__((const));
+size_t GPBComputeUInt64SizeNoTag(uint64_t value) __attribute__((const));
+size_t GPBComputeInt64SizeNoTag(int64_t value) __attribute__((const));
+size_t GPBComputeInt32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeFixed64SizeNoTag(uint64_t value) __attribute__((const));
+size_t GPBComputeFixed32SizeNoTag(uint32_t value) __attribute__((const));
+size_t GPBComputeBoolSizeNoTag(BOOL value) __attribute__((const));
+size_t GPBComputeStringSizeNoTag(NSString *value) __attribute__((const));
+size_t GPBComputeGroupSizeNoTag(GPBMessage *value) __attribute__((const));
+size_t GPBComputeUnknownGroupSizeNoTag(GPBUnknownFieldSet *value)
+    __attribute__((const));
+size_t GPBComputeMessageSizeNoTag(GPBMessage *value) __attribute__((const));
+size_t GPBComputeDataSizeNoTag(NSData *value) __attribute__((const));
+size_t GPBComputeUInt32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeEnumSizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeSFixed32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeSFixed64SizeNoTag(int64_t value) __attribute__((const));
+size_t GPBComputeSInt32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeSInt64SizeNoTag(int64_t value) __attribute__((const));
+
+// Note that this will calculate the size of 64 bit values truncated to 32.
+size_t GPBComputeSizeTSizeAsInt32NoTag(size_t value) __attribute__((const));
+
+size_t GPBComputeRawVarint32Size(int32_t value) __attribute__((const));
+size_t GPBComputeRawVarint64Size(int64_t value) __attribute__((const));
+
+// Note that this will calculate the size of 64 bit values truncated to 32.
+size_t GPBComputeRawVarint32SizeForInteger(NSInteger value)
+    __attribute__((const));
+
+// Compute the number of bytes that would be needed to encode a
+// MessageSet extension to the stream.  For historical reasons,
+// the wire format differs from normal fields.
+size_t GPBComputeMessageSetExtensionSize(int32_t fieldNumber, GPBMessage *value)
+    __attribute__((const));
+
+// Compute the number of bytes that would be needed to encode an
+// unparsed MessageSet extension field to the stream.  For
+// historical reasons, the wire format differs from normal fields.
+size_t GPBComputeRawMessageSetExtensionSize(int32_t fieldNumber, NSData *value)
+    __attribute__((const));
+
+size_t GPBComputeEnumSize(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+
+CF_EXTERN_C_END
+
+// Write methods for types that can be in packed arrays.
+//%PDDM-DEFINE _WRITE_PACKABLE_DECLS(NAME, ARRAY_TYPE, TYPE)
+//%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE)value;
+//%- (void)write##NAME##s:(int32_t)fieldNumber
+//%       NAME$S values:(GPB##ARRAY_TYPE##Array *)values
+//%       NAME$S    tag:(uint32_t)tag;
+//%- (void)write##NAME##NoTag:(TYPE)value;
+//%
+// Write methods for types that aren't in packed arrays.
+//%PDDM-DEFINE _WRITE_UNPACKABLE_DECLS(NAME, TYPE)
+//%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE)value;
+//%- (void)write##NAME##s:(int32_t)fieldNumber values:(NSArray *)values;
+//%- (void)write##NAME##NoTag:(TYPE)value;
+//%
+// Special write methods for Groups.
+//%PDDM-DEFINE _WRITE_GROUP_DECLS(NAME, TYPE)
+//%- (void)write##NAME:(int32_t)fieldNumber
+//%       NAME$S value:(TYPE)value;
+//%- (void)write##NAME##s:(int32_t)fieldNumber values:(NSArray *)values;
+//%- (void)write##NAME##NoTag:(int32_t)fieldNumber
+//%            NAME$S value:(TYPE)value;
+//%
+
+// One macro to hide it all up above.
+//%PDDM-DEFINE _WRITE_DECLS()
+//%_WRITE_PACKABLE_DECLS(Double, Double, double)
+//%_WRITE_PACKABLE_DECLS(Float, Float, float)
+//%_WRITE_PACKABLE_DECLS(UInt64, UInt64, uint64_t)
+//%_WRITE_PACKABLE_DECLS(Int64, Int64, int64_t)
+//%_WRITE_PACKABLE_DECLS(Int32, Int32, int32_t)
+//%_WRITE_PACKABLE_DECLS(UInt32, UInt32, uint32_t)
+//%_WRITE_PACKABLE_DECLS(Fixed64, UInt64, uint64_t)
+//%_WRITE_PACKABLE_DECLS(Fixed32, UInt32, uint32_t)
+//%_WRITE_PACKABLE_DECLS(SInt32, Int32, int32_t)
+//%_WRITE_PACKABLE_DECLS(SInt64, Int64, int64_t)
+//%_WRITE_PACKABLE_DECLS(SFixed64, Int64, int64_t)
+//%_WRITE_PACKABLE_DECLS(SFixed32, Int32, int32_t)
+//%_WRITE_PACKABLE_DECLS(Bool, Bool, BOOL)
+//%_WRITE_PACKABLE_DECLS(Enum, Enum, int32_t)
+//%_WRITE_UNPACKABLE_DECLS(String, NSString *)
+//%_WRITE_UNPACKABLE_DECLS(Message, GPBMessage *)
+//%_WRITE_UNPACKABLE_DECLS(Data, NSData *)
+//%_WRITE_GROUP_DECLS(Group, GPBMessage *)
+//%_WRITE_GROUP_DECLS(UnknownGroup, GPBUnknownFieldSet *)

+ 1229 - 0
objectivec/GPBCodedOutputStream.m

@@ -0,0 +1,1229 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBCodedOutputStream.h"
+
+#import <mach/vm_param.h>
+
+#import "GPBArray.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+// Structure for containing state of a GPBCodedInputStream. Brought out into
+// a struct so that we can inline several common functions instead of dealing
+// with overhead of ObjC dispatch.
+typedef struct GPBOutputBufferState {
+  uint8_t *bytes;
+  size_t size;
+  size_t position;
+  NSOutputStream *output;
+} GPBOutputBufferState;
+
+@implementation GPBCodedOutputStream {
+  GPBOutputBufferState state_;
+  NSMutableData *buffer_;
+}
+
+static const int32_t LITTLE_ENDIAN_32_SIZE = sizeof(uint32_t);
+static const int32_t LITTLE_ENDIAN_64_SIZE = sizeof(uint64_t);
+
+// Internal helper that writes the current buffer to the output. The
+// buffer position is reset to its initial value when this returns.
+static void GPBRefreshBuffer(GPBOutputBufferState *state) {
+  if (state->output == nil) {
+    // We're writing to a single buffer.
+    [NSException raise:@"OutOfSpace" format:@""];
+  }
+  if (state->position != 0) {
+    NSInteger written =
+        [state->output write:state->bytes maxLength:state->position];
+    if (written != (NSInteger)state->position) {
+      [NSException raise:@"WriteFailed" format:@""];
+    }
+    state->position = 0;
+  }
+}
+
+static void GPBWriteRawByte(GPBOutputBufferState *state, uint8_t value) {
+  if (state->position == state->size) {
+    GPBRefreshBuffer(state);
+  }
+  state->bytes[state->position++] = value;
+}
+
+static void GPBWriteRawVarint32(GPBOutputBufferState *state, int32_t value) {
+  while (YES) {
+    if ((value & ~0x7F) == 0) {
+      uint8_t val = (uint8_t)value;
+      GPBWriteRawByte(state, val);
+      return;
+    } else {
+      GPBWriteRawByte(state, (value & 0x7F) | 0x80);
+      value = GPBLogicalRightShift32(value, 7);
+    }
+  }
+}
+
+static void GPBWriteRawVarint64(GPBOutputBufferState *state, int64_t value) {
+  while (YES) {
+    if ((value & ~0x7FL) == 0) {
+      uint8_t val = (uint8_t)value;
+      GPBWriteRawByte(state, val);
+      return;
+    } else {
+      GPBWriteRawByte(state, ((int32_t)value & 0x7F) | 0x80);
+      value = GPBLogicalRightShift64(value, 7);
+    }
+  }
+}
+
+static void GPBWriteInt32NoTag(GPBOutputBufferState *state, int32_t value) {
+  if (value >= 0) {
+    GPBWriteRawVarint32(state, value);
+  } else {
+    // Must sign-extend
+    GPBWriteRawVarint64(state, value);
+  }
+}
+
+static void GPBWriteUInt32(GPBOutputBufferState *state, int32_t fieldNumber,
+                           uint32_t value) {
+  GPBWriteTagWithFormat(state, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint32(state, value);
+}
+
+static void GPBWriteTagWithFormat(GPBOutputBufferState *state,
+                                  uint32_t fieldNumber, GPBWireFormat format) {
+  GPBWriteRawVarint32(state, GPBWireFormatMakeTag(fieldNumber, format));
+}
+
+static void GPBWriteRawLittleEndian32(GPBOutputBufferState *state,
+                                      int32_t value) {
+  GPBWriteRawByte(state, (value)&0xFF);
+  GPBWriteRawByte(state, (value >> 8) & 0xFF);
+  GPBWriteRawByte(state, (value >> 16) & 0xFF);
+  GPBWriteRawByte(state, (value >> 24) & 0xFF);
+}
+
+static void GPBWriteRawLittleEndian64(GPBOutputBufferState *state,
+                                      int64_t value) {
+  GPBWriteRawByte(state, (int32_t)(value)&0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 8) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 16) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 24) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 32) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 40) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 48) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 56) & 0xFF);
+}
+
+#if DEBUG && !defined(NS_BLOCK_ASSERTIONS)
++ (void)load {
+  // This test exists to verify that CFStrings with embedded NULLs will work
+  // for us. If this Assert fails, all code below that depends on
+  // CFStringGetCStringPtr will NOT work properly on strings that contain
+  // embedded NULLs, and we do get that in some protobufs.
+  // Note that this will not be compiled in release.
+  // We didn't feel that just keeping it in a unit test was sufficient because
+  // the Protobuf unit tests are only run when somebody is actually working
+  // on protobufs.
+  CFStringRef zeroTest = CFSTR("Test\0String");
+  const char *cString = CFStringGetCStringPtr(zeroTest, kCFStringEncodingUTF8);
+  NSAssert(cString == NULL, @"Serious Error");
+}
+#endif  // DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+
+- (void)dealloc {
+  [self flush];
+  [state_.output close];
+  [state_.output release];
+  [buffer_ release];
+
+  [super dealloc];
+}
+
+- (instancetype)initWithOutputStream:(NSOutputStream *)output {
+  NSMutableData *data = [NSMutableData dataWithLength:PAGE_SIZE];
+  return [self initWithOutputStream:output data:data];
+}
+
+- (instancetype)initWithData:(NSMutableData *)data {
+  return [self initWithOutputStream:nil data:data];
+}
+
+- (instancetype)initWithOutputStream:(NSOutputStream *)output
+                          bufferSize:(size_t)bufferSize {
+  NSMutableData *data = [NSMutableData dataWithLength:bufferSize];
+  return [self initWithOutputStream:output data:data];
+}
+
+- (instancetype)initWithOutputStream:(NSOutputStream *)output
+                                data:(NSMutableData *)data {
+  if ((self = [super init])) {
+    buffer_ = [data retain];
+    [output open];
+    state_.bytes = [data mutableBytes];
+    state_.size = [data length];
+    state_.output = [output retain];
+  }
+  return self;
+}
+
++ (instancetype)streamWithOutputStream:(NSOutputStream *)output
+                            bufferSize:(size_t)bufferSize {
+  return [[[self alloc] initWithOutputStream:output
+                                  bufferSize:bufferSize] autorelease];
+}
+
++ (instancetype)streamWithOutputStream:(NSOutputStream *)output {
+  return [[[self alloc] initWithOutputStream:output
+                                  bufferSize:PAGE_SIZE] autorelease];
+}
+
++ (instancetype)streamWithData:(NSMutableData *)data {
+  return [[[self alloc] initWithData:data] autorelease];
+}
+
+- (void)writeDoubleNoTag:(double)value {
+  GPBWriteRawLittleEndian64(&state_, GPBConvertDoubleToInt64(value));
+}
+
+- (void)writeDouble:(int32_t)fieldNumber value:(double)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed64);
+  GPBWriteRawLittleEndian64(&state_, GPBConvertDoubleToInt64(value));
+}
+
+- (void)writeFloatNoTag:(float)value {
+  GPBWriteRawLittleEndian32(&state_, GPBConvertFloatToInt32(value));
+}
+
+- (void)writeFloat:(int32_t)fieldNumber value:(float)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed32);
+  GPBWriteRawLittleEndian32(&state_, GPBConvertFloatToInt32(value));
+}
+
+- (void)writeUInt64NoTag:(uint64_t)value {
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeUInt64:(int32_t)fieldNumber value:(uint64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeInt64NoTag:(int64_t)value {
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeInt64:(int32_t)fieldNumber value:(int64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeInt32NoTag:(int32_t)value {
+  GPBWriteInt32NoTag(&state_, value);
+}
+
+- (void)writeInt32:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteInt32NoTag(&state_, value);
+}
+
+- (void)writeFixed64NoTag:(uint64_t)value {
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeFixed64:(int32_t)fieldNumber value:(uint64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed64);
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeFixed32NoTag:(uint32_t)value {
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeFixed32:(int32_t)fieldNumber value:(uint32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed32);
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeBoolNoTag:(BOOL)value {
+  GPBWriteRawByte(&state_, (value ? 1 : 0));
+}
+
+- (void)writeBool:(int32_t)fieldNumber value:(BOOL)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawByte(&state_, (value ? 1 : 0));
+}
+
+- (void)writeStringNoTag:(const NSString *)value {
+  // If you are concerned about embedded NULLs see the test in
+  // +load above.
+  const char *quickString =
+      CFStringGetCStringPtr((CFStringRef)value, kCFStringEncodingUTF8);
+  size_t length = (quickString != NULL)
+                      ? strlen(quickString)
+                      : [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+  GPBWriteRawVarint32(&state_, (int32_t)length);
+
+  if (length == 0) {
+    return;
+  }
+
+  // Fast path: Most strings are short, if the buffer already has space,
+  // add to it directly.
+  NSUInteger bufferBytesLeft = state_.size - state_.position;
+  if (bufferBytesLeft >= length) {
+    NSUInteger usedBufferLength = 0;
+    BOOL result;
+    if (quickString != NULL) {
+      memcpy(state_.bytes + state_.position, quickString, length);
+      usedBufferLength = length;
+      result = YES;
+    } else {
+      result = [value getBytes:state_.bytes + state_.position
+                     maxLength:bufferBytesLeft
+                    usedLength:&usedBufferLength
+                      encoding:NSUTF8StringEncoding
+                       options:0
+                         range:NSMakeRange(0, [value length])
+                remainingRange:NULL];
+    }
+    if (result) {
+      NSAssert2((usedBufferLength == length),
+                @"Our UTF8 calc was wrong? %tu vs %zd", usedBufferLength,
+                length);
+      state_.position += usedBufferLength;
+      return;
+    }
+  } else if (quickString != NULL) {
+    [self writeRawPtr:quickString offset:0 length:length];
+  } else {
+    // Slow path: just get it as data and write it out.
+    NSData *utf8Data = [value dataUsingEncoding:NSUTF8StringEncoding];
+    NSAssert2(([utf8Data length] == length),
+              @"Strings UTF8 length was wrong? %tu vs %zd", [utf8Data length],
+              length);
+    [self writeRawData:utf8Data];
+  }
+}
+
+- (void)writeString:(int32_t)fieldNumber value:(NSString *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatLengthDelimited);
+  [self writeStringNoTag:value];
+}
+
+- (void)writeGroupNoTag:(int32_t)fieldNumber value:(GPBMessage *)value {
+  [value writeToCodedOutputStream:self];
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatEndGroup);
+}
+
+- (void)writeGroup:(int32_t)fieldNumber value:(GPBMessage *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatStartGroup);
+  [self writeGroupNoTag:fieldNumber value:value];
+}
+
+- (void)writeUnknownGroupNoTag:(int32_t)fieldNumber
+                         value:(const GPBUnknownFieldSet *)value {
+  [value writeToCodedOutputStream:self];
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatEndGroup);
+}
+
+- (void)writeUnknownGroup:(int32_t)fieldNumber
+                    value:(GPBUnknownFieldSet *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatStartGroup);
+  [self writeUnknownGroupNoTag:fieldNumber value:value];
+}
+
+- (void)writeMessageNoTag:(GPBMessage *)value {
+  GPBWriteRawVarint32(&state_, (int32_t)[value serializedSize]);
+  [value writeToCodedOutputStream:self];
+}
+
+- (void)writeMessage:(int32_t)fieldNumber value:(GPBMessage *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatLengthDelimited);
+  [self writeMessageNoTag:value];
+}
+
+- (void)writeDataNoTag:(NSData *)value {
+  GPBWriteRawVarint32(&state_, (int32_t)[value length]);
+  [self writeRawData:value];
+}
+
+- (void)writeData:(int32_t)fieldNumber value:(NSData *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatLengthDelimited);
+  [self writeDataNoTag:value];
+}
+
+- (void)writeUInt32NoTag:(uint32_t)value {
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeUInt32:(int32_t)fieldNumber value:(uint32_t)value {
+  GPBWriteUInt32(&state_, fieldNumber, value);
+}
+
+- (void)writeEnumNoTag:(int32_t)value {
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeEnum:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeSFixed32NoTag:(int32_t)value {
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeSFixed32:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed32);
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeSFixed64NoTag:(int64_t)value {
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeSFixed64:(int32_t)fieldNumber value:(int64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed64);
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeSInt32NoTag:(int32_t)value {
+  GPBWriteRawVarint32(&state_, GPBEncodeZigZag32(value));
+}
+
+- (void)writeSInt32:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint32(&state_, GPBEncodeZigZag32(value));
+}
+
+- (void)writeSInt64NoTag:(int64_t)value {
+  GPBWriteRawVarint64(&state_, GPBEncodeZigZag64(value));
+}
+
+- (void)writeSInt64:(int32_t)fieldNumber value:(int64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint64(&state_, GPBEncodeZigZag64(value));
+}
+
+//%PDDM-DEFINE WRITE_PACKABLE_DEFNS(NAME, ARRAY_TYPE, TYPE, ACCESSOR_NAME)
+//%- (void)write##NAME##s:(int32_t)fieldNumber
+//%       NAME$S values:(GPB##ARRAY_TYPE##Array *)values
+//%       NAME$S    tag:(uint32_t)tag {
+//%  if (tag != 0) {
+//%    if (values.count == 0) return;
+//%    __block size_t dataSize = 0;
+//%    [values enumerate##ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%#pragma unused(idx, stop)
+//%      dataSize += GPBCompute##NAME##SizeNoTag(value);
+//%    }];
+//%    GPBWriteRawVarint32(&state_, tag);
+//%    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+//%    [values enumerate##ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%#pragma unused(idx, stop)
+//%      [self write##NAME##NoTag:value];
+//%    }];
+//%  } else {
+//%    [values enumerate##ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%#pragma unused(idx, stop)
+//%      [self write##NAME:fieldNumber value:value];
+//%    }];
+//%  }
+//%}
+//%
+//%PDDM-DEFINE WRITE_UNPACKABLE_DEFNS(NAME, TYPE)
+//%- (void)write##NAME##s:(int32_t)fieldNumber values:(NSArray *)values {
+//%  for (TYPE *value in values) {
+//%    [self write##NAME:fieldNumber value:value];
+//%  }
+//%}
+//%
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Double, Double, double, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeDoubles:(int32_t)fieldNumber
+              values:(GPBDoubleArray *)values
+                 tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeDoubleSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeDoubleNoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeDouble:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Float, Float, float, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeFloats:(int32_t)fieldNumber
+             values:(GPBFloatArray *)values
+                tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeFloatSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFloatNoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFloat:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(UInt64, UInt64, uint64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeUInt64s:(int32_t)fieldNumber
+              values:(GPBUInt64Array *)values
+                 tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeUInt64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Int64, Int64, int64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeInt64s:(int32_t)fieldNumber
+             values:(GPBInt64Array *)values
+                tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeInt64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Int32, Int32, int32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeInt32s:(int32_t)fieldNumber
+             values:(GPBInt32Array *)values
+                tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeInt32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(UInt32, UInt32, uint32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeUInt32s:(int32_t)fieldNumber
+              values:(GPBUInt32Array *)values
+                 tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeUInt32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Fixed64, UInt64, uint64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeFixed64s:(int32_t)fieldNumber
+               values:(GPBUInt64Array *)values
+                  tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeFixed64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Fixed32, UInt32, uint32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeFixed32s:(int32_t)fieldNumber
+               values:(GPBUInt32Array *)values
+                  tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeFixed32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SInt32, Int32, int32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSInt32s:(int32_t)fieldNumber
+              values:(GPBInt32Array *)values
+                 tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSInt32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SInt64, Int64, int64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSInt64s:(int32_t)fieldNumber
+              values:(GPBInt64Array *)values
+                 tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSInt64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SFixed64, Int64, int64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSFixed64s:(int32_t)fieldNumber
+                values:(GPBInt64Array *)values
+                   tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSFixed64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SFixed32, Int32, int32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSFixed32s:(int32_t)fieldNumber
+                values:(GPBInt32Array *)values
+                   tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSFixed32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Bool, Bool, BOOL, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeBools:(int32_t)fieldNumber
+            values:(GPBBoolArray *)values
+               tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeBoolSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeBoolNoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeBool:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Enum, Enum, int32_t, Raw)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeEnums:(int32_t)fieldNumber
+            values:(GPBEnumArray *)values
+               tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeEnumSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeEnumNoTag:value];
+    }];
+  } else {
+    [values enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeEnum:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(String, NSString)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeStrings:(int32_t)fieldNumber values:(NSArray *)values {
+  for (NSString *value in values) {
+    [self writeString:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(Message, GPBMessage)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeMessages:(int32_t)fieldNumber values:(NSArray *)values {
+  for (GPBMessage *value in values) {
+    [self writeMessage:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(Data, NSData)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeDatas:(int32_t)fieldNumber values:(NSArray *)values {
+  for (NSData *value in values) {
+    [self writeData:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(Group, GPBMessage)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeGroups:(int32_t)fieldNumber values:(NSArray *)values {
+  for (GPBMessage *value in values) {
+    [self writeGroup:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(UnknownGroup, GPBUnknownFieldSet)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeUnknownGroups:(int32_t)fieldNumber values:(NSArray *)values {
+  for (GPBUnknownFieldSet *value in values) {
+    [self writeUnknownGroup:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND-END (19 expansions)
+
+- (void)writeMessageSetExtension:(int32_t)fieldNumber
+                           value:(GPBMessage *)value {
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatStartGroup);
+  GPBWriteUInt32(&state_, GPBWireFormatMessageSetTypeId, fieldNumber);
+  [self writeMessage:GPBWireFormatMessageSetMessage value:value];
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatEndGroup);
+}
+
+- (void)writeRawMessageSetExtension:(int32_t)fieldNumber value:(NSData *)value {
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatStartGroup);
+  GPBWriteUInt32(&state_, GPBWireFormatMessageSetTypeId, fieldNumber);
+  [self writeData:GPBWireFormatMessageSetMessage value:value];
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatEndGroup);
+}
+
+- (void)flush {
+  if (state_.output != nil) {
+    GPBRefreshBuffer(&state_);
+  }
+}
+
+- (void)writeRawByte:(uint8_t)value {
+  GPBWriteRawByte(&state_, value);
+}
+
+- (void)writeRawData:(const NSData *)data {
+  [self writeRawPtr:[data bytes] offset:0 length:[data length]];
+}
+
+- (void)writeRawPtr:(const void *)value
+             offset:(size_t)offset
+             length:(size_t)length {
+  if (value == nil || length == 0) {
+    return;
+  }
+
+  NSUInteger bufferLength = state_.size;
+  NSUInteger bufferBytesLeft = bufferLength - state_.position;
+  if (bufferBytesLeft >= length) {
+    // We have room in the current buffer.
+    memcpy(state_.bytes + state_.position, ((uint8_t *)value) + offset, length);
+    state_.position += length;
+  } else {
+    // Write extends past current buffer.  Fill the rest of this buffer and
+    // flush.
+    size_t bytesWritten = bufferBytesLeft;
+    memcpy(state_.bytes + state_.position, ((uint8_t *)value) + offset,
+           bytesWritten);
+    offset += bytesWritten;
+    length -= bytesWritten;
+    state_.position = bufferLength;
+    GPBRefreshBuffer(&state_);
+    bufferLength = state_.size;
+
+    // Now deal with the rest.
+    // Since we have an output stream, this is our buffer
+    // and buffer offset == 0
+    if (length <= bufferLength) {
+      // Fits in new buffer.
+      memcpy(state_.bytes, ((uint8_t *)value) + offset, length);
+      state_.position = length;
+    } else {
+      // Write is very big.  Let's do it all at once.
+      [state_.output write:((uint8_t *)value) + offset maxLength:length];
+    }
+  }
+}
+
+- (void)writeTag:(uint32_t)fieldNumber format:(GPBWireFormat)format {
+  GPBWriteTagWithFormat(&state_, fieldNumber, format);
+}
+
+- (void)writeRawVarint32:(int32_t)value {
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeRawVarintSizeTAs32:(size_t)value {
+  // Note the truncation.
+  GPBWriteRawVarint32(&state_, (int32_t)value);
+}
+
+- (void)writeRawVarint64:(int64_t)value {
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeRawLittleEndian32:(int32_t)value {
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeRawLittleEndian64:(int64_t)value {
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+@end
+
+size_t GPBComputeDoubleSizeNoTag(Float64 value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_64_SIZE;
+}
+
+size_t GPBComputeFloatSizeNoTag(Float32 value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_32_SIZE;
+}
+
+size_t GPBComputeUInt64SizeNoTag(uint64_t value) {
+  return GPBComputeRawVarint64Size(value);
+}
+
+size_t GPBComputeInt64SizeNoTag(int64_t value) {
+  return GPBComputeRawVarint64Size(value);
+}
+
+size_t GPBComputeInt32SizeNoTag(int32_t value) {
+  if (value >= 0) {
+    return GPBComputeRawVarint32Size(value);
+  } else {
+    // Must sign-extend.
+    return 10;
+  }
+}
+
+size_t GPBComputeSizeTSizeAsInt32NoTag(size_t value) {
+  return GPBComputeInt32SizeNoTag((int32_t)value);
+}
+
+size_t GPBComputeFixed64SizeNoTag(uint64_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_64_SIZE;
+}
+
+size_t GPBComputeFixed32SizeNoTag(uint32_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_32_SIZE;
+}
+
+size_t GPBComputeBoolSizeNoTag(BOOL value) {
+#pragma unused(value)
+  return 1;
+}
+
+size_t GPBComputeStringSizeNoTag(NSString *value) {
+  // If you are concerned about embedded NULLs see the test in
+  // +load above.
+  const char *quickString =
+      CFStringGetCStringPtr((CFStringRef)value, kCFStringEncodingUTF8);
+  NSUInteger length =
+      (quickString != NULL)
+          ? strlen(quickString)
+          : [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+  return GPBComputeRawVarint32SizeForInteger(length) + length;
+}
+
+size_t GPBComputeGroupSizeNoTag(GPBMessage *value) {
+  return [value serializedSize];
+}
+
+size_t GPBComputeUnknownGroupSizeNoTag(GPBUnknownFieldSet *value) {
+  return value.serializedSize;
+}
+
+size_t GPBComputeMessageSizeNoTag(GPBMessage *value) {
+  size_t size = [value serializedSize];
+  return GPBComputeRawVarint32SizeForInteger(size) + size;
+}
+
+size_t GPBComputeDataSizeNoTag(NSData *value) {
+  NSUInteger valueLength = [value length];
+  return GPBComputeRawVarint32SizeForInteger(valueLength) + valueLength;
+}
+
+size_t GPBComputeUInt32SizeNoTag(int32_t value) {
+  return GPBComputeRawVarint32Size(value);
+}
+
+size_t GPBComputeEnumSizeNoTag(int32_t value) {
+  return GPBComputeRawVarint32Size(value);
+}
+
+size_t GPBComputeSFixed32SizeNoTag(int32_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_32_SIZE;
+}
+
+size_t GPBComputeSFixed64SizeNoTag(int64_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_64_SIZE;
+}
+
+size_t GPBComputeSInt32SizeNoTag(int32_t value) {
+  return GPBComputeRawVarint32Size(GPBEncodeZigZag32(value));
+}
+
+size_t GPBComputeSInt64SizeNoTag(int64_t value) {
+  return GPBComputeRawVarint64Size(GPBEncodeZigZag64(value));
+}
+
+size_t GPBComputeDoubleSize(int32_t fieldNumber, double value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeDoubleSizeNoTag(value);
+}
+
+size_t GPBComputeFloatSize(int32_t fieldNumber, float value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeFloatSizeNoTag(value);
+}
+
+size_t GPBComputeUInt64Size(int32_t fieldNumber, uint64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeUInt64SizeNoTag(value);
+}
+
+size_t GPBComputeInt64Size(int32_t fieldNumber, int64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeInt64SizeNoTag(value);
+}
+
+size_t GPBComputeInt32Size(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeInt32SizeNoTag(value);
+}
+
+size_t GPBComputeFixed64Size(int32_t fieldNumber, uint64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeFixed64SizeNoTag(value);
+}
+
+size_t GPBComputeFixed32Size(int32_t fieldNumber, uint32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeFixed32SizeNoTag(value);
+}
+
+size_t GPBComputeBoolSize(int32_t fieldNumber, BOOL value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeBoolSizeNoTag(value);
+}
+
+size_t GPBComputeStringSize(int32_t fieldNumber, NSString *value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeStringSizeNoTag(value);
+}
+
+size_t GPBComputeGroupSize(int32_t fieldNumber, GPBMessage *value) {
+  return GPBComputeTagSize(fieldNumber) * 2 + GPBComputeGroupSizeNoTag(value);
+}
+
+size_t GPBComputeUnknownGroupSize(int32_t fieldNumber,
+                                  GPBUnknownFieldSet *value) {
+  return GPBComputeTagSize(fieldNumber) * 2 +
+         GPBComputeUnknownGroupSizeNoTag(value);
+}
+
+size_t GPBComputeMessageSize(int32_t fieldNumber, GPBMessage *value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeMessageSizeNoTag(value);
+}
+
+size_t GPBComputeDataSize(int32_t fieldNumber, NSData *value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeDataSizeNoTag(value);
+}
+
+size_t GPBComputeUInt32Size(int32_t fieldNumber, uint32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeUInt32SizeNoTag(value);
+}
+
+size_t GPBComputeEnumSize(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeEnumSizeNoTag(value);
+}
+
+size_t GPBComputeSFixed32Size(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeSFixed32SizeNoTag(value);
+}
+
+size_t GPBComputeSFixed64Size(int32_t fieldNumber, int64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeSFixed64SizeNoTag(value);
+}
+
+size_t GPBComputeSInt32Size(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeSInt32SizeNoTag(value);
+}
+
+size_t GPBComputeSInt64Size(int32_t fieldNumber, int64_t value) {
+  return GPBComputeTagSize(fieldNumber) +
+         GPBComputeRawVarint64Size(GPBEncodeZigZag64(value));
+}
+
+size_t GPBComputeMessageSetExtensionSize(int32_t fieldNumber,
+                                         GPBMessage *value) {
+  return GPBComputeTagSize(GPBWireFormatMessageSetItem) * 2 +
+         GPBComputeUInt32Size(GPBWireFormatMessageSetTypeId, fieldNumber) +
+         GPBComputeMessageSize(GPBWireFormatMessageSetMessage, value);
+}
+
+size_t GPBComputeRawMessageSetExtensionSize(int32_t fieldNumber,
+                                            NSData *value) {
+  return GPBComputeTagSize(GPBWireFormatMessageSetItem) * 2 +
+         GPBComputeUInt32Size(GPBWireFormatMessageSetTypeId, fieldNumber) +
+         GPBComputeDataSize(GPBWireFormatMessageSetMessage, value);
+}
+
+size_t GPBComputeTagSize(int32_t fieldNumber) {
+  return GPBComputeRawVarint32Size(
+      GPBWireFormatMakeTag(fieldNumber, GPBWireFormatVarint));
+}
+
+size_t GPBComputeWireFormatTagSize(int field_number, GPBType type) {
+  size_t result = GPBComputeTagSize(field_number);
+  if (type == GPBTypeGroup) {
+    // Groups have both a start and an end tag.
+    return result * 2;
+  } else {
+    return result;
+  }
+}
+
+size_t GPBComputeRawVarint32Size(int32_t value) {
+  // value is treated as unsigned, so it won't be sign-extended if negative.
+  if ((value & (0xffffffff << 7)) == 0) return 1;
+  if ((value & (0xffffffff << 14)) == 0) return 2;
+  if ((value & (0xffffffff << 21)) == 0) return 3;
+  if ((value & (0xffffffff << 28)) == 0) return 4;
+  return 5;
+}
+
+size_t GPBComputeRawVarint32SizeForInteger(NSInteger value) {
+  // Note the truncation.
+  return GPBComputeRawVarint32Size((int32_t)value);
+}
+
+size_t GPBComputeRawVarint64Size(int64_t value) {
+  if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
+  if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
+  if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
+  if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
+  if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
+  if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
+  if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
+  if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
+  if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
+  return 10;
+}

+ 143 - 0
objectivec/GPBDescriptor.h

@@ -0,0 +1,143 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBTypes.h"
+
+@class GPBEnumDescriptor;
+@class GPBFieldDescriptor;
+@class GPBFieldOptions;
+@class GPBFileDescriptor;
+@class GPBOneofDescriptor;
+
+typedef NS_ENUM(NSInteger, GPBFileSyntax) {
+  GPBFileSyntaxUnknown = 0,
+  GPBFileSyntaxProto2 = 2,
+  GPBFileSyntaxProto3 = 3,
+};
+
+typedef NS_ENUM(NSInteger, GPBFieldType) {
+  GPBFieldTypeSingle,    // optional/required
+  GPBFieldTypeRepeated,  // repeated
+  GPBFieldTypeMap,       // map<K,V>
+};
+
+@interface GPBDescriptor : NSObject<NSCopying>
+
+@property(nonatomic, readonly, copy) NSString *name;
+@property(nonatomic, readonly, strong) NSArray *fields;
+@property(nonatomic, readonly, strong) NSArray *oneofs;
+@property(nonatomic, readonly, strong) NSArray *enums;
+@property(nonatomic, readonly, strong) NSArray *extensions;
+@property(nonatomic, readonly) const GPBExtensionRange *extensionRanges;
+@property(nonatomic, readonly) NSUInteger extensionRangesCount;
+@property(nonatomic, readonly, assign) GPBFileDescriptor *file;
+
+@property(nonatomic, readonly, getter=isWireFormat) BOOL wireFormat;
+@property(nonatomic, readonly) Class messageClass;
+
+- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber;
+- (GPBFieldDescriptor *)fieldWithName:(NSString *)name;
+- (GPBOneofDescriptor *)oneofWithName:(NSString *)name;
+- (GPBEnumDescriptor *)enumWithName:(NSString *)name;
+- (GPBFieldDescriptor *)extensionWithNumber:(uint32_t)fieldNumber;
+- (GPBFieldDescriptor *)extensionWithName:(NSString *)name;
+
+@end
+
+@interface GPBFileDescriptor : NSObject
+
+@property(nonatomic, readonly, copy) NSString *package;
+@property(nonatomic, readonly) GPBFileSyntax syntax;
+
+@end
+
+@interface GPBOneofDescriptor : NSObject
+@property(nonatomic, readonly) NSString *name;
+@property(nonatomic, readonly) NSArray *fields;
+
+- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber;
+- (GPBFieldDescriptor *)fieldWithName:(NSString *)name;
+@end
+
+@interface GPBFieldDescriptor : NSObject
+
+@property(nonatomic, readonly, copy) NSString *name;
+@property(nonatomic, readonly) uint32_t number;
+@property(nonatomic, readonly) GPBType type;
+@property(nonatomic, readonly) BOOL hasDefaultValue;
+@property(nonatomic, readonly) GPBValue defaultValue;
+@property(nonatomic, readonly, getter=isRequired) BOOL required;
+@property(nonatomic, readonly, getter=isOptional) BOOL optional;
+@property(nonatomic, readonly) GPBFieldType fieldType;
+// If it is a map, the value type is in -type.
+@property(nonatomic, readonly) GPBType mapKeyType;
+@property(nonatomic, readonly, getter=isPackable) BOOL packable;
+
+@property(nonatomic, readonly, assign) GPBOneofDescriptor *containingOneof;
+
+@property(nonatomic, readonly) GPBFieldOptions *fieldOptions;
+
+// Message properties
+@property(nonatomic, readonly, assign) Class msgClass;
+
+// Enum properties
+@property(nonatomic, readonly, strong) GPBEnumDescriptor *enumDescriptor;
+
+- (BOOL)isValidEnumValue:(int32_t)value;
+
+// For now, this will return nil if it doesn't know the name to use for
+// TextFormat.
+- (NSString *)textFormatName;
+
+@end
+
+@interface GPBEnumDescriptor : NSObject
+
+@property(nonatomic, readonly, copy) NSString *name;
+@property(nonatomic, readonly) GPBEnumValidationFunc enumVerifier;
+
+- (NSString *)enumNameForValue:(int32_t)number;
+- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name;
+
+- (NSString *)textFormatNameForValue:(int32_t)number;
+
+@end
+
+@interface GPBExtensionDescriptor : NSObject
+@property(nonatomic, readonly) uint32_t fieldNumber;
+@property(nonatomic, readonly) GPBType type;
+@property(nonatomic, readonly, getter=isRepeated) BOOL repeated;
+@property(nonatomic, readonly, getter=isPackable) BOOL packable;
+@property(nonatomic, readonly, assign) Class msgClass;
+@property(nonatomic, readonly) NSString *singletonName;
+@property(nonatomic, readonly, strong) GPBEnumDescriptor *enumDescriptor;
+@end

+ 888 - 0
objectivec/GPBDescriptor.m

@@ -0,0 +1,888 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBDescriptor_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import "GPBUtilities_PackagePrivate.h"
+#import "GPBWireFormat.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "google/protobuf/Descriptor.pbobjc.h"
+
+// The address of this variable is used as a key for obj_getAssociatedObject.
+static const char kTextFormatExtraValueKey = 0;
+
+// Utility function to generate selectors on the fly.
+static SEL SelFromStrings(const char *prefix, const char *middle,
+                          const char *suffix, BOOL takesArg) {
+  if (prefix == NULL && suffix == NULL && !takesArg) {
+    return sel_getUid(middle);
+  }
+  const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
+  const size_t middleLen = strlen(middle);
+  const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
+  size_t totalLen =
+      prefixLen + middleLen + suffixLen + 1;  // include space for null on end.
+  if (takesArg) {
+    totalLen += 1;
+  }
+  char buffer[totalLen];
+  if (prefix != NULL) {
+    memcpy(buffer, prefix, prefixLen);
+    memcpy(buffer + prefixLen, middle, middleLen);
+    buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
+  } else {
+    memcpy(buffer, middle, middleLen);
+  }
+  if (suffix != NULL) {
+    memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
+  }
+  if (takesArg) {
+    buffer[totalLen - 2] = ':';
+  }
+  // Always null terminate it.
+  buffer[totalLen - 1] = 0;
+
+  SEL result = sel_getUid(buffer);
+  return result;
+}
+
+static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
+                                          NSArray *allMessageFields)
+    __attribute__((ns_returns_retained));
+
+static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
+                                          NSArray *allMessageFields) {
+  NSMutableArray *result = [[NSMutableArray alloc] init];
+  for (GPBFieldDescriptor *fieldDesc in allMessageFields) {
+    if (fieldDesc->description_->hasIndex == hasIndex) {
+      [result addObject:fieldDesc];
+    }
+  }
+  return result;
+}
+
+@implementation GPBDescriptor {
+  Class messageClass_;
+  NSArray *enums_;
+  NSArray *extensions_;
+  GPBFileDescriptor *file_;
+  BOOL wireFormat_;
+}
+
+@synthesize messageClass = messageClass_;
+@synthesize fields = fields_;
+@synthesize oneofs = oneofs_;
+@synthesize enums = enums_;
+@synthesize extensions = extensions_;
+@synthesize extensionRanges = extensionRanges_;
+@synthesize extensionRangesCount = extensionRangesCount_;
+@synthesize file = file_;
+@synthesize wireFormat = wireFormat_;
+
++ (instancetype)
+    allocDescriptorForClass:(Class)messageClass
+                  rootClass:(Class)rootClass
+                       file:(GPBFileDescriptor *)file
+                     fields:(GPBMessageFieldDescription *)fieldDescriptions
+                 fieldCount:(NSUInteger)fieldCount
+                     oneofs:(GPBMessageOneofDescription *)oneofDescriptions
+                 oneofCount:(NSUInteger)oneofCount
+                      enums:(GPBMessageEnumDescription *)enumDescriptions
+                  enumCount:(NSUInteger)enumCount
+                     ranges:(const GPBExtensionRange *)ranges
+                 rangeCount:(NSUInteger)rangeCount
+                storageSize:(size_t)storageSize
+                 wireFormat:(BOOL)wireFormat {
+  NSMutableArray *fields = nil;
+  NSMutableArray *oneofs = nil;
+  NSMutableArray *enums = nil;
+  NSMutableArray *extensionRanges = nil;
+  GPBFileSyntax syntax = file.syntax;
+  for (NSUInteger i = 0; i < fieldCount; ++i) {
+    if (fields == nil) {
+      fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
+    }
+    GPBFieldDescriptor *fieldDescriptor = [[GPBFieldDescriptor alloc]
+        initWithFieldDescription:&fieldDescriptions[i]
+                       rootClass:rootClass
+                          syntax:syntax];
+    [fields addObject:fieldDescriptor];
+    [fieldDescriptor release];
+  }
+  for (NSUInteger i = 0; i < oneofCount; ++i) {
+    if (oneofs == nil) {
+      oneofs = [[NSMutableArray alloc] initWithCapacity:oneofCount];
+    }
+    GPBMessageOneofDescription *oneofDescription = &oneofDescriptions[i];
+    NSArray *fieldsForOneof =
+        NewFieldsArrayForHasIndex(oneofDescription->index, fields);
+    GPBOneofDescriptor *oneofDescriptor =
+        [[GPBOneofDescriptor alloc] initWithOneofDescription:oneofDescription
+                                                      fields:fieldsForOneof];
+    [oneofs addObject:oneofDescriptor];
+    [oneofDescriptor release];
+    [fieldsForOneof release];
+  }
+  for (NSUInteger i = 0; i < enumCount; ++i) {
+    if (enums == nil) {
+      enums = [[NSMutableArray alloc] initWithCapacity:enumCount];
+    }
+    GPBEnumDescriptor *enumDescriptor =
+        enumDescriptions[i].enumDescriptorFunc();
+    [enums addObject:enumDescriptor];
+  }
+
+  // TODO(dmaclach): Add support for extensions
+  GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
+                                                     file:file
+                                                   fields:fields
+                                                   oneofs:oneofs
+                                                    enums:enums
+                                               extensions:nil
+                                          extensionRanges:ranges
+                                     extensionRangesCount:rangeCount
+                                              storageSize:storageSize
+                                               wireFormat:wireFormat];
+  [fields release];
+  [oneofs release];
+  [enums release];
+  [extensionRanges release];
+  return descriptor;
+}
+
++ (instancetype)
+    allocDescriptorForClass:(Class)messageClass
+                  rootClass:(Class)rootClass
+                       file:(GPBFileDescriptor *)file
+                     fields:(GPBMessageFieldDescription *)fieldDescriptions
+                 fieldCount:(NSUInteger)fieldCount
+                     oneofs:(GPBMessageOneofDescription *)oneofDescriptions
+                 oneofCount:(NSUInteger)oneofCount
+                      enums:(GPBMessageEnumDescription *)enumDescriptions
+                  enumCount:(NSUInteger)enumCount
+                     ranges:(const GPBExtensionRange *)ranges
+                 rangeCount:(NSUInteger)rangeCount
+                storageSize:(size_t)storageSize
+                 wireFormat:(BOOL)wireFormat
+        extraTextFormatInfo:(const char *)extraTextFormatInfo {
+  GPBDescriptor *descriptor = [self allocDescriptorForClass:messageClass
+                                                  rootClass:rootClass
+                                                       file:file
+                                                     fields:fieldDescriptions
+                                                 fieldCount:fieldCount
+                                                     oneofs:oneofDescriptions
+                                                 oneofCount:oneofCount
+                                                      enums:enumDescriptions
+                                                  enumCount:enumCount
+                                                     ranges:ranges
+                                                 rangeCount:rangeCount
+                                                storageSize:storageSize
+                                                 wireFormat:wireFormat];
+  // Extra info is a compile time option, so skip the work if not needed.
+  if (extraTextFormatInfo) {
+    NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
+    for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) {
+      if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
+        objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey,
+                                 extraInfoValue,
+                                 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+      }
+    }
+  }
+  return descriptor;
+}
+
+- (instancetype)initWithClass:(Class)messageClass
+                         file:(GPBFileDescriptor *)file
+                       fields:(NSArray *)fields
+                       oneofs:(NSArray *)oneofs
+                        enums:(NSArray *)enums
+                   extensions:(NSArray *)extensions
+              extensionRanges:(const GPBExtensionRange *)extensionRanges
+         extensionRangesCount:(NSUInteger)extensionRangesCount
+                  storageSize:(size_t)storageSize
+                   wireFormat:(BOOL)wireFormat {
+  if ((self = [super init])) {
+    messageClass_ = messageClass;
+    file_ = file;
+    fields_ = [fields retain];
+    oneofs_ = [oneofs retain];
+    enums_ = [enums retain];
+    extensions_ = [extensions retain];
+    extensionRanges_ = extensionRanges;
+    extensionRangesCount_ = extensionRangesCount;
+    storageSize_ = storageSize;
+    wireFormat_ = wireFormat;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [fields_ release];
+  [oneofs_ release];
+  [enums_ release];
+  [extensions_ release];
+  [super dealloc];
+}
+
+- (NSString *)name {
+  return NSStringFromClass(messageClass_);
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+#pragma unused(zone)
+  return [self retain];
+}
+
+- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if (GPBFieldNumber(descriptor) == fieldNumber) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
+  for (GPBOneofDescriptor *descriptor in oneofs_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBEnumDescriptor *)enumWithName:(NSString *)name {
+  for (GPBEnumDescriptor *descriptor in enums_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBFieldDescriptor *)extensionWithNumber:(uint32_t)fieldNumber {
+  for (GPBFieldDescriptor *descriptor in extensions_) {
+    if (GPBFieldNumber(descriptor) == fieldNumber) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBFieldDescriptor *)extensionWithName:(NSString *)name {
+  for (GPBFieldDescriptor *descriptor in extensions_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+@end
+
+@implementation GPBFileDescriptor {
+  NSString *package_;
+  GPBFileSyntax syntax_;
+}
+
+@synthesize package = package_;
+@synthesize syntax = syntax_;
+
+- (instancetype)initWithPackage:(NSString *)package
+                         syntax:(GPBFileSyntax)syntax {
+  self = [super init];
+  if (self) {
+    package_ = [package copy];
+    syntax_ = syntax;
+  }
+  return self;
+}
+
+@end
+
+@implementation GPBOneofDescriptor
+
+@synthesize fields = fields_;
+
+- (instancetype)initWithOneofDescription:
+                    (GPBMessageOneofDescription *)oneofDescription
+                                  fields:(NSArray *)fields {
+  self = [super init];
+  if (self) {
+    NSAssert(oneofDescription->index < 0, @"Should always be <0");
+    oneofDescription_ = oneofDescription;
+    fields_ = [fields retain];
+    for (GPBFieldDescriptor *fieldDesc in fields) {
+      fieldDesc->containingOneof_ = self;
+    }
+
+    caseSel_ = SelFromStrings(NULL, oneofDescription->name, "OneOfCase", NO);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [fields_ release];
+  [super dealloc];
+}
+
+- (NSString *)name {
+  return [NSString stringWithUTF8String:oneofDescription_->name];
+}
+
+- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if (GPBFieldNumber(descriptor) == fieldNumber) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+@end
+
+uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
+  GPBMessageFieldDescription *description = self->description_;
+  GPBWireFormat format;
+  if ((description->flags & GPBFieldMapKeyMask) != 0) {
+    // Maps are repeated messages on the wire.
+    format = GPBWireFormatForType(GPBTypeMessage, NO);
+  } else {
+    format = GPBWireFormatForType(description->type,
+                                  description->flags & GPBFieldPacked);
+  }
+  return GPBWireFormatMakeTag(description->number, format);
+}
+
+@implementation GPBFieldDescriptor {
+  GPBValue defaultValue_;
+  GPBFieldOptions *fieldOptions_;
+
+  // Message ivars
+  Class msgClass_;
+
+  // Enum ivars.
+  // If protos are generated with GenerateEnumDescriptors on then it will
+  // be a enumDescriptor, otherwise it will be a enumVerifier.
+  union {
+    GPBEnumDescriptor *enumDescriptor_;
+    GPBEnumValidationFunc enumVerifier_;
+  } enumHandling_;
+}
+
+@synthesize fieldOptions = fieldOptions_;
+@synthesize msgClass = msgClass_;
+@synthesize containingOneof = containingOneof_;
+
+- (instancetype)init {
+  // Throw an exception if people attempt to not use the designated initializer.
+  self = [super init];
+  if (self != nil) {
+    [self doesNotRecognizeSelector:_cmd];
+    self = nil;
+  }
+  return self;
+}
+
+- (instancetype)initWithFieldDescription:
+                    (GPBMessageFieldDescription *)description
+                               rootClass:(Class)rootClass
+                                  syntax:(GPBFileSyntax)syntax {
+  if ((self = [super init])) {
+    description_ = description;
+    getSel_ = sel_getUid(description->name);
+    setSel_ = SelFromStrings("set", description->name, NULL, YES);
+
+    if (description->fieldOptions) {
+      // FieldOptions stored as a length prefixed c-escaped string in descriptor
+      // records.
+      uint8_t *optionsBytes = (uint8_t *)description->fieldOptions;
+      uint32_t optionsLength = *((uint32_t *)optionsBytes);
+      // The length is stored in network byte order.
+      optionsLength = ntohl(optionsLength);
+      if (optionsLength > 0) {
+        optionsBytes += sizeof(optionsLength);
+        NSData *optionsData = [NSData dataWithBytesNoCopy:optionsBytes
+                                                   length:optionsLength
+                                             freeWhenDone:NO];
+        GPBExtensionRegistry *registry = [rootClass extensionRegistry];
+        fieldOptions_ = [[GPBFieldOptions parseFromData:optionsData
+                                      extensionRegistry:registry] retain];
+      }
+    }
+
+    GPBType type = description->type;
+    BOOL isMessage = GPBTypeIsMessage(type);
+    if (isMessage) {
+      // No has* for repeated/map or something in a oneof (we can't check
+      // containingOneof_ because it isn't set until after initialization).
+      if ((description->hasIndex >= 0) &&
+          (description->hasIndex != GPBNoHasBit)) {
+        hasSel_ = SelFromStrings("has", description->name, NULL, NO);
+        setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES);
+      }
+      const char *className = description->typeSpecific.className;
+      msgClass_ = objc_getClass(className);
+      NSAssert1(msgClass_, @"Class %s not defined", className);
+      // The defaultValue_ is fetched directly in -defaultValue to avoid
+      // initialization order issues.
+    } else {
+      if (!GPBFieldIsMapOrArray(self)) {
+        defaultValue_ = description->defaultValue;
+        if (type == GPBTypeData) {
+          // Data stored as a length prefixed c-string in descriptor records.
+          const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
+          if (bytes) {
+            uint32_t length = *((uint32_t *)bytes);
+            // The length is stored in network byte order.
+            length = ntohl(length);
+            bytes += sizeof(length);
+            defaultValue_.valueData =
+                [[NSData alloc] initWithBytes:bytes length:length];
+          }
+        }
+        // No has* methods for proto3 or if our hasIndex is < 0 because it
+        // means the field is in a oneof (we can't check containingOneof_
+        // because it isn't set until after initialization).
+        if ((syntax != GPBFileSyntaxProto3) && (description->hasIndex >= 0) &&
+            (description->hasIndex != GPBNoHasBit)) {
+          hasSel_ = SelFromStrings("has", description->name, NULL, NO);
+          setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES);
+        }
+      }
+      if (GPBTypeIsEnum(type)) {
+        if (description_->flags & GPBFieldHasEnumDescriptor) {
+          enumHandling_.enumDescriptor_ =
+              description->typeSpecific.enumDescFunc();
+        } else {
+          enumHandling_.enumVerifier_ = description->typeSpecific.enumVerifier;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (void)dealloc {
+  if (description_->type == GPBTypeData &&
+      !(description_->flags & GPBFieldRepeated)) {
+    [defaultValue_.valueData release];
+  }
+  [super dealloc];
+}
+
+- (GPBType)type {
+  return description_->type;
+}
+
+- (BOOL)hasDefaultValue {
+  return (description_->flags & GPBFieldHasDefaultValue) != 0;
+}
+
+- (uint32_t)number {
+  return description_->number;
+}
+
+- (NSString *)name {
+  return [NSString stringWithUTF8String:description_->name];
+}
+
+- (BOOL)isRequired {
+  return (description_->flags & GPBFieldRequired) != 0;
+}
+
+- (BOOL)isOptional {
+  return (description_->flags & GPBFieldOptional) != 0;
+}
+
+- (GPBFieldType)fieldType {
+  GPBFieldFlags flags = description_->flags;
+  if ((flags & GPBFieldRepeated) != 0) {
+    return GPBFieldTypeRepeated;
+  } else if ((flags & GPBFieldMapKeyMask) != 0) {
+    return GPBFieldTypeMap;
+  } else {
+    return GPBFieldTypeSingle;
+  }
+}
+
+- (GPBType)mapKeyType {
+  switch (description_->flags & GPBFieldMapKeyMask) {
+    case GPBFieldMapKeyInt32:
+      return GPBTypeInt32;
+    case GPBFieldMapKeyInt64:
+      return GPBTypeInt64;
+    case GPBFieldMapKeyUInt32:
+      return GPBTypeUInt32;
+    case GPBFieldMapKeyUInt64:
+      return GPBTypeUInt64;
+    case GPBFieldMapKeySInt32:
+      return GPBTypeSInt32;
+    case GPBFieldMapKeySInt64:
+      return GPBTypeSInt64;
+    case GPBFieldMapKeyFixed32:
+      return GPBTypeFixed32;
+    case GPBFieldMapKeyFixed64:
+      return GPBTypeFixed64;
+    case GPBFieldMapKeySFixed32:
+      return GPBTypeSFixed32;
+    case GPBFieldMapKeySFixed64:
+      return GPBTypeSFixed64;
+    case GPBFieldMapKeyBool:
+      return GPBTypeBool;
+    case GPBFieldMapKeyString:
+      return GPBTypeString;
+
+    default:
+      NSAssert(0, @"Not a map type");
+      return GPBTypeInt32;  // For lack of anything better.
+  }
+}
+
+- (BOOL)isPackable {
+  return (description_->flags & GPBFieldPacked) != 0;
+}
+
+- (BOOL)isValidEnumValue:(int32_t)value {
+  NSAssert(description_->type == GPBTypeEnum,
+           @"Field Must be of type GPBTypeEnum");
+  if (description_->flags & GPBFieldHasEnumDescriptor) {
+    return enumHandling_.enumDescriptor_.enumVerifier(value);
+  } else {
+    return enumHandling_.enumVerifier_(value);
+  }
+}
+
+- (GPBEnumDescriptor *)enumDescriptor {
+  if (description_->flags & GPBFieldHasEnumDescriptor) {
+    return enumHandling_.enumDescriptor_;
+  } else {
+    return nil;
+  }
+}
+
+- (GPBValue)defaultValue {
+  // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
+  // to an actual defaultValue in our initializer.
+  GPBValue value = defaultValue_;
+
+  if (!(description_->flags & GPBFieldRepeated)) {
+    // We special handle data and strings. If they are nil, we replace them
+    // with empty string/empty data.
+    GPBType type = description_->type;
+    if (type == GPBTypeData && value.valueData == nil) {
+      value.valueData = GPBEmptyNSData();
+    } else if (type == GPBTypeString && value.valueString == nil) {
+      value.valueString = @"";
+    }
+  }
+  return value;
+}
+
+- (NSString *)textFormatName {
+  if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
+    NSValue *extraInfoValue =
+        objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
+    // Support can be left out at generation time.
+    if (!extraInfoValue) {
+      return nil;
+    }
+    const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
+    return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
+                                   self.name);
+  }
+
+  // The logic here has to match SetCommonFieldVariables() from
+  // objectivec_field.cc in the proto compiler.
+  NSString *name = self.name;
+  NSUInteger len = [name length];
+
+  // Remove the "_p" added to reserved names.
+  if ([name hasSuffix:@"_p"]) {
+    name = [name substringToIndex:(len - 2)];
+    len = [name length];
+  }
+
+  // Remove "Array" from the end for repeated fields.
+  if (((description_->flags & GPBFieldRepeated) != 0) &&
+      [name hasSuffix:@"Array"]) {
+    name = [name substringToIndex:(len - 5)];
+    len = [name length];
+  }
+
+  // Groups vs. other fields.
+  if (description_->type == GPBTypeGroup) {
+    // Just capitalize the first letter.
+    unichar firstChar = [name characterAtIndex:0];
+    if (firstChar >= 'a' && firstChar <= 'z') {
+      NSString *firstCharString =
+          [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
+      NSString *result =
+          [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
+                                        withString:firstCharString];
+      return result;
+    }
+    return name;
+
+  } else {
+    // Undo the CamelCase.
+    NSMutableString *result = [NSMutableString stringWithCapacity:len];
+    for (NSUInteger i = 0; i < len; i++) {
+      unichar c = [name characterAtIndex:i];
+      if (c >= 'A' && c <= 'Z') {
+        if (i > 0) {
+          [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
+        } else {
+          [result appendFormat:@"%C", c];
+        }
+      } else {
+        [result appendFormat:@"%C", c];
+      }
+    }
+    return result;
+  }
+}
+
+@end
+
+@implementation GPBEnumDescriptor {
+  NSString *name_;
+  GPBMessageEnumValueDescription *valueDescriptions_;
+  NSUInteger valueDescriptionsCount_;
+  GPBEnumValidationFunc enumVerifier_;
+  const uint8_t *extraTextFormatInfo_;
+}
+
+@synthesize name = name_;
+@synthesize enumVerifier = enumVerifier_;
+
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                    values:(GPBMessageEnumValueDescription *)valueDescriptions
+                valueCount:(NSUInteger)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier {
+  GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
+                                                      values:valueDescriptions
+                                                  valueCount:valueCount
+                                                enumVerifier:enumVerifier];
+  return descriptor;
+}
+
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                    values:(GPBMessageEnumValueDescription *)valueDescriptions
+                valueCount:(NSUInteger)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier
+       extraTextFormatInfo:(const char *)extraTextFormatInfo {
+  // Call the common case.
+  GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
+                                                        values:valueDescriptions
+                                                    valueCount:valueCount
+                                                  enumVerifier:enumVerifier];
+  // Set the extra info.
+  descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
+  return descriptor;
+}
+
+- (instancetype)initWithName:(NSString *)name
+                      values:(GPBMessageEnumValueDescription *)valueDescriptions
+                  valueCount:(NSUInteger)valueCount
+                enumVerifier:(GPBEnumValidationFunc)enumVerifier {
+  if ((self = [super init])) {
+    name_ = [name copy];
+    valueDescriptions_ = valueDescriptions;
+    valueDescriptionsCount_ = valueCount;
+    enumVerifier_ = enumVerifier;
+  }
+  return self;
+}
+
+- (NSString *)enumNameForValue:(int32_t)number {
+  for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) {
+    GPBMessageEnumValueDescription *scan = &valueDescriptions_[i];
+    if ((scan->number == number) && (scan->name != NULL)) {
+      NSString *fullName =
+          [NSString stringWithFormat:@"%@_%s", name_, scan->name];
+      return fullName;
+    }
+  }
+  return nil;
+}
+
+- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
+  // Must have the prefix.
+  NSUInteger prefixLen = name_.length + 1;
+  if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
+      ([name characterAtIndex:prefixLen - 1] != '_')) {
+    return NO;
+  }
+
+  // Skip over the prefix.
+  const char *nameAsCStr = [name UTF8String];
+  nameAsCStr += prefixLen;
+
+  // Find it.
+  for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) {
+    GPBMessageEnumValueDescription *scan = &valueDescriptions_[i];
+    if ((scan->name != NULL) && (strcmp(nameAsCStr, scan->name) == 0)) {
+      if (outValue) {
+        *outValue = scan->number;
+      }
+      return YES;
+    }
+  }
+  return NO;
+}
+
+- (void)dealloc {
+  [name_ release];
+  [super dealloc];
+}
+
+- (NSString *)textFormatNameForValue:(int32_t)number {
+  // Find the EnumValue descriptor and its index.
+  GPBMessageEnumValueDescription *valueDescriptor = NULL;
+  NSUInteger valueDescriptorIndex;
+  for (valueDescriptorIndex = 0; valueDescriptorIndex < valueDescriptionsCount_;
+       ++valueDescriptorIndex) {
+    GPBMessageEnumValueDescription *scan =
+        &valueDescriptions_[valueDescriptorIndex];
+    if (scan->number == number) {
+      valueDescriptor = scan;
+      break;
+    }
+  }
+
+  // If we didn't find it, or names were disable at proto compile time, nothing
+  // we can do.
+  if (!valueDescriptor || !valueDescriptor->name) {
+    return nil;
+  }
+
+  NSString *result = nil;
+  // Naming adds an underscore between enum name and value name, skip that also.
+  NSString *shortName = [NSString stringWithUTF8String:valueDescriptor->name];
+
+  // See if it is in the map of special format handling.
+  if (extraTextFormatInfo_) {
+    result = GPBDecodeTextFormatName(extraTextFormatInfo_,
+                                     (int32_t)valueDescriptorIndex, shortName);
+  }
+  // Logic here needs to match what objectivec_enum.cc does in the proto
+  // compiler.
+  if (result == nil) {
+    NSUInteger len = [shortName length];
+    NSMutableString *worker = [NSMutableString stringWithCapacity:len];
+    for (NSUInteger i = 0; i < len; i++) {
+      unichar c = [shortName characterAtIndex:i];
+      if (i > 0 && c >= 'A' && c <= 'Z') {
+        [worker appendString:@"_"];
+      }
+      [worker appendFormat:@"%c", toupper((char)c)];
+    }
+    result = worker;
+  }
+  return result;
+}
+
+@end
+
+@implementation GPBExtensionDescriptor
+
+- (instancetype)initWithExtensionDescription:
+        (GPBExtensionDescription *)description {
+  if ((self = [super init])) {
+    description_ = description;
+  }
+  return self;
+}
+
+- (NSString *)singletonName {
+  return [NSString stringWithUTF8String:description_->singletonName];
+}
+
+- (const char *)singletonNameC {
+  return description_->singletonName;
+}
+
+- (uint32_t)fieldNumber {
+  return description_->fieldNumber;
+}
+
+- (GPBType)type {
+  return description_->type;
+}
+
+- (BOOL)isRepeated {
+  return (description_->options & GPBExtensionRepeated) != 0;
+}
+
+- (BOOL)isMap {
+  return (description_->options & GPBFieldMapKeyMask) != 0;
+}
+
+- (BOOL)isPackable {
+  return (description_->options & GPBExtensionPacked) != 0;
+}
+
+- (Class)msgClass {
+  return objc_getClass(description_->messageOrGroupClassName);
+}
+
+- (GPBEnumDescriptor *)enumDescriptor {
+  if (GPBTypeIsEnum(description_->type)) {
+    GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
+    return enumDescriptor;
+  }
+  return nil;
+}
+
+@end

+ 293 - 0
objectivec/GPBDescriptor_PackagePrivate.h

@@ -0,0 +1,293 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This header is private to the ProtobolBuffers library and must NOT be
+// included by any sources outside this library. The contents of this file are
+// subject to change at any time without notice.
+
+#import "GPBDescriptor.h"
+
+// Describes attributes of the field.
+typedef NS_OPTIONS(uint32_t, GPBFieldFlags) {
+  // These map to standard protobuf concepts.
+  GPBFieldRequired        = 1 << 0,
+  GPBFieldRepeated        = 1 << 1,
+  GPBFieldPacked          = 1 << 2,
+  GPBFieldOptional        = 1 << 3,
+  GPBFieldHasDefaultValue = 1 << 4,
+
+  // These are not standard protobuf concepts, they are specific to the
+  // Objective C runtime.
+
+  // These bits are used to mark the field as a map and what the key
+  // type is.
+  GPBFieldMapKeyMask     = 0xF << 8,
+  GPBFieldMapKeyInt32    =  1 << 8,
+  GPBFieldMapKeyInt64    =  2 << 8,
+  GPBFieldMapKeyUInt32   =  3 << 8,
+  GPBFieldMapKeyUInt64   =  4 << 8,
+  GPBFieldMapKeySInt32   =  5 << 8,
+  GPBFieldMapKeySInt64   =  6 << 8,
+  GPBFieldMapKeyFixed32  =  7 << 8,
+  GPBFieldMapKeyFixed64  =  8 << 8,
+  GPBFieldMapKeySFixed32 =  9 << 8,
+  GPBFieldMapKeySFixed64 = 10 << 8,
+  GPBFieldMapKeyBool     = 11 << 8,
+  GPBFieldMapKeyString   = 12 << 8,
+
+  // Indicates the field needs custom handling for the TextFormat name, if not
+  // set, the name can be derived from the ObjC name.
+  GPBFieldTextFormatNameCustom = 1 << 16,
+  // Indicates the field has an enum descriptor.
+  // TODO(thomasvl): Output the CPP check to use descFunc or validator based
+  // on final compile.  This will then get added based on that.
+  GPBFieldHasEnumDescriptor = 1 << 17,
+};
+
+// Describes a single field in a protobuf as it is represented as an ivar.
+typedef struct GPBMessageFieldDescription {
+  // Name of ivar.
+  const char *name;
+  // The field number for the ivar.
+  uint32_t number;
+  // The index (in bits) into _has_storage_.
+  //   > 0: the bit to use for a value being set.
+  //   = 0: no storage used.
+  //   < 0: in a oneOf, use a full int32 to record the field active.
+  int32_t hasIndex;
+  // Field flags. Use accessor functions below.
+  GPBFieldFlags flags;
+  // Type of the ivar.
+  GPBType type;
+  // Offset of the variable into it's structure struct.
+  size_t offset;
+  // FieldOptions protobuf, serialized as string.
+  const char *fieldOptions;
+
+  GPBValue defaultValue;  // Default value for the ivar.
+  union {
+    const char *className;  // Name for message class.
+    // For enums only: If EnumDescriptors are compiled in, it will be that,
+    // otherwise it will be the verifier.
+    GPBEnumDescriptorFunc enumDescFunc;
+    GPBEnumValidationFunc enumVerifier;
+  } typeSpecific;
+} GPBMessageFieldDescription;
+
+// Describes a oneof.
+typedef struct GPBMessageOneofDescription {
+  // Name of this enum oneof.
+  const char *name;
+  // The index of this oneof in the has_storage.
+  int32_t index;
+} GPBMessageOneofDescription;
+
+// Describes an enum type defined in a .proto file.
+typedef struct GPBMessageEnumDescription {
+  GPBEnumDescriptorFunc enumDescriptorFunc;
+} GPBMessageEnumDescription;
+
+// Describes an individual enum constant of a particular type.
+typedef struct GPBMessageEnumValueDescription {
+  // Name of this enum constant.
+  const char *name;
+  // Numeric value of this enum constant.
+  int32_t number;
+} GPBMessageEnumValueDescription;
+
+// Describes attributes of the extension.
+typedef NS_OPTIONS(uint32_t, GPBExtensionOptions) {
+  // These map to standard protobuf concepts.
+  GPBExtensionRepeated      = 1 << 0,
+  GPBExtensionPacked        = 1 << 1,
+  GPBExtensionSetWireFormat = 1 << 2,
+};
+
+// An extension
+typedef struct GPBExtensionDescription {
+  const char *singletonName;
+  GPBType type;
+  const char *extendedClass;
+  int32_t fieldNumber;
+  GPBValue defaultValue;
+  const char *messageOrGroupClassName;
+  GPBExtensionOptions options;
+  GPBEnumDescriptorFunc enumDescriptorFunc;
+} GPBExtensionDescription;
+
+@interface GPBDescriptor () {
+ @package
+  NSArray *fields_;
+  NSArray *oneofs_;
+  size_t storageSize_;
+}
+
+// fieldDescriptions, enumDescriptions, rangeDescriptions, and
+// extraTextFormatInfo have to be long lived, they are held as raw pointers.
++ (instancetype)
+    allocDescriptorForClass:(Class)messageClass
+                  rootClass:(Class)rootClass
+                       file:(GPBFileDescriptor *)file
+                     fields:(GPBMessageFieldDescription *)fieldDescriptions
+                 fieldCount:(NSUInteger)fieldCount
+                     oneofs:(GPBMessageOneofDescription *)oneofDescriptions
+                 oneofCount:(NSUInteger)oneofCount
+                      enums:(GPBMessageEnumDescription *)enumDescriptions
+                  enumCount:(NSUInteger)enumCount
+                     ranges:(const GPBExtensionRange *)ranges
+                 rangeCount:(NSUInteger)rangeCount
+                storageSize:(size_t)storageSize
+                 wireFormat:(BOOL)wireFormat;
++ (instancetype)
+    allocDescriptorForClass:(Class)messageClass
+                  rootClass:(Class)rootClass
+                       file:(GPBFileDescriptor *)file
+                     fields:(GPBMessageFieldDescription *)fieldDescriptions
+                 fieldCount:(NSUInteger)fieldCount
+                     oneofs:(GPBMessageOneofDescription *)oneofDescriptions
+                 oneofCount:(NSUInteger)oneofCount
+                      enums:(GPBMessageEnumDescription *)enumDescriptions
+                  enumCount:(NSUInteger)enumCount
+                     ranges:(const GPBExtensionRange *)ranges
+                 rangeCount:(NSUInteger)rangeCount
+                storageSize:(size_t)storageSize
+                 wireFormat:(BOOL)wireFormat
+        extraTextFormatInfo:(const char *)extraTextFormatInfo;
+
+- (instancetype)initWithClass:(Class)messageClass
+                         file:(GPBFileDescriptor *)file
+                       fields:(NSArray *)fields
+                       oneofs:(NSArray *)oneofs
+                        enums:(NSArray *)enums
+                   extensions:(NSArray *)extensions
+              extensionRanges:(const GPBExtensionRange *)ranges
+         extensionRangesCount:(NSUInteger)rangeCount
+                  storageSize:(size_t)storage
+                   wireFormat:(BOOL)wireFormat;
+
+@end
+
+@interface GPBFileDescriptor ()
+- (instancetype)initWithPackage:(NSString *)package
+                         syntax:(GPBFileSyntax)syntax;
+@end
+
+@interface GPBOneofDescriptor () {
+ @package
+  GPBMessageOneofDescription *oneofDescription_;
+  NSArray *fields_;
+
+  SEL caseSel_;
+}
+- (instancetype)initWithOneofDescription:
+                    (GPBMessageOneofDescription *)oneofDescription
+                                  fields:(NSArray *)fields;
+@end
+
+@interface GPBFieldDescriptor () {
+ @package
+  GPBMessageFieldDescription *description_;
+  GPB_UNSAFE_UNRETAINED GPBOneofDescriptor *containingOneof_;
+
+  SEL getSel_;
+  SEL setSel_;
+  SEL hasSel_;
+  SEL setHasSel_;
+}
+
+// Single initializer
+// description has to be long lived, it is held as a raw pointer.
+- (instancetype)initWithFieldDescription:
+                    (GPBMessageFieldDescription *)description
+                               rootClass:(Class)rootClass
+                                  syntax:(GPBFileSyntax)syntax;
+@end
+
+@interface GPBEnumDescriptor ()
+// valueDescriptions and extraTextFormatInfo have to be long lived, they are
+// held as raw pointers.
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                    values:(GPBMessageEnumValueDescription *)valueDescriptions
+                valueCount:(NSUInteger)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier;
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                    values:(GPBMessageEnumValueDescription *)valueDescriptions
+                valueCount:(NSUInteger)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier
+       extraTextFormatInfo:(const char *)extraTextFormatInfo;
+
+- (instancetype)initWithName:(NSString *)name
+                      values:(GPBMessageEnumValueDescription *)valueDescriptions
+                  valueCount:(NSUInteger)valueCount
+                enumVerifier:(GPBEnumValidationFunc)enumVerifier;
+@end
+
+@interface GPBExtensionDescriptor () {
+ @package
+  GPBExtensionDescription *description_;
+}
+
+// description has to be long lived, it is held as a raw pointer.
+- (instancetype)initWithExtensionDescription:
+        (GPBExtensionDescription *)description;
+@end
+
+CF_EXTERN_C_BEGIN
+
+GPB_INLINE BOOL GPBFieldIsMapOrArray(GPBFieldDescriptor *field) {
+  return (field->description_->flags &
+          (GPBFieldRepeated | GPBFieldMapKeyMask)) != 0;
+}
+
+GPB_INLINE GPBType GPBGetFieldType(GPBFieldDescriptor *field) {
+  return field->description_->type;
+}
+
+GPB_INLINE int32_t GPBFieldHasIndex(GPBFieldDescriptor *field) {
+  return field->description_->hasIndex;
+}
+
+GPB_INLINE uint32_t GPBFieldNumber(GPBFieldDescriptor *field) {
+  return field->description_->number;
+}
+
+uint32_t GPBFieldTag(GPBFieldDescriptor *self);
+
+GPB_INLINE BOOL GPBPreserveUnknownFields(GPBFileSyntax syntax) {
+  return syntax != GPBFileSyntaxProto3;
+}
+
+GPB_INLINE BOOL GPBHasPreservingUnknownEnumSemantics(GPBFileSyntax syntax) {
+  return syntax == GPBFileSyntaxProto3;
+}
+
+CF_EXTERN_C_END

+ 2233 - 0
objectivec/GPBDictionary.h

@@ -0,0 +1,2233 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBTypes.h"
+
+// These classes are used for map fields of basic data types. They are used because
+// they perform better than boxing into NSNumbers in NSDictionaries.
+
+// Note: These are not meant to be subclassed.
+
+//%PDDM-EXPAND DECLARE_DICTIONARIES()
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32 -> UInt32
+
+@interface GPBUInt32UInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32UInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32UInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(uint32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, uint32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32UInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(uint32_t)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Int32
+
+@interface GPBUInt32Int32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32Int32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32Int32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32Int32Dictionary *)otherDictionary;
+
+- (void)setValue:(int32_t)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> UInt64
+
+@interface GPBUInt32UInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32UInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32UInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(uint64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, uint64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32UInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(uint64_t)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Int64
+
+@interface GPBUInt32Int64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32Int64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32Int64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(int64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, int64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32Int64Dictionary *)otherDictionary;
+
+- (void)setValue:(int64_t)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Bool
+
+@interface GPBUInt32BoolDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32BoolDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32BoolDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(BOOL *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, BOOL value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32BoolDictionary *)otherDictionary;
+
+- (void)setValue:(BOOL)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Float
+
+@interface GPBUInt32FloatDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32FloatDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32FloatDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(float *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, float value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32FloatDictionary *)otherDictionary;
+
+- (void)setValue:(float)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Double
+
+@interface GPBUInt32DoubleDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32DoubleDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32DoubleDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint32_t)key value:(double *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, double value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32DoubleDictionary *)otherDictionary;
+
+- (void)setValue:(double)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Enum
+
+@interface GPBUInt32EnumDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const uint32_t [])keys
+                                           count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32EnumDictionary *)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                   forKeys:(const uint32_t [])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32EnumDictionary *)dictionary;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (BOOL)valueForKey:(uint32_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (BOOL)valueForKey:(uint32_t)key rawValue:(int32_t *)rawValue;
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t rawValue, BOOL *stop))block;
+
+- (void)addRawEntriesFromDictionary:(GPBUInt32EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)setValue:(int32_t)value forKey:(uint32_t)key;
+
+// This method bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+- (void)setRawValue:(int32_t)rawValue forKey:(uint32_t)key;
+
+// No validation applies to these methods.
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Object
+
+@interface GPBUInt32ObjectDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(uint32_t)key;
++ (instancetype)dictionaryWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt32ObjectDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt32ObjectDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (id)valueForKey:(uint32_t)key;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, id value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt32ObjectDictionary *)otherDictionary;
+
+- (void)setValue:(id)value forKey:(uint32_t)key;
+
+- (void)removeValueForKey:(uint32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> UInt32
+
+@interface GPBInt32UInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32UInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32UInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(uint32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, uint32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32UInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(uint32_t)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Int32
+
+@interface GPBInt32Int32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32Int32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32Int32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32Int32Dictionary *)otherDictionary;
+
+- (void)setValue:(int32_t)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> UInt64
+
+@interface GPBInt32UInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32UInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32UInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(uint64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, uint64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32UInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(uint64_t)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Int64
+
+@interface GPBInt32Int64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32Int64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32Int64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(int64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, int64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32Int64Dictionary *)otherDictionary;
+
+- (void)setValue:(int64_t)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Bool
+
+@interface GPBInt32BoolDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32BoolDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32BoolDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(BOOL *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, BOOL value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32BoolDictionary *)otherDictionary;
+
+- (void)setValue:(BOOL)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Float
+
+@interface GPBInt32FloatDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32FloatDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32FloatDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(float *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, float value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32FloatDictionary *)otherDictionary;
+
+- (void)setValue:(float)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Double
+
+@interface GPBInt32DoubleDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32DoubleDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32DoubleDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int32_t)key value:(double *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, double value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32DoubleDictionary *)otherDictionary;
+
+- (void)setValue:(double)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Enum
+
+@interface GPBInt32EnumDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(int32_t)key;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const int32_t [])keys
+                                           count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32EnumDictionary *)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                   forKeys:(const int32_t [])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32EnumDictionary *)dictionary;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (BOOL)valueForKey:(int32_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (BOOL)valueForKey:(int32_t)key rawValue:(int32_t *)rawValue;
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int32_t key, int32_t rawValue, BOOL *stop))block;
+
+- (void)addRawEntriesFromDictionary:(GPBInt32EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)setValue:(int32_t)value forKey:(int32_t)key;
+
+// This method bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+- (void)setRawValue:(int32_t)rawValue forKey:(int32_t)key;
+
+// No validation applies to these methods.
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Object
+
+@interface GPBInt32ObjectDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(int32_t)key;
++ (instancetype)dictionaryWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt32ObjectDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt32ObjectDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (id)valueForKey:(int32_t)key;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, id value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt32ObjectDictionary *)otherDictionary;
+
+- (void)setValue:(id)value forKey:(int32_t)key;
+
+- (void)removeValueForKey:(int32_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> UInt32
+
+@interface GPBUInt64UInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64UInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64UInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(uint32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, uint32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64UInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(uint32_t)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Int32
+
+@interface GPBUInt64Int32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64Int32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64Int32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64Int32Dictionary *)otherDictionary;
+
+- (void)setValue:(int32_t)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> UInt64
+
+@interface GPBUInt64UInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64UInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64UInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(uint64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, uint64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64UInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(uint64_t)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Int64
+
+@interface GPBUInt64Int64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64Int64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64Int64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(int64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, int64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64Int64Dictionary *)otherDictionary;
+
+- (void)setValue:(int64_t)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Bool
+
+@interface GPBUInt64BoolDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64BoolDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64BoolDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(BOOL *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, BOOL value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64BoolDictionary *)otherDictionary;
+
+- (void)setValue:(BOOL)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Float
+
+@interface GPBUInt64FloatDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64FloatDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64FloatDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(float *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, float value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64FloatDictionary *)otherDictionary;
+
+- (void)setValue:(float)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Double
+
+@interface GPBUInt64DoubleDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64DoubleDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64DoubleDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(uint64_t)key value:(double *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, double value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64DoubleDictionary *)otherDictionary;
+
+- (void)setValue:(double)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Enum
+
+@interface GPBUInt64EnumDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const uint64_t [])keys
+                                           count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64EnumDictionary *)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                   forKeys:(const uint64_t [])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64EnumDictionary *)dictionary;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (BOOL)valueForKey:(uint64_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (BOOL)valueForKey:(uint64_t)key rawValue:(int32_t *)rawValue;
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t rawValue, BOOL *stop))block;
+
+- (void)addRawEntriesFromDictionary:(GPBUInt64EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)setValue:(int32_t)value forKey:(uint64_t)key;
+
+// This method bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+- (void)setRawValue:(int32_t)rawValue forKey:(uint64_t)key;
+
+// No validation applies to these methods.
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Object
+
+@interface GPBUInt64ObjectDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(uint64_t)key;
++ (instancetype)dictionaryWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBUInt64ObjectDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBUInt64ObjectDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (id)valueForKey:(uint64_t)key;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, id value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBUInt64ObjectDictionary *)otherDictionary;
+
+- (void)setValue:(id)value forKey:(uint64_t)key;
+
+- (void)removeValueForKey:(uint64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> UInt32
+
+@interface GPBInt64UInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64UInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64UInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(uint32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, uint32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64UInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(uint32_t)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Int32
+
+@interface GPBInt64Int32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64Int32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64Int32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64Int32Dictionary *)otherDictionary;
+
+- (void)setValue:(int32_t)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> UInt64
+
+@interface GPBInt64UInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64UInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64UInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(uint64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, uint64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64UInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(uint64_t)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Int64
+
+@interface GPBInt64Int64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64Int64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64Int64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(int64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, int64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64Int64Dictionary *)otherDictionary;
+
+- (void)setValue:(int64_t)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Bool
+
+@interface GPBInt64BoolDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64BoolDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64BoolDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(BOOL *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, BOOL value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64BoolDictionary *)otherDictionary;
+
+- (void)setValue:(BOOL)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Float
+
+@interface GPBInt64FloatDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64FloatDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64FloatDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(float *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, float value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64FloatDictionary *)otherDictionary;
+
+- (void)setValue:(float)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Double
+
+@interface GPBInt64DoubleDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64DoubleDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64DoubleDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(int64_t)key value:(double *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, double value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64DoubleDictionary *)otherDictionary;
+
+- (void)setValue:(double)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Enum
+
+@interface GPBInt64EnumDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(int64_t)key;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const int64_t [])keys
+                                           count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64EnumDictionary *)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                   forKeys:(const int64_t [])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64EnumDictionary *)dictionary;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (BOOL)valueForKey:(int64_t)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (BOOL)valueForKey:(int64_t)key rawValue:(int32_t *)rawValue;
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int64_t key, int32_t rawValue, BOOL *stop))block;
+
+- (void)addRawEntriesFromDictionary:(GPBInt64EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)setValue:(int32_t)value forKey:(int64_t)key;
+
+// This method bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+- (void)setRawValue:(int32_t)rawValue forKey:(int64_t)key;
+
+// No validation applies to these methods.
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Object
+
+@interface GPBInt64ObjectDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(int64_t)key;
++ (instancetype)dictionaryWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBInt64ObjectDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBInt64ObjectDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (id)valueForKey:(int64_t)key;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, id value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBInt64ObjectDictionary *)otherDictionary;
+
+- (void)setValue:(id)value forKey:(int64_t)key;
+
+- (void)removeValueForKey:(int64_t)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> UInt32
+
+@interface GPBBoolUInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolUInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolUInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(uint32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, uint32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolUInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(uint32_t)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Int32
+
+@interface GPBBoolInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(int32_t)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> UInt64
+
+@interface GPBBoolUInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolUInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolUInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(uint64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, uint64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolUInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(uint64_t)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Int64
+
+@interface GPBBoolInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(int64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, int64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(int64_t)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Bool
+
+@interface GPBBoolBoolDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolBoolDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolBoolDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(BOOL *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, BOOL value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolBoolDictionary *)otherDictionary;
+
+- (void)setValue:(BOOL)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Float
+
+@interface GPBBoolFloatDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolFloatDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolFloatDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(float *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, float value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolFloatDictionary *)otherDictionary;
+
+- (void)setValue:(float)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Double
+
+@interface GPBBoolDoubleDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolDoubleDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolDoubleDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(BOOL)key value:(double *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, double value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolDoubleDictionary *)otherDictionary;
+
+- (void)setValue:(double)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Enum
+
+@interface GPBBoolEnumDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(BOOL)key;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const BOOL [])keys
+                                           count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolEnumDictionary *)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                   forKeys:(const BOOL [])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolEnumDictionary *)dictionary;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (BOOL)valueForKey:(BOOL)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (BOOL)valueForKey:(BOOL)key rawValue:(int32_t *)rawValue;
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(BOOL key, int32_t rawValue, BOOL *stop))block;
+
+- (void)addRawEntriesFromDictionary:(GPBBoolEnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)setValue:(int32_t)value forKey:(BOOL)key;
+
+// This method bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+- (void)setRawValue:(int32_t)rawValue forKey:(BOOL)key;
+
+// No validation applies to these methods.
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Object
+
+@interface GPBBoolObjectDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(BOOL)key;
++ (instancetype)dictionaryWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBBoolObjectDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const id GPB_UNSAFE_UNRETAINED [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBBoolObjectDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (id)valueForKey:(BOOL)key;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, id value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBBoolObjectDictionary *)otherDictionary;
+
+- (void)setValue:(id)value forKey:(BOOL)key;
+
+- (void)removeValueForKey:(BOOL)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> UInt32
+
+@interface GPBStringUInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringUInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringUInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(uint32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, uint32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringUInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(uint32_t)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Int32
+
+@interface GPBStringInt32Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringInt32Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringInt32Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringInt32Dictionary *)otherDictionary;
+
+- (void)setValue:(int32_t)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> UInt64
+
+@interface GPBStringUInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringUInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringUInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(uint64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, uint64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringUInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(uint64_t)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Int64
+
+@interface GPBStringInt64Dictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringInt64Dictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringInt64Dictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(int64_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, int64_t value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringInt64Dictionary *)otherDictionary;
+
+- (void)setValue:(int64_t)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Bool
+
+@interface GPBStringBoolDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringBoolDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringBoolDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(BOOL *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, BOOL value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringBoolDictionary *)otherDictionary;
+
+- (void)setValue:(BOOL)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Float
+
+@interface GPBStringFloatDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringFloatDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringFloatDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(float *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, float value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringFloatDictionary *)otherDictionary;
+
+- (void)setValue:(float)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Double
+
+@interface GPBStringDoubleDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(NSString *)key;
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                               count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringDoubleDictionary *)dictionary;
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringDoubleDictionary *)dictionary;
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+- (BOOL)valueForKey:(NSString *)key value:(double *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, double value, BOOL *stop))block;
+
+- (void)addEntriesFromDictionary:(GPBStringDoubleDictionary *)otherDictionary;
+
+- (void)setValue:(double)value forKey:(NSString *)key;
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Enum
+
+@interface GPBStringEnumDictionary : NSObject <NSCopying>
+
+@property(nonatomic, readonly) NSUInteger count;
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
++ (instancetype)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(NSString *)key;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                                           count:(NSUInteger)count;
++ (instancetype)dictionaryWithDictionary:(GPBStringEnumDictionary *)dictionary;
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems;
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                   forKeys:(const NSString * GPB_UNSAFE_UNRETAINED [])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithDictionary:(GPBStringEnumDictionary *)dictionary;
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+- (BOOL)valueForKey:(NSString *)key value:(int32_t *)value;
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+- (BOOL)valueForKey:(NSString *)key rawValue:(int32_t *)rawValue;
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(NSString *key, int32_t rawValue, BOOL *stop))block;
+
+- (void)addRawEntriesFromDictionary:(GPBStringEnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+- (void)setValue:(int32_t)value forKey:(NSString *)key;
+
+// This method bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+- (void)setRawValue:(int32_t)rawValue forKey:(NSString *)key;
+
+// No validation applies to these methods.
+
+- (void)removeValueForKey:(NSString *)aKey;
+- (void)removeAll;
+
+@end
+
+//%PDDM-EXPAND-END DECLARE_DICTIONARIES()
+
+//%PDDM-DEFINE DECLARE_DICTIONARIES()
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(UInt32, uint32_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(Int32, int32_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(UInt64, uint64_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(Int64, int64_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(Bool, BOOL)
+//%DICTIONARY_POD_INTERFACES_FOR_KEY(String, NSString, *, OBJECT)
+//%PDDM-DEFINE DICTIONARY_INTERFACES_FOR_POD_KEY(KEY_NAME, KEY_TYPE)
+//%DICTIONARY_POD_INTERFACES_FOR_KEY(KEY_NAME, KEY_TYPE, , POD)
+//%DICTIONARY_POD_KEY_TO_OBJECT_INTERFACE(KEY_NAME, KEY_TYPE, Object, id)
+//%PDDM-DEFINE DICTIONARY_POD_INTERFACES_FOR_KEY(KEY_NAME, KEY_TYPE, KisP, KHELPER)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, UInt32, uint32_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Int32, int32_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, UInt64, uint64_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Int64, int64_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Bool, BOOL)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Float, float)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Double, double)
+//%DICTIONARY_KEY_TO_ENUM_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Enum, int32_t)
+//%PDDM-DEFINE DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, POD)
+//%PDDM-DEFINE DICTIONARY_POD_KEY_TO_OBJECT_INTERFACE(KEY_NAME, KEY_TYPE, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, , POD, VALUE_NAME, VALUE_TYPE, OBJECT)
+//%PDDM-DEFINE VALUE_FOR_KEY_POD(KEY_TYPE, VALUE_TYPE)
+//%- (BOOL)valueForKey:(KEY_TYPE)key value:(VALUE_TYPE *)value;
+//%PDDM-DEFINE VALUE_FOR_KEY_OBJECT(KEY_TYPE, VALUE_TYPE)
+//%- (VALUE_TYPE)valueForKey:(KEY_TYPE)key;
+//%PDDM-DEFINE VALUE_FOR_KEY_Enum(KEY_TYPE, VALUE_TYPE)
+//%VALUE_FOR_KEY_POD(KEY_TYPE, VALUE_TYPE)
+//%PDDM-DEFINE ARRAY_ARG_MODIFIERPOD()
+// Nothing
+//%PDDM-DEFINE ARRAY_ARG_MODIFIEREnum()
+// Nothing
+//%PDDM-DEFINE ARRAY_ARG_MODIFIEROBJECT()
+//%GPB_UNSAFE_UNRETAINED ##
+//%PDDM-DEFINE DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@interface GPB##KEY_NAME##VALUE_NAME##Dictionary : NSObject <NSCopying>
+//%
+//%@property(nonatomic, readonly) NSUInteger count;
+//%
+//%+ (instancetype)dictionary;
+//%+ (instancetype)dictionaryWithValue:(VALUE_TYPE)value
+//%                             forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%+ (instancetype)dictionaryWithValues:(const VALUE_TYPE ARRAY_ARG_MODIFIER##VHELPER()[])values
+//%                             forKeys:(const KEY_TYPE##KisP$S##KisP ARRAY_ARG_MODIFIER##KHELPER()[])keys
+//%                               count:(NSUInteger)count;
+//%+ (instancetype)dictionaryWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary;
+//%+ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
+//%
+//%- (instancetype)initWithValues:(const VALUE_TYPE ARRAY_ARG_MODIFIER##VHELPER()[])values
+//%                       forKeys:(const KEY_TYPE##KisP$S##KisP ARRAY_ARG_MODIFIER##KHELPER()[])keys
+//%                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary;
+//%- (instancetype)initWithCapacity:(NSUInteger)numItems;
+//%
+//%DICTIONARY_IMMUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%
+//%- (void)addEntriesFromDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)otherDictionary;
+//%
+//%DICTIONARY_MUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_KEY_TO_ENUM_INTERFACE2(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, Enum)
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_INTERFACE2(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@interface GPB##KEY_NAME##VALUE_NAME##Dictionary : NSObject <NSCopying>
+//%
+//%@property(nonatomic, readonly) NSUInteger count;
+//%@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+//%
+//%+ (instancetype)dictionary;
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func;
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                        rawValue:(VALUE_TYPE)rawValue
+//%                                          forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                       rawValues:(const VALUE_TYPE ARRAY_ARG_MODIFIER##VHELPER()[])values
+//%                                         forKeys:(const KEY_TYPE##KisP$S##KisP ARRAY_ARG_MODIFIER##KHELPER()[])keys
+//%                                           count:(NSUInteger)count;
+//%+ (instancetype)dictionaryWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary;
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                        capacity:(NSUInteger)numItems;
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func;
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                 rawValues:(const VALUE_TYPE ARRAY_ARG_MODIFIER##VHELPER()[])values
+//%                                   forKeys:(const KEY_TYPE##KisP$S##KisP ARRAY_ARG_MODIFIER##KHELPER()[])keys
+//%                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary;
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                  capacity:(NSUInteger)numItems;
+//%
+//%// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+//%// is not a valid enumerator as defined by validationFunc. If the actual value is
+//%// desired, use "raw" version of the method.
+//%
+//%DICTIONARY_IMMUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%
+//%// These methods bypass the validationFunc to provide access to values that were not
+//%// known at the time the binary was compiled.
+//%
+//%- (BOOL)valueForKey:(KEY_TYPE##KisP$S##KisP)key rawValue:(VALUE_TYPE *)rawValue;
+//%
+//%- (void)enumerateKeysAndRawValuesUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE rawValue, BOOL *stop))block;
+//%
+//%- (void)addRawEntriesFromDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)otherDictionary;
+//%
+//%// If value is not a valid enumerator as defined by validationFunc, these
+//%// methods will assert in debug, and will log in release and assign the value
+//%// to the default value. Use the rawValue methods below to assign non enumerator
+//%// values.
+//%
+//%DICTIONARY_MUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_IMMUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%VALUE_FOR_KEY_##VHELPER(KEY_TYPE##KisP$S##KisP, VALUE_TYPE)
+//%
+//%- (void)enumerateKeysAndValuesUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE value, BOOL *stop))block;
+
+//%PDDM-DEFINE DICTIONARY_MUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%- (void)setValue:(VALUE_TYPE)value forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%DICTIONARY_EXTRA_MUTABLE_METHODS_##VHELPER(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+//%- (void)removeValueForKey:(KEY_TYPE##KisP$S##KisP)aKey;
+//%- (void)removeAll;
+
+//%PDDM-DEFINE DICTIONARY_EXTRA_MUTABLE_METHODS_POD(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+// Empty
+//%PDDM-DEFINE DICTIONARY_EXTRA_MUTABLE_METHODS_OBJECT(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+// Empty
+//%PDDM-DEFINE DICTIONARY_EXTRA_MUTABLE_METHODS_Enum(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+//%
+//%// This method bypass the validationFunc to provide setting of values that were not
+//%// known at the time the binary was compiled.
+//%- (void)setRawValue:(VALUE_TYPE)rawValue forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%
+//%// No validation applies to these methods.
+//%

+ 12627 - 0
objectivec/GPBDictionary.m

@@ -0,0 +1,12627 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBDictionary_PackagePrivate.h"
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+// ------------------------------ NOTE ------------------------------
+// At the moment, this is all using NSNumbers in NSDictionaries under
+// the hood, but it is all hidden so we can come back and optimize
+// with direct CFDictionary usage later.  The reason that wasn't
+// done yet is needing to support 32bit iOS builds.  Otherwise
+// it would be pretty simple to store all this data in CFDictionaries
+// directly.
+// ------------------------------------------------------------------
+
+#define kMapKeyFieldNumber 1
+#define kMapValueFieldNumber 2
+
+static BOOL DictDefault_IsValidValue(int32_t value) {
+  // Anything but the bad value marker is allowed.
+  return (value != kGPBUnrecognizedEnumeratorValue);
+}
+
+//%PDDM-DEFINE SERIALIZE_SUPPORT_2_TYPE(VALUE_NAME, VALUE_TYPE, GPBTYPE_NAME1, GPBTYPE_NAME2)
+//%GPB_INLINE size_t ComputeDict##VALUE_NAME##FieldSize(VALUE_TYPE value, uint32_t fieldNum, GPBType wireType) {
+//%  NSCAssert((wireType == GPBType##GPBTYPE_NAME1) || (wireType == GPBType##GPBTYPE_NAME2),
+//%            @"bad type: %d", wireType);
+//%  if (wireType == GPBType##GPBTYPE_NAME1) {
+//%    return GPBCompute##GPBTYPE_NAME1##Size(fieldNum, value);
+//%  } else {  // wireType == GPBType##GPBTYPE_NAME2
+//%    return GPBCompute##GPBTYPE_NAME2##Size(fieldNum, value);
+//%  }
+//%}
+//%
+//%GPB_INLINE void WriteDict##VALUE_NAME##Field(GPBCodedOutputStream *stream, VALUE_TYPE value, uint32_t fieldNum, GPBType wireType) {
+//%  NSCAssert((wireType == GPBType##GPBTYPE_NAME1) || (wireType == GPBType##GPBTYPE_NAME2),
+//%            @"bad type: %d", wireType);
+//%  if (wireType == GPBType##GPBTYPE_NAME1) {
+//%    [stream write##GPBTYPE_NAME1##:fieldNum value:value];
+//%  } else {  // wireType == GPBType##GPBTYPE_NAME2
+//%    [stream write##GPBTYPE_NAME2##:fieldNum value:value];
+//%  }
+//%}
+//%
+//%PDDM-DEFINE SERIALIZE_SUPPORT_3_TYPE(VALUE_NAME, VALUE_TYPE, GPBTYPE_NAME1, GPBTYPE_NAME2, GPBTYPE_NAME3)
+//%GPB_INLINE size_t ComputeDict##VALUE_NAME##FieldSize(VALUE_TYPE value, uint32_t fieldNum, GPBType wireType) {
+//%  NSCAssert((wireType == GPBType##GPBTYPE_NAME1) || (wireType == GPBType##GPBTYPE_NAME2) || (wireType == GPBType##GPBTYPE_NAME3),
+//%            @"bad type: %d", wireType);
+//%  if (wireType == GPBType##GPBTYPE_NAME1) {
+//%    return GPBCompute##GPBTYPE_NAME1##Size(fieldNum, value);
+//%  } else if (wireType == GPBType##GPBTYPE_NAME2) {
+//%    return GPBCompute##GPBTYPE_NAME2##Size(fieldNum, value);
+//%  } else {  // wireType == GPBType##GPBTYPE_NAME3
+//%    return GPBCompute##GPBTYPE_NAME3##Size(fieldNum, value);
+//%  }
+//%}
+//%
+//%GPB_INLINE void WriteDict##VALUE_NAME##Field(GPBCodedOutputStream *stream, VALUE_TYPE value, uint32_t fieldNum, GPBType wireType) {
+//%  NSCAssert((wireType == GPBType##GPBTYPE_NAME1) || (wireType == GPBType##GPBTYPE_NAME2) || (wireType == GPBType##GPBTYPE_NAME3),
+//%            @"bad type: %d", wireType);
+//%  if (wireType == GPBType##GPBTYPE_NAME1) {
+//%    [stream write##GPBTYPE_NAME1##:fieldNum value:value];
+//%  } else if (wireType == GPBType##GPBTYPE_NAME2) {
+//%    [stream write##GPBTYPE_NAME2##:fieldNum value:value];
+//%  } else {  // wireType == GPBType##GPBTYPE_NAME3
+//%    [stream write##GPBTYPE_NAME3##:fieldNum value:value];
+//%  }
+//%}
+//%
+//%PDDM-DEFINE SIMPLE_SERIALIZE_SUPPORT(VALUE_NAME, VALUE_TYPE, VisP)
+//%GPB_INLINE size_t ComputeDict##VALUE_NAME##FieldSize(VALUE_TYPE VisP##value, uint32_t fieldNum, GPBType wireType) {
+//%  NSCAssert(wireType == GPBType##VALUE_NAME, @"bad type: %d", wireType);
+//%  return GPBCompute##VALUE_NAME##Size(fieldNum, value);
+//%}
+//%
+//%GPB_INLINE void WriteDict##VALUE_NAME##Field(GPBCodedOutputStream *stream, VALUE_TYPE VisP##value, uint32_t fieldNum, GPBType wireType) {
+//%  NSCAssert(wireType == GPBType##VALUE_NAME, @"bad type: %d", wireType);
+//%  [stream write##VALUE_NAME##:fieldNum value:value];
+//%}
+//%
+//%PDDM-DEFINE SERIALIZE_SUPPORT_HELPERS()
+//%SERIALIZE_SUPPORT_3_TYPE(Int32, int32_t, Int32, SInt32, SFixed32)
+//%SERIALIZE_SUPPORT_2_TYPE(UInt32, uint32_t, UInt32, Fixed32)
+//%SERIALIZE_SUPPORT_3_TYPE(Int64, int64_t, Int64, SInt64, SFixed64)
+//%SERIALIZE_SUPPORT_2_TYPE(UInt64, uint64_t, UInt64, Fixed64)
+//%SIMPLE_SERIALIZE_SUPPORT(Bool, BOOL, )
+//%SIMPLE_SERIALIZE_SUPPORT(Enum, int32_t, )
+//%SIMPLE_SERIALIZE_SUPPORT(Float, float, )
+//%SIMPLE_SERIALIZE_SUPPORT(Double, double, )
+//%SIMPLE_SERIALIZE_SUPPORT(String, NSString, *)
+//%SERIALIZE_SUPPORT_3_TYPE(Object, id, Message, String, Data)
+//%PDDM-EXPAND SERIALIZE_SUPPORT_HELPERS()
+// This block of code is generated, do not edit it directly.
+
+GPB_INLINE size_t ComputeDictInt32FieldSize(int32_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeInt32) || (wireType == GPBTypeSInt32) || (wireType == GPBTypeSFixed32),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeInt32) {
+    return GPBComputeInt32Size(fieldNum, value);
+  } else if (wireType == GPBTypeSInt32) {
+    return GPBComputeSInt32Size(fieldNum, value);
+  } else {  // wireType == GPBTypeSFixed32
+    return GPBComputeSFixed32Size(fieldNum, value);
+  }
+}
+
+GPB_INLINE void WriteDictInt32Field(GPBCodedOutputStream *stream, int32_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeInt32) || (wireType == GPBTypeSInt32) || (wireType == GPBTypeSFixed32),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeInt32) {
+    [stream writeInt32:fieldNum value:value];
+  } else if (wireType == GPBTypeSInt32) {
+    [stream writeSInt32:fieldNum value:value];
+  } else {  // wireType == GPBTypeSFixed32
+    [stream writeSFixed32:fieldNum value:value];
+  }
+}
+
+GPB_INLINE size_t ComputeDictUInt32FieldSize(uint32_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeUInt32) || (wireType == GPBTypeFixed32),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeUInt32) {
+    return GPBComputeUInt32Size(fieldNum, value);
+  } else {  // wireType == GPBTypeFixed32
+    return GPBComputeFixed32Size(fieldNum, value);
+  }
+}
+
+GPB_INLINE void WriteDictUInt32Field(GPBCodedOutputStream *stream, uint32_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeUInt32) || (wireType == GPBTypeFixed32),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeUInt32) {
+    [stream writeUInt32:fieldNum value:value];
+  } else {  // wireType == GPBTypeFixed32
+    [stream writeFixed32:fieldNum value:value];
+  }
+}
+
+GPB_INLINE size_t ComputeDictInt64FieldSize(int64_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeInt64) || (wireType == GPBTypeSInt64) || (wireType == GPBTypeSFixed64),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeInt64) {
+    return GPBComputeInt64Size(fieldNum, value);
+  } else if (wireType == GPBTypeSInt64) {
+    return GPBComputeSInt64Size(fieldNum, value);
+  } else {  // wireType == GPBTypeSFixed64
+    return GPBComputeSFixed64Size(fieldNum, value);
+  }
+}
+
+GPB_INLINE void WriteDictInt64Field(GPBCodedOutputStream *stream, int64_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeInt64) || (wireType == GPBTypeSInt64) || (wireType == GPBTypeSFixed64),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeInt64) {
+    [stream writeInt64:fieldNum value:value];
+  } else if (wireType == GPBTypeSInt64) {
+    [stream writeSInt64:fieldNum value:value];
+  } else {  // wireType == GPBTypeSFixed64
+    [stream writeSFixed64:fieldNum value:value];
+  }
+}
+
+GPB_INLINE size_t ComputeDictUInt64FieldSize(uint64_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeUInt64) || (wireType == GPBTypeFixed64),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeUInt64) {
+    return GPBComputeUInt64Size(fieldNum, value);
+  } else {  // wireType == GPBTypeFixed64
+    return GPBComputeFixed64Size(fieldNum, value);
+  }
+}
+
+GPB_INLINE void WriteDictUInt64Field(GPBCodedOutputStream *stream, uint64_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeUInt64) || (wireType == GPBTypeFixed64),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeUInt64) {
+    [stream writeUInt64:fieldNum value:value];
+  } else {  // wireType == GPBTypeFixed64
+    [stream writeFixed64:fieldNum value:value];
+  }
+}
+
+GPB_INLINE size_t ComputeDictBoolFieldSize(BOOL value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeBool, @"bad type: %d", wireType);
+  return GPBComputeBoolSize(fieldNum, value);
+}
+
+GPB_INLINE void WriteDictBoolField(GPBCodedOutputStream *stream, BOOL value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeBool, @"bad type: %d", wireType);
+  [stream writeBool:fieldNum value:value];
+}
+
+GPB_INLINE size_t ComputeDictEnumFieldSize(int32_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeEnum, @"bad type: %d", wireType);
+  return GPBComputeEnumSize(fieldNum, value);
+}
+
+GPB_INLINE void WriteDictEnumField(GPBCodedOutputStream *stream, int32_t value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeEnum, @"bad type: %d", wireType);
+  [stream writeEnum:fieldNum value:value];
+}
+
+GPB_INLINE size_t ComputeDictFloatFieldSize(float value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeFloat, @"bad type: %d", wireType);
+  return GPBComputeFloatSize(fieldNum, value);
+}
+
+GPB_INLINE void WriteDictFloatField(GPBCodedOutputStream *stream, float value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeFloat, @"bad type: %d", wireType);
+  [stream writeFloat:fieldNum value:value];
+}
+
+GPB_INLINE size_t ComputeDictDoubleFieldSize(double value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeDouble, @"bad type: %d", wireType);
+  return GPBComputeDoubleSize(fieldNum, value);
+}
+
+GPB_INLINE void WriteDictDoubleField(GPBCodedOutputStream *stream, double value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeDouble, @"bad type: %d", wireType);
+  [stream writeDouble:fieldNum value:value];
+}
+
+GPB_INLINE size_t ComputeDictStringFieldSize(NSString *value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeString, @"bad type: %d", wireType);
+  return GPBComputeStringSize(fieldNum, value);
+}
+
+GPB_INLINE void WriteDictStringField(GPBCodedOutputStream *stream, NSString *value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert(wireType == GPBTypeString, @"bad type: %d", wireType);
+  [stream writeString:fieldNum value:value];
+}
+
+GPB_INLINE size_t ComputeDictObjectFieldSize(id value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeMessage) || (wireType == GPBTypeString) || (wireType == GPBTypeData),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeMessage) {
+    return GPBComputeMessageSize(fieldNum, value);
+  } else if (wireType == GPBTypeString) {
+    return GPBComputeStringSize(fieldNum, value);
+  } else {  // wireType == GPBTypeData
+    return GPBComputeDataSize(fieldNum, value);
+  }
+}
+
+GPB_INLINE void WriteDictObjectField(GPBCodedOutputStream *stream, id value, uint32_t fieldNum, GPBType wireType) {
+  NSCAssert((wireType == GPBTypeMessage) || (wireType == GPBTypeString) || (wireType == GPBTypeData),
+            @"bad type: %d", wireType);
+  if (wireType == GPBTypeMessage) {
+    [stream writeMessage:fieldNum value:value];
+  } else if (wireType == GPBTypeString) {
+    [stream writeString:fieldNum value:value];
+  } else {  // wireType == GPBTypeData
+    [stream writeData:fieldNum value:value];
+  }
+}
+
+//%PDDM-EXPAND-END SERIALIZE_SUPPORT_HELPERS()
+
+size_t GPBDictionaryComputeSizeInternalHelper(NSDictionary *dict, GPBFieldDescriptor *field) {
+  NSCAssert(field.mapKeyType == GPBTypeString, @"Unexpected key type");
+  GPBType mapValueType = GPBGetFieldType(field);
+  __block size_t result = 0;
+  [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = GPBComputeStringSize(kMapKeyFieldNumber, key);
+    msgSize += ComputeDictObjectFieldSize(obj, kMapValueFieldNumber, mapValueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * dict.count;
+  return result;
+}
+
+void GPBDictionaryWriteToStreamInternalHelper(GPBCodedOutputStream *outputStream,
+                                              NSDictionary *dict,
+                                              GPBFieldDescriptor *field) {
+  NSCAssert(field.mapKeyType == GPBTypeString, @"Unexpected key type");
+  GPBType mapValueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = GPBComputeStringSize(kMapKeyFieldNumber, key);
+    msgSize += ComputeDictObjectFieldSize(obj, kMapValueFieldNumber, mapValueType);
+
+    // Write the size and fields.
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    [outputStream writeString:kMapValueFieldNumber value:obj];
+    WriteDictObjectField(outputStream, obj, kMapValueFieldNumber, mapValueType);
+  }];
+}
+
+BOOL GPBDictionaryIsInitializedInternalHelper(NSDictionary *dict, GPBFieldDescriptor *field) {
+  NSCAssert(field.mapKeyType == GPBTypeString, @"Unexpected key type");
+  NSCAssert(GPBGetFieldType(field) == GPBTypeMessage, @"Unexpected value type");
+  for (GPBMessage *msg in [dict objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+// Note: if the type is an object, it the retain pass back to the caller.
+static void ReadValue(GPBCodedInputStream *stream,
+                      GPBValue *valueToFill,
+                      GPBType type,
+                      GPBExtensionRegistry *registry,
+                      GPBFieldDescriptor *field) {
+  switch (type) {
+    case GPBTypeBool:
+      valueToFill->valueBool = GPBCodedInputStreamReadBool(&stream->state_);
+      break;
+    case GPBTypeFixed32:
+      valueToFill->valueUInt32 = GPBCodedInputStreamReadFixed32(&stream->state_);
+      break;
+    case GPBTypeSFixed32:
+      valueToFill->valueInt32 = GPBCodedInputStreamReadSFixed32(&stream->state_);
+      break;
+    case GPBTypeFloat:
+      valueToFill->valueFloat = GPBCodedInputStreamReadFloat(&stream->state_);
+      break;
+    case GPBTypeFixed64:
+      valueToFill->valueUInt64 = GPBCodedInputStreamReadFixed64(&stream->state_);
+      break;
+    case GPBTypeSFixed64:
+      valueToFill->valueInt64 = GPBCodedInputStreamReadSFixed64(&stream->state_);
+      break;
+    case GPBTypeDouble:
+      valueToFill->valueDouble = GPBCodedInputStreamReadDouble(&stream->state_);
+      break;
+    case GPBTypeInt32:
+      valueToFill->valueInt32 = GPBCodedInputStreamReadInt32(&stream->state_);
+      break;
+    case GPBTypeInt64:
+      valueToFill->valueInt64 = GPBCodedInputStreamReadInt32(&stream->state_);
+      break;
+    case GPBTypeSInt32:
+      valueToFill->valueInt32 = GPBCodedInputStreamReadSInt32(&stream->state_);
+      break;
+    case GPBTypeSInt64:
+      valueToFill->valueInt64 = GPBCodedInputStreamReadSInt64(&stream->state_);
+      break;
+    case GPBTypeUInt32:
+      valueToFill->valueUInt32 = GPBCodedInputStreamReadUInt32(&stream->state_);
+      break;
+    case GPBTypeUInt64:
+      valueToFill->valueUInt64 = GPBCodedInputStreamReadUInt64(&stream->state_);
+      break;
+    case GPBTypeData:
+      [valueToFill->valueData release];
+      valueToFill->valueData = GPBCodedInputStreamReadRetainedData(&stream->state_);
+      break;
+    case GPBTypeString:
+      [valueToFill->valueString release];
+      valueToFill->valueString = GPBCodedInputStreamReadRetainedString(&stream->state_);
+      break;
+    case GPBTypeMessage: {
+      GPBMessage *message = [[field.msgClass alloc] init];
+      [stream readMessage:message extensionRegistry:registry];
+      [valueToFill->valueMessage release];
+      valueToFill->valueMessage = message;
+      break;
+    }
+    case GPBTypeGroup:
+      NSCAssert(NO, @"Can't happen");
+      break;
+    case GPBTypeEnum:
+      valueToFill->valueEnum = GPBCodedInputStreamReadEnum(&stream->state_);
+      break;
+  }
+}
+
+void GPBDictionaryReadEntry(id mapDictionary,
+                            GPBCodedInputStream *stream,
+                            GPBExtensionRegistry *registry,
+                            GPBFieldDescriptor *field,
+                            GPBMessage *parentMessage) {
+  GPBType keyType = field.mapKeyType;
+  GPBType valueType = GPBGetFieldType(field);
+
+  GPBValue key;
+  GPBValue value;
+  // Zero them (but pick up any enum default for proto2).
+  key.valueString = value.valueString = nil;
+  if (valueType == GPBTypeEnum) {
+    value = field.defaultValue;
+  }
+
+  GPBCodedInputStreamState *state = &stream->state_;
+  uint32_t keyTag =
+      GPBWireFormatMakeTag(kMapKeyFieldNumber, GPBWireFormatForType(keyType, NO));
+  uint32_t valueTag =
+      GPBWireFormatMakeTag(kMapValueFieldNumber, GPBWireFormatForType(valueType, NO));
+
+  BOOL hitError = NO;
+  while (YES) {
+    uint32_t tag = GPBCodedInputStreamReadTag(state);
+    if (tag == keyTag) {
+      ReadValue(stream, &key, keyType, registry, field);
+    } else if (tag == valueTag) {
+      ReadValue(stream, &value, valueType, registry, field);
+    } else if (tag == 0) {
+      // zero signals EOF / limit reached
+      break;
+    } else {  // Unknown
+      if (![stream skipField:tag]){
+        hitError = YES;
+        break;
+      }
+    }
+  }
+
+  if (!hitError) {
+    // Handle the special defaults and/or missing key/value.
+    if ((keyType == GPBTypeString) && (key.valueString == nil)) {
+      key.valueString = [@"" retain];
+    }
+    if (GPBTypeIsObject(valueType) && value.valueString == nil) {
+      switch (valueType) {
+        case GPBTypeString:
+          value.valueString = [@"" retain];
+          break;
+        case GPBTypeData:
+          value.valueData = [GPBEmptyNSData() retain];
+          break;
+        case GPBTypeMessage: {
+          value.valueMessage = [[field.msgClass alloc] init];
+          break;
+        }
+        default:
+          // Nothing
+          break;
+      }
+    }
+
+    if ((keyType == GPBTypeString) && GPBTypeIsObject(valueType)) {
+      // mapDictionary is an NSMutableDictionary
+      [mapDictionary setObject:value.valueString forKey:key.valueString];
+    } else {
+      if (valueType == GPBTypeEnum) {
+        if (GPBHasPreservingUnknownEnumSemantics([parentMessage descriptor].file.syntax) ||
+            [field isValidEnumValue:value.valueEnum]) {
+          [mapDictionary setGPBValue:&value forGPBValueKey:&key];
+        } else {
+          NSData *data = [mapDictionary serializedDataForUnknownValue:value.valueEnum
+                                                               forKey:&key
+                                                              keyType:keyType];
+          [parentMessage addUnknownMapEntry:GPBFieldNumber(field) value:data];
+        }
+      } else {
+        [mapDictionary setGPBValue:&value forGPBValueKey:&key];
+      }
+    }
+  }
+
+  if (GPBTypeIsObject(keyType)) [key.valueString release];
+  if (GPBTypeIsObject(valueType)) [value.valueString release];
+}
+
+//
+// Macros for the common basic cases.
+//
+
+//%PDDM-DEFINE DICTIONARY_IMPL_FOR_POD_KEY(KEY_NAME, KEY_TYPE)
+//%DICTIONARY_POD_IMPL_FOR_KEY(KEY_NAME, KEY_TYPE, , POD)
+//%DICTIONARY_POD_KEY_TO_OBJECT_IMPL(KEY_NAME, KEY_TYPE, Object, id)
+
+//%PDDM-DEFINE DICTIONARY_POD_IMPL_FOR_KEY(KEY_NAME, KEY_TYPE, KisP, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, UInt32, uint32_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Int32, int32_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, UInt64, uint64_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Int64, int64_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Bool, BOOL, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Float, float, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Double, double, KHELPER)
+//%DICTIONARY_KEY_TO_ENUM_IMPL(KEY_NAME, KEY_TYPE, KisP, Enum, int32_t, KHELPER)
+
+//%PDDM-DEFINE DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%DICTIONARY_COMMON_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, POD)
+
+//%PDDM-DEFINE DICTIONARY_POD_KEY_TO_OBJECT_IMPL(KEY_NAME, KEY_TYPE, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_COMMON_IMPL(KEY_NAME, KEY_TYPE, , VALUE_NAME, VALUE_TYPE, POD, OBJECT)
+
+//%PDDM-DEFINE DICTIONARY_COMMON_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##Dictionary {
+//% @package
+//%  NSMutableDictionary *_dictionary;
+//%}
+//%
+//%+ (instancetype)dictionary {
+//%  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValue:(VALUE_TYPE)value
+//%                             forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPB##KEY_NAME##VALUE_NAME##Dictionary*)[self alloc] initWithValues:&value
+//%               KEY_NAME$S VALUE_NAME$S                               forKeys:&key
+//%               KEY_NAME$S VALUE_NAME$S                                 count:1] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValues:(const VALUE_TYPE [])values
+//%                             forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                               count:(NSUInteger)count {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPB##KEY_NAME##VALUE_NAME##Dictionary*)[self alloc] initWithValues:values
+//%               KEY_NAME$S VALUE_NAME$S                               forKeys:keys
+//%               KEY_NAME$S VALUE_NAME$S                                 count:count] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPB##KEY_NAME##VALUE_NAME##Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+//%  return [[[self alloc] initWithCapacity:numItems] autorelease];
+//%}
+//%
+//%- (instancetype)init {
+//%  return [self initWithValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWithValues:(const VALUE_TYPE [])values
+//%                       forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                         count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    _dictionary = [[NSMutableDictionary alloc] init];
+//%    if (count && values && keys) {
+//%      for (NSUInteger i = 0; i < count; ++i) {
+//%        [_dictionary setObject:WRAPPED##VHELPER(values[i]) forKey:WRAPPED##KHELPER(keys[i])];
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWithValues:NULL forKeys:NULL count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithCapacity:(NSUInteger)numItems {
+//%  #pragma unused(numItems)
+//%  return [self initWithValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%DICTIONARY_IMMUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, )
+//%
+//%VALUE_FOR_KEY_##VHELPER(KEY_TYPE##KisP$S##KisP, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%
+//%DICTIONARY_MUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, )
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%DICTIONARY_KEY_TO_ENUM_IMPL2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, POD)
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_IMPL2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##Dictionary {
+//% @package
+//%  NSMutableDictionary *_dictionary;
+//%  GPBEnumValidationFunc _validationFunc;
+//%}
+//%
+//%@synthesize validationFunc = _validationFunc;
+//%
+//%+ (instancetype)dictionary {
+//%  return [[[self alloc] initWithValidationFunction:NULL
+//%                                         rawValues:NULL
+//%                                           forKeys:NULL
+//%                                             count:0] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+//%  return [[[self alloc] initWithValidationFunction:func
+//%                                         rawValues:NULL
+//%                                           forKeys:NULL
+//%                                             count:0] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                        rawValue:(VALUE_TYPE)rawValue
+//%                                          forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPB##KEY_NAME##VALUE_NAME##Dictionary*)[self alloc] initWithValidationFunction:func
+//%               KEY_NAME$S VALUE_NAME$S                                         rawValues:&rawValue
+//%               KEY_NAME$S VALUE_NAME$S                                           forKeys:&key
+//%               KEY_NAME$S VALUE_NAME$S                                             count:1] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                       rawValues:(const VALUE_TYPE [])rawValues
+//%                                         forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                                           count:(NSUInteger)count {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPB##KEY_NAME##VALUE_NAME##Dictionary*)[self alloc] initWithValidationFunction:func
+//%               KEY_NAME$S VALUE_NAME$S                                         rawValues:rawValues
+//%               KEY_NAME$S VALUE_NAME$S                                           forKeys:keys
+//%               KEY_NAME$S VALUE_NAME$S                                             count:count] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPB##KEY_NAME##VALUE_NAME##Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                        capacity:(NSUInteger)numItems {
+//%  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+//%}
+//%
+//%- (instancetype)init {
+//%  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+//%  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                 rawValues:(const VALUE_TYPE [])rawValues
+//%                                   forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                                     count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    _dictionary = [[NSMutableDictionary alloc] init];
+//%    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+//%    if (count && rawValues && keys) {
+//%      for (NSUInteger i = 0; i < count; ++i) {
+//%        [_dictionary setObject:WRAPPED##VHELPER(rawValues[i]) forKey:WRAPPED##KHELPER(keys[i])];
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWithValidationFunction:dictionary.validationFunc
+//%                                rawValues:NULL
+//%                                  forKeys:NULL
+//%                                    count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                  capacity:(NSUInteger)numItems {
+//%  #pragma unused(numItems)
+//%  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%DICTIONARY_IMMUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, Raw)
+//%
+//%- (BOOL)valueForKey:(KEY_TYPE##KisP$S##KisP)key value:(VALUE_TYPE *)value {
+//%  NSNumber *wrapped = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  if (wrapped && value) {
+//%    VALUE_TYPE result = UNWRAP##VALUE_NAME(wrapped);
+//%    if (!_validationFunc(result)) {
+//%      result = kGPBUnrecognizedEnumeratorValue;
+//%    }
+//%    *value = result;
+//%  }
+//%  return (wrapped != NULL);
+//%}
+//%
+//%- (BOOL)valueForKey:(KEY_TYPE##KisP$S##KisP)key rawValue:(VALUE_TYPE *)rawValue {
+//%  NSNumber *wrapped = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  if (wrapped && rawValue) {
+//%    *rawValue = UNWRAP##VALUE_NAME(wrapped);
+//%  }
+//%  return (wrapped != NULL);
+//%}
+//%
+//%- (void)enumerateKeysAndValuesUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE value, BOOL *stop))block {
+//%  GPBEnumValidationFunc func = _validationFunc;
+//%  [_dictionary enumerateKeysAndObjectsUsingBlock:^(ENUM_TYPE##KHELPER(KEY_TYPE)##aKey,
+//%                                                   ENUM_TYPE##VHELPER(VALUE_TYPE)##aValue,
+//%                                                   BOOL *stop) {
+//%      VALUE_TYPE unwrapped = UNWRAP##VALUE_NAME(aValue);
+//%      if (!func(unwrapped)) {
+//%        unwrapped = kGPBUnrecognizedEnumeratorValue;
+//%      }
+//%      block(UNWRAP##KEY_NAME(aKey), unwrapped, stop);
+//%  }];
+//%}
+//%
+//%DICTIONARY_MUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, Raw)
+//%
+//%- (void)setValue:(VALUE_TYPE)value forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  if (!_validationFunc(value)) {
+//%    [NSException raise:NSInvalidArgumentException
+//%                format:@"GPB##KEY_NAME##VALUE_NAME##Dictionary: Attempt to set an unknown enum value (%d)",
+//%                       value];
+//%  }
+//%
+//%  [_dictionary setObject:WRAPPED##VHELPER(value) forKey:WRAPPED##KHELPER(key)];
+//%}
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_IMMUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, ACCESSOR_NAME)
+//%- (void)dealloc {
+//%  [_dictionary release];
+//%  [super dealloc];
+//%}
+//%
+//%- (instancetype)copyWithZone:(NSZone *)zone {
+//%  return [[GPB##KEY_NAME##VALUE_NAME##Dictionary allocWithZone:zone] initWithDictionary:self];
+//%}
+//%
+//%- (BOOL)isEqual:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)other {
+//%  if (self == other) {
+//%    return YES;
+//%  }
+//%  if (![other isKindOfClass:[GPB##KEY_NAME##VALUE_NAME##Dictionary class]]) {
+//%    return NO;
+//%  }
+//%  return [_dictionary isEqual:other->_dictionary];
+//%}
+//%
+//%- (NSUInteger)hash {
+//%  return _dictionary.count;
+//%}
+//%
+//%- (NSString *)description {
+//%  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+//%}
+//%
+//%- (NSUInteger)count {
+//%  return _dictionary.count;
+//%}
+//%
+//%- (void)enumerateKeysAnd##ACCESSOR_NAME##ValuesUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE value, BOOL *stop))block {
+//%  [_dictionary enumerateKeysAndObjectsUsingBlock:^(ENUM_TYPE##KHELPER(KEY_TYPE)##aKey,
+//%                                                   ENUM_TYPE##VHELPER(VALUE_TYPE)##aValue,
+//%                                                   BOOL *stop) {
+//%      block(UNWRAP##KEY_NAME(aKey), UNWRAP##VALUE_NAME(aValue), stop);
+//%  }];
+//%}
+//%
+//%EXTRA_METHODS_##VHELPER(KEY_NAME, VALUE_NAME)- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+//%  NSUInteger count = _dictionary.count;
+//%  if (count == 0) {
+//%    return 0;
+//%  }
+//%
+//%  GPBType valueType = GPBGetFieldType(field);
+//%  GPBType keyType = field.mapKeyType;
+//%  __block size_t result = 0;
+//%  [_dictionary enumerateKeysAndObjectsUsingBlock:^(ENUM_TYPE##KHELPER(KEY_TYPE)##aKey,
+//%                                                   ENUM_TYPE##VHELPER(VALUE_TYPE)##aValue,
+//%                                                   BOOL *stop) {
+//%    #pragma unused(stop)
+//%    size_t msgSize = ComputeDict##KEY_NAME##FieldSize(UNWRAP##KEY_NAME(aKey), kMapKeyFieldNumber, keyType);
+//%    msgSize += ComputeDict##VALUE_NAME##FieldSize(UNWRAP##VALUE_NAME(aValue), kMapValueFieldNumber, valueType);
+//%    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+//%  }];
+//%  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+//%  result += tagSize * count;
+//%  return result;
+//%}
+//%
+//%- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+//%                         asField:(GPBFieldDescriptor *)field {
+//%  GPBType valueType = GPBGetFieldType(field);
+//%  GPBType keyType = field.mapKeyType;
+//%  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+//%  [_dictionary enumerateKeysAndObjectsUsingBlock:^(ENUM_TYPE##KHELPER(KEY_TYPE)##aKey,
+//%                                                   ENUM_TYPE##VHELPER(VALUE_TYPE)##aValue,
+//%                                                   BOOL *stop) {
+//%    #pragma unused(stop)
+//%    // Write the tag.
+//%    [outputStream writeInt32NoTag:tag];
+//%    // Write the size of the message.
+//%    size_t msgSize = ComputeDict##KEY_NAME##FieldSize(UNWRAP##KEY_NAME(aKey), kMapKeyFieldNumber, keyType);
+//%    msgSize += ComputeDict##VALUE_NAME##FieldSize(UNWRAP##VALUE_NAME(aValue), kMapValueFieldNumber, valueType);
+//%    [outputStream writeInt32NoTag:(int32_t)msgSize];
+//%    // Write the fields.
+//%    WriteDict##KEY_NAME##Field(outputStream, UNWRAP##KEY_NAME(aKey), kMapKeyFieldNumber, keyType);
+//%    WriteDict##VALUE_NAME##Field(outputStream, UNWRAP##VALUE_NAME(aValue), kMapValueFieldNumber, valueType);
+//%  }];
+//%}
+//%
+//%SERIAL_DATA_FOR_ENTRY_##VHELPER(KEY_NAME, VALUE_NAME)- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+//%  [_dictionary setObject:WRAPPED##VHELPER(value->##GPBVALUE_##VHELPER(VALUE_NAME)##) forKey:WRAPPED##KHELPER(key->value##KEY_NAME)];
+//%}
+//%
+//%- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+//%  [self enumerateKeysAnd##ACCESSOR_NAME##ValuesUsingBlock:^(KEY_TYPE KisP##key, VALUE_TYPE value, BOOL *stop) {
+//%      #pragma unused(stop)
+//%      block(TEXT_FORMAT_OBJ##KEY_NAME(key), TEXT_FORMAT_OBJ##VALUE_NAME(value));
+//%  }];
+//%}
+//%PDDM-DEFINE DICTIONARY_MUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, ACCESSOR_NAME)
+//%- (void)add##ACCESSOR_NAME##EntriesFromDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)otherDictionary {
+//%  if (otherDictionary) {
+//%    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+//%  }
+//%}
+//%
+//%- (void)set##ACCESSOR_NAME##Value:(VALUE_TYPE)value forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  [_dictionary setObject:WRAPPED##VHELPER(value) forKey:WRAPPED##KHELPER(key)];
+//%}
+//%
+//%- (void)removeValueForKey:(KEY_TYPE##KisP$S##KisP)aKey {
+//%  [_dictionary removeObjectForKey:WRAPPED##KHELPER(aKey)];
+//%}
+//%
+//%- (void)removeAll {
+//%  [_dictionary removeAllObjects];
+//%}
+
+//
+// Custom Generation for Bool keys
+//
+
+//%PDDM-DEFINE DICTIONARY_BOOL_KEY_TO_POD_IMPL(VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_BOOL_KEY_TO_VALUE_IMPL(VALUE_NAME, VALUE_TYPE, POD)
+//%PDDM-DEFINE DICTIONARY_BOOL_KEY_TO_OBJECT_IMPL(VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_BOOL_KEY_TO_VALUE_IMPL(VALUE_NAME, VALUE_TYPE, OBJECT)
+
+//%PDDM-DEFINE DICTIONARY_BOOL_KEY_TO_VALUE_IMPL(VALUE_NAME, VALUE_TYPE, HELPER)
+//%#pragma mark - Bool -> VALUE_NAME
+//%
+//%@implementation GPBBool##VALUE_NAME##Dictionary {
+//% @package
+//%  VALUE_TYPE _values[2];
+//%BOOL_DICT_HAS_STORAGE_##HELPER()}
+//%
+//%+ (instancetype)dictionary {
+//%  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValue:(VALUE_TYPE)value
+//%                             forKey:(BOOL)key {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPBBool##VALUE_NAME##Dictionary*)[self alloc] initWithValues:&value
+//%                    VALUE_NAME$S                               forKeys:&key
+//%                    VALUE_NAME$S                                 count:1] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithValues:(const VALUE_TYPE [])values
+//%                             forKeys:(const BOOL [])keys
+//%                               count:(NSUInteger)count {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPBBool##VALUE_NAME##Dictionary*)[self alloc] initWithValues:values
+//%                    VALUE_NAME$S                               forKeys:keys
+//%                    VALUE_NAME$S                                 count:count] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithDictionary:(GPBBool##VALUE_NAME##Dictionary *)dictionary {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+//%  // on to get the type correct.
+//%  return [[(GPBBool##VALUE_NAME##Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+//%}
+//%
+//%+ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+//%  return [[[self alloc] initWithCapacity:numItems] autorelease];
+//%}
+//%
+//%- (instancetype)init {
+//%  return [self initWithValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%BOOL_DICT_INITS_##HELPER(VALUE_NAME, VALUE_TYPE)
+//%
+//%- (instancetype)initWithCapacity:(NSUInteger)numItems {
+//%  #pragma unused(numItems)
+//%  return [self initWithValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%BOOL_DICT_DEALLOC##HELPER()- (instancetype)copyWithZone:(NSZone *)zone {
+//%  return [[GPBBool##VALUE_NAME##Dictionary allocWithZone:zone] initWithDictionary:self];
+//%}
+//%
+//%- (BOOL)isEqual:(GPBBool##VALUE_NAME##Dictionary *)other {
+//%  if (self == other) {
+//%    return YES;
+//%  }
+//%  if (![other isKindOfClass:[GPBBool##VALUE_NAME##Dictionary class]]) {
+//%    return NO;
+//%  }
+//%  if ((BOOL_DICT_W_HAS##HELPER(0, ) != BOOL_DICT_W_HAS##HELPER(0, other->)) ||
+//%      (BOOL_DICT_W_HAS##HELPER(1, ) != BOOL_DICT_W_HAS##HELPER(1, other->))) {
+//%    return NO;
+//%  }
+//%  if ((BOOL_DICT_W_HAS##HELPER(0, ) && (NEQ_##HELPER(_values[0], other->_values[0]))) ||
+//%      (BOOL_DICT_W_HAS##HELPER(1, ) && (NEQ_##HELPER(_values[1], other->_values[1])))) {
+//%    return NO;
+//%  }
+//%  return YES;
+//%}
+//%
+//%- (NSUInteger)hash {
+//%  return (BOOL_DICT_W_HAS##HELPER(0, ) ? 1 : 0) + (BOOL_DICT_W_HAS##HELPER(1, ) ? 1 : 0);
+//%}
+//%
+//%- (NSString *)description {
+//%  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+//%  if (BOOL_DICT_W_HAS##HELPER(0, )) {
+//%    [result appendFormat:@"NO: STR_FORMAT_##HELPER(VALUE_NAME)", _values[0]];
+//%  }
+//%  if (BOOL_DICT_W_HAS##HELPER(1, )) {
+//%    [result appendFormat:@"YES: STR_FORMAT_##HELPER(VALUE_NAME)", _values[1]];
+//%  }
+//%  [result appendString:@" }"];
+//%  return result;
+//%}
+//%
+//%- (NSUInteger)count {
+//%  return (BOOL_DICT_W_HAS##HELPER(0, ) ? 1 : 0) + (BOOL_DICT_W_HAS##HELPER(1, ) ? 1 : 0);
+//%}
+//%
+//%BOOL_VALUE_FOR_KEY_##HELPER(VALUE_TYPE)
+//%
+//%BOOL_SET_GPBVALUE_FOR_KEY_##HELPER(VALUE_NAME, VALUE_TYPE, VisP)
+//%
+//%- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+//%  if (BOOL_DICT_HAS##HELPER(0, )) {
+//%    block(@"false", TEXT_FORMAT_OBJ##VALUE_NAME(_values[0]));
+//%  }
+//%  if (BOOL_DICT_W_HAS##HELPER(1, )) {
+//%    block(@"true", TEXT_FORMAT_OBJ##VALUE_NAME(_values[1]));
+//%  }
+//%}
+//%
+//%- (void)enumerateKeysAndValuesUsingBlock:
+//%    (void (^)(BOOL key, VALUE_TYPE value, BOOL *stop))block {
+//%  BOOL stop = NO;
+//%  if (BOOL_DICT_HAS##HELPER(0, )) {
+//%    block(NO, _values[0], &stop);
+//%  }
+//%  if (!stop && BOOL_DICT_W_HAS##HELPER(1, )) {
+//%    block(YES, _values[1], &stop);
+//%  }
+//%}
+//%
+//%BOOL_EXTRA_METHODS_##HELPER(Bool, VALUE_NAME)- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+//%  GPBType valueType = GPBGetFieldType(field);
+//%  NSUInteger count = 0;
+//%  size_t result = 0;
+//%  for (int i = 0; i < 2; ++i) {
+//%    if (BOOL_DICT_HAS##HELPER(i, )) {
+//%      ++count;
+//%      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+//%      msgSize += ComputeDict##VALUE_NAME##FieldSize(_values[i], kMapValueFieldNumber, valueType);
+//%      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+//%    }
+//%  }
+//%  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+//%  result += tagSize * count;
+//%  return result;
+//%}
+//%
+//%- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+//%                         asField:(GPBFieldDescriptor *)field {
+//%  GPBType valueType = GPBGetFieldType(field);
+//%  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+//%  for (int i = 0; i < 2; ++i) {
+//%    if (BOOL_DICT_HAS##HELPER(i, )) {
+//%      // Write the tag.
+//%      [outputStream writeInt32NoTag:tag];
+//%      // Write the size of the message.
+//%      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+//%      msgSize += ComputeDict##VALUE_NAME##FieldSize(_values[i], kMapValueFieldNumber, valueType);
+//%      [outputStream writeInt32NoTag:(int32_t)msgSize];
+//%      // Write the fields.
+//%      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+//%      WriteDict##VALUE_NAME##Field(outputStream, _values[i], kMapValueFieldNumber, valueType);
+//%    }
+//%  }
+//%}
+//%
+//%BOOL_DICT_MUTATIONS_##HELPER(VALUE_NAME, VALUE_TYPE)
+//%
+//%@end
+//%
+
+
+//
+// Helpers for PODs
+//
+
+//%PDDM-DEFINE VALUE_FOR_KEY_POD(KEY_TYPE, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%- (BOOL)valueForKey:(KEY_TYPE)key value:(VALUE_TYPE *)value {
+//%  NSNumber *wrapped = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  if (wrapped && value) {
+//%    *value = UNWRAP##VALUE_NAME(wrapped);
+//%  }
+//%  return (wrapped != NULL);
+//%}
+//%PDDM-DEFINE WRAPPEDPOD(VALUE)
+//%@(VALUE)
+//%PDDM-DEFINE UNWRAPUInt32(VALUE)
+//%[VALUE unsignedIntValue]
+//%PDDM-DEFINE UNWRAPInt32(VALUE)
+//%[VALUE intValue]
+//%PDDM-DEFINE UNWRAPUInt64(VALUE)
+//%[VALUE unsignedLongLongValue]
+//%PDDM-DEFINE UNWRAPInt64(VALUE)
+//%[VALUE longLongValue]
+//%PDDM-DEFINE UNWRAPBool(VALUE)
+//%[VALUE boolValue]
+//%PDDM-DEFINE UNWRAPFloat(VALUE)
+//%[VALUE floatValue]
+//%PDDM-DEFINE UNWRAPDouble(VALUE)
+//%[VALUE doubleValue]
+//%PDDM-DEFINE UNWRAPEnum(VALUE)
+//%[VALUE intValue]
+//%PDDM-DEFINE TEXT_FORMAT_OBJUInt32(VALUE)
+//%[NSString stringWithFormat:@"%u", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJInt32(VALUE)
+//%[NSString stringWithFormat:@"%d", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJUInt64(VALUE)
+//%[NSString stringWithFormat:@"%llu", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJInt64(VALUE)
+//%[NSString stringWithFormat:@"%lld", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJBool(VALUE)
+//%(VALUE ? @"true" : @"false")
+//%PDDM-DEFINE TEXT_FORMAT_OBJFloat(VALUE)
+//%[NSString stringWithFormat:@"%.*g", FLT_DIG, VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJDouble(VALUE)
+//%[NSString stringWithFormat:@"%.*lg", DBL_DIG, VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJEnum(VALUE)
+//%@(VALUE)
+//%PDDM-DEFINE ENUM_TYPEPOD(TYPE)
+//%NSNumber *
+//%PDDM-DEFINE NEQ_POD(VAL1, VAL2)
+//%VAL1 != VAL2
+//%PDDM-DEFINE EXTRA_METHODS_POD(KEY_NAME, VALUE_NAME)
+// Empty
+//%PDDM-DEFINE BOOL_EXTRA_METHODS_POD(KEY_NAME, VALUE_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD(KEY_NAME, VALUE_NAME)
+//%SERIAL_DATA_FOR_ENTRY_POD_##VALUE_NAME(KEY_NAME)
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_UInt32(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Int32(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_UInt64(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Int64(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Bool(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Float(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Double(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Enum(KEY_NAME)
+//%- (NSData *)serializedDataForUnknownValue:(int32_t)value
+//%                                   forKey:(GPBValue *)key
+//%                                  keyType:(GPBType)keyType {
+//%  size_t msgSize = ComputeDict##KEY_NAME##FieldSize(key->value##KEY_NAME, kMapKeyFieldNumber, keyType);
+//%  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+//%  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+//%  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+//%  WriteDict##KEY_NAME##Field(outputStream, key->value##KEY_NAME, kMapKeyFieldNumber, keyType);
+//%  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+//%  [outputStream release];
+//%  return data;
+//%}
+//%
+//%PDDM-DEFINE GPBVALUE_POD(VALUE_NAME)
+//%value##VALUE_NAME
+
+//%PDDM-DEFINE BOOL_DICT_HAS_STORAGE_POD()
+//%  BOOL _valueSet[2];
+//%
+//%PDDM-DEFINE BOOL_DICT_INITS_POD(VALUE_NAME, VALUE_TYPE)
+//%- (instancetype)initWithValues:(const VALUE_TYPE [])values
+//%                       forKeys:(const BOOL [])keys
+//%                         count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    for (NSUInteger i = 0; i < count; ++i) {
+//%      int idx = keys[i] ? 1 : 0;
+//%      _values[idx] = values[i];
+//%      _valueSet[idx] = YES;
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPBBool##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWithValues:NULL forKeys:NULL count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      for (int i = 0; i < 2; ++i) {
+//%        if (dictionary->_valueSet[i]) {
+//%          _values[i] = dictionary->_values[i];
+//%          _valueSet[i] = YES;
+//%        }
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%PDDM-DEFINE BOOL_DICT_DEALLOCPOD()
+// Empty
+//%PDDM-DEFINE BOOL_DICT_W_HASPOD(IDX, REF)
+//%BOOL_DICT_HASPOD(IDX, REF)
+//%PDDM-DEFINE BOOL_DICT_HASPOD(IDX, REF)
+//%REF##_valueSet[IDX]
+//%PDDM-DEFINE BOOL_VALUE_FOR_KEY_POD(VALUE_TYPE)
+//%- (BOOL)valueForKey:(BOOL)key value:(VALUE_TYPE *)value {
+//%  int idx = (key ? 1 : 0);
+//%  if (_valueSet[idx]) {
+//%    if (value) {
+//%      *value = _values[idx];
+//%    }
+//%    return YES;
+//%  }
+//%  return NO;
+//%}
+//%PDDM-DEFINE BOOL_SET_GPBVALUE_FOR_KEY_POD(VALUE_NAME, VALUE_TYPE, VisP)
+//%- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+//%  int idx = (key->valueBool ? 1 : 0);
+//%  _values[idx] = value->value##VALUE_NAME;
+//%  _valueSet[idx] = YES;
+//%}
+//%PDDM-DEFINE BOOL_DICT_MUTATIONS_POD(VALUE_NAME, VALUE_TYPE)
+//%- (void)addEntriesFromDictionary:(GPBBool##VALUE_NAME##Dictionary *)otherDictionary {
+//%  if (otherDictionary) {
+//%    for (int i = 0; i < 2; ++i) {
+//%      if (otherDictionary->_valueSet[i]) {
+//%        _valueSet[i] = YES;
+//%        _values[i] = otherDictionary->_values[i];
+//%      }
+//%    }
+//%  }
+//%}
+//%
+//%- (void)setValue:(VALUE_TYPE)value forKey:(BOOL)key {
+//%  int idx = (key ? 1 : 0);
+//%  _values[idx] = value;
+//%  _valueSet[idx] = YES;
+//%}
+//%
+//%- (void)removeValueForKey:(BOOL)aKey {
+//%  _valueSet[aKey ? 1 : 0] = NO;
+//%}
+//%
+//%- (void)removeAll {
+//%  _valueSet[0] = NO;
+//%  _valueSet[1] = NO;
+//%}
+//%PDDM-DEFINE STR_FORMAT_POD(VALUE_NAME)
+//%STR_FORMAT_##VALUE_NAME()
+//%PDDM-DEFINE STR_FORMAT_UInt32()
+//%%u
+//%PDDM-DEFINE STR_FORMAT_Int32()
+//%%d
+//%PDDM-DEFINE STR_FORMAT_UInt64()
+//%%llu
+//%PDDM-DEFINE STR_FORMAT_Int64()
+//%%lld
+//%PDDM-DEFINE STR_FORMAT_Bool()
+//%%d
+//%PDDM-DEFINE STR_FORMAT_Float()
+//%%f
+//%PDDM-DEFINE STR_FORMAT_Double()
+//%%lf
+
+//
+// Helpers for Objects
+//
+
+//%PDDM-DEFINE VALUE_FOR_KEY_OBJECT(KEY_TYPE, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%- (VALUE_TYPE)valueForKey:(KEY_TYPE)key {
+//%  VALUE_TYPE result = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  return result;
+//%}
+//%PDDM-DEFINE WRAPPEDOBJECT(VALUE)
+//%VALUE
+//%PDDM-DEFINE UNWRAPString(VALUE)
+//%VALUE
+//%PDDM-DEFINE UNWRAPObject(VALUE)
+//%VALUE
+//%PDDM-DEFINE TEXT_FORMAT_OBJString(VALUE)
+//%VALUE
+//%PDDM-DEFINE TEXT_FORMAT_OBJObject(VALUE)
+//%VALUE
+//%PDDM-DEFINE ENUM_TYPEOBJECT(TYPE)
+//%ENUM_TYPEOBJECT_##TYPE()
+//%PDDM-DEFINE ENUM_TYPEOBJECT_NSString()
+//%NSString *
+//%PDDM-DEFINE ENUM_TYPEOBJECT_id()
+//%id ##
+//%PDDM-DEFINE NEQ_OBJECT(VAL1, VAL2)
+//%![VAL1 isEqual:VAL2]
+//%PDDM-DEFINE EXTRA_METHODS_OBJECT(KEY_NAME, VALUE_NAME)
+//%- (BOOL)isInitialized {
+//%  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+//%    if (!msg.initialized) {
+//%      return NO;
+//%    }
+//%  }
+//%  return YES;
+//%}
+//%
+//%- (instancetype)deepCopyWithZone:(NSZone *)zone {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *newDict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  [_dictionary enumerateKeysAndObjectsUsingBlock:^(id aKey,
+//%                                                   GPBMessage *msg,
+//%                                                   BOOL *stop) {
+//%    #pragma unused(stop)
+//%    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+//%    [newDict->_dictionary setObject:copiedMsg forKey:aKey];
+//%    [copiedMsg release];
+//%  }];
+//%  return newDict;
+//%}
+//%
+//%
+//%PDDM-DEFINE BOOL_EXTRA_METHODS_OBJECT(KEY_NAME, VALUE_NAME)
+//%- (BOOL)isInitialized {
+//%  if (_values[0] && ![_values[0] isInitialized]) {
+//%    return NO;
+//%  }
+//%  if (_values[1] && ![_values[1] isInitialized]) {
+//%    return NO;
+//%  }
+//%  return YES;
+//%}
+//%
+//%- (instancetype)deepCopyWithZone:(NSZone *)zone {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *newDict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  for (int i = 0; i < 2; ++i) {
+//%    if (_values[i] != nil) {
+//%      newDict->_values[i] = [_values[i] copyWithZone:zone];
+//%    }
+//%  }
+//%  return newDict;
+//%}
+//%
+//%
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_OBJECT(KEY_NAME, VALUE_NAME)
+// Empty
+//%PDDM-DEFINE GPBVALUE_OBJECT(VALUE_NAME)
+//%valueString
+
+
+//%PDDM-DEFINE BOOL_DICT_HAS_STORAGE_OBJECT()
+// Empty
+//%PDDM-DEFINE BOOL_DICT_INITS_OBJECT(VALUE_NAME, VALUE_TYPE)
+//%- (instancetype)initWithValues:(const VALUE_TYPE [])values
+//%                       forKeys:(const BOOL [])keys
+//%                         count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    for (NSUInteger i = 0; i < count; ++i) {
+//%      int idx = keys[i] ? 1 : 0;
+//%      [_values[idx] release];
+//%      _values[idx] = (VALUE_TYPE)[values[i] retain];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPBBool##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWithValues:NULL forKeys:NULL count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      _values[0] = [dictionary->_values[0] retain];
+//%      _values[1] = [dictionary->_values[1] retain];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%PDDM-DEFINE BOOL_DICT_DEALLOCOBJECT()
+//%- (void)dealloc {
+//%  [_values[0] release];
+//%  [_values[1] release];
+//%  [super dealloc];
+//%}
+//%
+//%
+//%PDDM-DEFINE BOOL_DICT_W_HASOBJECT(IDX, REF)
+//%(BOOL_DICT_HASOBJECT(IDX, REF))
+//%PDDM-DEFINE BOOL_DICT_HASOBJECT(IDX, REF)
+//%REF##_values[IDX] != nil
+//%PDDM-DEFINE BOOL_VALUE_FOR_KEY_OBJECT(VALUE_TYPE)
+//%- (VALUE_TYPE)valueForKey:(BOOL)key {
+//%  return _values[key ? 1 : 0];
+//%}
+//%PDDM-DEFINE BOOL_SET_GPBVALUE_FOR_KEY_OBJECT(VALUE_NAME, VALUE_TYPE, VisP)
+//%- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+//%  int idx = (key->valueBool ? 1 : 0);
+//%  [_values[idx] release];
+//%  _values[idx] = [value->valueString retain];
+//%}
+
+//%PDDM-DEFINE BOOL_DICT_MUTATIONS_OBJECT(VALUE_NAME, VALUE_TYPE)
+//%- (void)addEntriesFromDictionary:(GPBBool##VALUE_NAME##Dictionary *)otherDictionary {
+//%  if (otherDictionary) {
+//%    for (int i = 0; i < 2; ++i) {
+//%      if (otherDictionary->_values[i] != nil) {
+//%        [_values[i] release];
+//%        _values[i] = [otherDictionary->_values[i] retain];
+//%      }
+//%    }
+//%  }
+//%}
+//%
+//%- (void)setValue:(VALUE_TYPE)value forKey:(BOOL)key {
+//%  int idx = (key ? 1 : 0);
+//%  [_values[idx] release];
+//%  _values[idx] = [value retain];
+//%}
+//%
+//%- (void)removeValueForKey:(BOOL)aKey {
+//%  int idx = (aKey ? 1 : 0);
+//%  [_values[idx] release];
+//%  _values[idx] = nil;
+//%}
+//%
+//%- (void)removeAll {
+//%  for (int i = 0; i < 2; ++i) {
+//%    [_values[i] release];
+//%    _values[i] = nil;
+//%  }
+//%}
+//%PDDM-DEFINE STR_FORMAT_OBJECT(VALUE_NAME)
+//%%@
+
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32 -> UInt32
+
+@implementation GPBUInt32UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32UInt32Dictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32UInt32Dictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32UInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32UInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32UInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32UInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32UInt32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, uint32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue unsignedIntValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt32Field(outputStream, [aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(uint32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint32_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Int32
+
+@implementation GPBUInt32Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32Int32Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32Int32Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32Int32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32Int32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32Int32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32Int32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32Int32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt32Field(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> UInt64
+
+@implementation GPBUInt32UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32UInt64Dictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32UInt64Dictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32UInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32UInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32UInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32UInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32UInt64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, uint64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue unsignedLongLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt64Field(outputStream, [aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(uint64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint64_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Int64
+
+@implementation GPBUInt32Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32Int64Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32Int64Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32Int64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32Int64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32Int64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32Int64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32Int64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, int64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue longLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt64Field(outputStream, [aValue longLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(int64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int64_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Bool
+
+@implementation GPBUInt32BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32BoolDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32BoolDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32BoolDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32BoolDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32BoolDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32BoolDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32BoolDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, BOOL value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue boolValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictBoolField(outputStream, [aValue boolValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(BOOL *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(BOOL)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Float
+
+@implementation GPBUInt32FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32FloatDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32FloatDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32FloatDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32FloatDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32FloatDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32FloatDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32FloatDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, float value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue floatValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictFloatField(outputStream, [aValue floatValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(float *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(float)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Double
+
+@implementation GPBUInt32DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32DoubleDictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32DoubleDictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32DoubleDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32DoubleDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32DoubleDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32DoubleDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32DoubleDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, double value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue doubleValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictDoubleField(outputStream, [aValue doubleValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(double *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(double)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Enum
+
+@implementation GPBUInt32EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                   rawValues:&rawValue
+                                                                     forKeys:&key
+                                                                       count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])rawValues
+                                         forKeys:(const uint32_t [])keys
+                                           count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                   rawValues:rawValues
+                                                                     forKeys:keys
+                                                                       count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32EnumDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32EnumDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const uint32_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32EnumDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32EnumDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictEnumField(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType {
+  size_t msgSize = ComputeDictUInt32FieldSize(key->valueUInt32, kMapKeyFieldNumber, keyType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictUInt32Field(outputStream, key->valueUInt32, kMapKeyFieldNumber, keyType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(uint32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], @(value));
+  }];
+}
+
+- (BOOL)valueForKey:(uint32_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)valueForKey:(uint32_t)key rawValue:(int32_t *)rawValue {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+      block([aKey unsignedIntValue], unwrapped, stop);
+  }];
+}
+
+- (void)addRawEntriesFromDictionary:(GPBUInt32EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setValue:(int32_t)value forKey:(uint32_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBUInt32EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+@end
+
+#pragma mark - UInt32 -> Object
+
+@implementation GPBUInt32ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(uint32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32ObjectDictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const id [])values
+                             forKeys:(const uint32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32ObjectDictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt32ObjectDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt32ObjectDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const id [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:values[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32ObjectDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt32ObjectDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32ObjectDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint32_t key, id value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedIntValue], aValue, stop);
+  }];
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBUInt32ObjectDictionary *newDict =
+      [[GPBUInt32ObjectDictionary alloc] init];
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(id aKey,
+                                                   GPBMessage *msg,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [newDict->_dictionary setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }];
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, [aKey unsignedIntValue], kMapKeyFieldNumber, keyType);
+    WriteDictObjectField(outputStream, aValue, kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint32_t key, id value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], value);
+  }];
+}
+
+- (id)valueForKey:(uint32_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(id)value forKey:(uint32_t)key {
+  [_dictionary setObject:value forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32 -> UInt32
+
+@implementation GPBInt32UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32UInt32Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32UInt32Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32UInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32UInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32UInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32UInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32UInt32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, uint32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue unsignedIntValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt32Field(outputStream, [aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(uint32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint32_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Int32
+
+@implementation GPBInt32Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32Int32Dictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32Int32Dictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32Int32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32Int32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32Int32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32Int32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32Int32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt32Field(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> UInt64
+
+@implementation GPBInt32UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32UInt64Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32UInt64Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32UInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32UInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32UInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32UInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32UInt64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, uint64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue unsignedLongLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt64Field(outputStream, [aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(uint64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint64_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Int64
+
+@implementation GPBInt32Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32Int64Dictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32Int64Dictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32Int64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32Int64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32Int64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32Int64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32Int64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, int64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue longLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt64Field(outputStream, [aValue longLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(int64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int64_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Bool
+
+@implementation GPBInt32BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32BoolDictionary*)[self alloc] initWithValues:&value
+                                                        forKeys:&key
+                                                          count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32BoolDictionary*)[self alloc] initWithValues:values
+                                                        forKeys:keys
+                                                          count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32BoolDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32BoolDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32BoolDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32BoolDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32BoolDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, BOOL value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue boolValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictBoolField(outputStream, [aValue boolValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(BOOL *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(BOOL)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Float
+
+@implementation GPBInt32FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32FloatDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32FloatDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32FloatDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32FloatDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32FloatDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32FloatDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32FloatDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, float value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue floatValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictFloatField(outputStream, [aValue floatValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(float *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(float)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Double
+
+@implementation GPBInt32DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32DoubleDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32DoubleDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32DoubleDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32DoubleDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32DoubleDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32DoubleDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32DoubleDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, double value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue doubleValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictDoubleField(outputStream, [aValue doubleValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(double *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(double)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Enum
+
+@implementation GPBInt32EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                  rawValues:&rawValue
+                                                                    forKeys:&key
+                                                                      count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])rawValues
+                                         forKeys:(const int32_t [])keys
+                                           count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                  rawValues:rawValues
+                                                                    forKeys:keys
+                                                                      count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32EnumDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32EnumDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const int32_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32EnumDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32EnumDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictEnumField(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType {
+  size_t msgSize = ComputeDictInt32FieldSize(key->valueInt32, kMapKeyFieldNumber, keyType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictInt32Field(outputStream, key->valueInt32, kMapKeyFieldNumber, keyType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(int32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], @(value));
+  }];
+}
+
+- (BOOL)valueForKey:(int32_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)valueForKey:(int32_t)key rawValue:(int32_t *)rawValue {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+      block([aKey intValue], unwrapped, stop);
+  }];
+}
+
+- (void)addRawEntriesFromDictionary:(GPBInt32EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setValue:(int32_t)value forKey:(int32_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBInt32EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+@end
+
+#pragma mark - Int32 -> Object
+
+@implementation GPBInt32ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(int32_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32ObjectDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const id [])values
+                             forKeys:(const int32_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32ObjectDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt32ObjectDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt32ObjectDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const id [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:values[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32ObjectDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt32ObjectDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32ObjectDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int32_t key, id value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+      block([aKey intValue], aValue, stop);
+  }];
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBInt32ObjectDictionary *newDict =
+      [[GPBInt32ObjectDictionary alloc] init];
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(id aKey,
+                                                   GPBMessage *msg,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [newDict->_dictionary setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }];
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, [aKey intValue], kMapKeyFieldNumber, keyType);
+    WriteDictObjectField(outputStream, aValue, kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int32_t key, id value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], value);
+  }];
+}
+
+- (id)valueForKey:(int32_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(id)value forKey:(int32_t)key {
+  [_dictionary setObject:value forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt64 -> UInt32
+
+@implementation GPBUInt64UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64UInt32Dictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64UInt32Dictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64UInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64UInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64UInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64UInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64UInt32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, uint32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue unsignedIntValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt32Field(outputStream, [aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(uint32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint32_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Int32
+
+@implementation GPBUInt64Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64Int32Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64Int32Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64Int32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64Int32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64Int32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64Int32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64Int32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt32Field(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> UInt64
+
+@implementation GPBUInt64UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64UInt64Dictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64UInt64Dictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64UInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64UInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64UInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64UInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64UInt64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, uint64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue unsignedLongLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt64Field(outputStream, [aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(uint64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint64_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Int64
+
+@implementation GPBUInt64Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64Int64Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64Int64Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64Int64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64Int64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64Int64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64Int64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64Int64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, int64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue longLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt64Field(outputStream, [aValue longLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(int64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int64_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Bool
+
+@implementation GPBUInt64BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64BoolDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64BoolDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64BoolDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64BoolDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64BoolDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64BoolDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64BoolDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, BOOL value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue boolValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictBoolField(outputStream, [aValue boolValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(BOOL *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(BOOL)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Float
+
+@implementation GPBUInt64FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64FloatDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64FloatDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64FloatDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64FloatDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64FloatDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64FloatDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64FloatDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, float value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue floatValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictFloatField(outputStream, [aValue floatValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(float *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(float)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Double
+
+@implementation GPBUInt64DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64DoubleDictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64DoubleDictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64DoubleDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64DoubleDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64DoubleDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64DoubleDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64DoubleDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, double value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue doubleValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictDoubleField(outputStream, [aValue doubleValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(double *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(double)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Enum
+
+@implementation GPBUInt64EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                   rawValues:&rawValue
+                                                                     forKeys:&key
+                                                                       count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])rawValues
+                                         forKeys:(const uint64_t [])keys
+                                           count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                   rawValues:rawValues
+                                                                     forKeys:keys
+                                                                       count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64EnumDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64EnumDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const uint64_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64EnumDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64EnumDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictEnumField(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType {
+  size_t msgSize = ComputeDictUInt64FieldSize(key->valueUInt64, kMapKeyFieldNumber, keyType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictUInt64Field(outputStream, key->valueUInt64, kMapKeyFieldNumber, keyType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(uint64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], @(value));
+  }];
+}
+
+- (BOOL)valueForKey:(uint64_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)valueForKey:(uint64_t)key rawValue:(int32_t *)rawValue {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+      block([aKey unsignedLongLongValue], unwrapped, stop);
+  }];
+}
+
+- (void)addRawEntriesFromDictionary:(GPBUInt64EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setValue:(int32_t)value forKey:(uint64_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBUInt64EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+@end
+
+#pragma mark - UInt64 -> Object
+
+@implementation GPBUInt64ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(uint64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64ObjectDictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const id [])values
+                             forKeys:(const uint64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64ObjectDictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBUInt64ObjectDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBUInt64ObjectDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const id [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:values[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64ObjectDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBUInt64ObjectDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64ObjectDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(uint64_t key, id value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+      block([aKey unsignedLongLongValue], aValue, stop);
+  }];
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBUInt64ObjectDictionary *newDict =
+      [[GPBUInt64ObjectDictionary alloc] init];
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(id aKey,
+                                                   GPBMessage *msg,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [newDict->_dictionary setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }];
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, [aKey unsignedLongLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictObjectField(outputStream, aValue, kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(uint64_t key, id value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], value);
+  }];
+}
+
+- (id)valueForKey:(uint64_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(id)value forKey:(uint64_t)key {
+  [_dictionary setObject:value forKey:@(key)];
+}
+
+- (void)removeValueForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int64 -> UInt32
+
+@implementation GPBInt64UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64UInt32Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64UInt32Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64UInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64UInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64UInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64UInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64UInt32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, uint32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue unsignedIntValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt32Field(outputStream, [aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(uint32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint32_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Int32
+
+@implementation GPBInt64Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64Int32Dictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64Int32Dictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64Int32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64Int32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64Int32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64Int32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64Int32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt32Field(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> UInt64
+
+@implementation GPBInt64UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64UInt64Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64UInt64Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64UInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64UInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64UInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64UInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64UInt64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, uint64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue unsignedLongLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictUInt64Field(outputStream, [aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(uint64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint64_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Int64
+
+@implementation GPBInt64Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64Int64Dictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64Int64Dictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64Int64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64Int64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64Int64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64Int64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64Int64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, int64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue longLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictInt64Field(outputStream, [aValue longLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(int64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int64_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Bool
+
+@implementation GPBInt64BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64BoolDictionary*)[self alloc] initWithValues:&value
+                                                        forKeys:&key
+                                                          count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64BoolDictionary*)[self alloc] initWithValues:values
+                                                        forKeys:keys
+                                                          count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64BoolDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64BoolDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64BoolDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64BoolDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64BoolDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, BOOL value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue boolValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictBoolField(outputStream, [aValue boolValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(BOOL *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(BOOL)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Float
+
+@implementation GPBInt64FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64FloatDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64FloatDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64FloatDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64FloatDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64FloatDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64FloatDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64FloatDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, float value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue floatValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictFloatField(outputStream, [aValue floatValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(float *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(float)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Double
+
+@implementation GPBInt64DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64DoubleDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64DoubleDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64DoubleDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64DoubleDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64DoubleDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64DoubleDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64DoubleDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, double value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue doubleValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictDoubleField(outputStream, [aValue doubleValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(double *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(double)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Enum
+
+@implementation GPBInt64EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                  rawValues:&rawValue
+                                                                    forKeys:&key
+                                                                      count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])rawValues
+                                         forKeys:(const int64_t [])keys
+                                           count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64EnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                  rawValues:rawValues
+                                                                    forKeys:keys
+                                                                      count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64EnumDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64EnumDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const int64_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64EnumDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64EnumDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictEnumField(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType {
+  size_t msgSize = ComputeDictInt64FieldSize(key->valueInt64, kMapKeyFieldNumber, keyType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictInt64Field(outputStream, key->valueInt64, kMapKeyFieldNumber, keyType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(int64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], @(value));
+  }];
+}
+
+- (BOOL)valueForKey:(int64_t)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)valueForKey:(int64_t)key rawValue:(int32_t *)rawValue {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+      block([aKey longLongValue], unwrapped, stop);
+  }];
+}
+
+- (void)addRawEntriesFromDictionary:(GPBInt64EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setValue:(int32_t)value forKey:(int64_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBInt64EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+}
+
+@end
+
+#pragma mark - Int64 -> Object
+
+@implementation GPBInt64ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(int64_t)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64ObjectDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const id [])values
+                             forKeys:(const int64_t [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64ObjectDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBInt64ObjectDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBInt64ObjectDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const id [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:values[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64ObjectDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBInt64ObjectDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64ObjectDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(int64_t key, id value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+      block([aKey longLongValue], aValue, stop);
+  }];
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBInt64ObjectDictionary *newDict =
+      [[GPBInt64ObjectDictionary alloc] init];
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(id aKey,
+                                                   GPBMessage *msg,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [newDict->_dictionary setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }];
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *aKey,
+                                                   id aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictObjectFieldSize(aValue, kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, [aKey longLongValue], kMapKeyFieldNumber, keyType);
+    WriteDictObjectField(outputStream, aValue, kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(int64_t key, id value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], value);
+  }];
+}
+
+- (id)valueForKey:(int64_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(id)value forKey:(int64_t)key {
+  [_dictionary setObject:value forKey:@(key)];
+}
+
+- (void)removeValueForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_POD_IMPL_FOR_KEY(String, NSString, *, OBJECT)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - String -> UInt32
+
+@implementation GPBStringUInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringUInt32Dictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringUInt32Dictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringUInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringUInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringUInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringUInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringUInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringUInt32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, uint32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue unsignedIntValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictUInt32Field(outputStream, [aValue unsignedIntValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(uint32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringUInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint32_t)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Int32
+
+@implementation GPBStringInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringInt32Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringInt32Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringInt32Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictInt32Field(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> UInt64
+
+@implementation GPBStringUInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringUInt64Dictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringUInt64Dictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringUInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringUInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringUInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringUInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringUInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringUInt64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, uint64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue unsignedLongLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictUInt64Field(outputStream, [aValue unsignedLongLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(uint64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringUInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(uint64_t)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Int64
+
+@implementation GPBStringInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringInt64Dictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringInt64Dictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringInt64Dictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, int64_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue longLongValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictInt64Field(outputStream, [aValue longLongValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(int64_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(int64_t)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Bool
+
+@implementation GPBStringBoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringBoolDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringBoolDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringBoolDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringBoolDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringBoolDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringBoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringBoolDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringBoolDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, BOOL value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue boolValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictBoolField(outputStream, [aValue boolValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(BOOL *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringBoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(BOOL)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Float
+
+@implementation GPBStringFloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringFloatDictionary*)[self alloc] initWithValues:&value
+                                                          forKeys:&key
+                                                            count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringFloatDictionary*)[self alloc] initWithValues:values
+                                                          forKeys:keys
+                                                            count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringFloatDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringFloatDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringFloatDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringFloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringFloatDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringFloatDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, float value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue floatValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictFloatField(outputStream, [aValue floatValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(float *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringFloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(float)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Double
+
+@implementation GPBStringDoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringDoubleDictionary*)[self alloc] initWithValues:&value
+                                                           forKeys:&key
+                                                             count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const NSString * [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringDoubleDictionary*)[self alloc] initWithValues:values
+                                                           forKeys:keys
+                                                             count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringDoubleDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringDoubleDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringDoubleDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringDoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringDoubleDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringDoubleDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, double value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue doubleValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictDoubleField(outputStream, [aValue doubleValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndValuesUsingBlock:^(NSString *key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(double *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringDoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setValue:(double)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Enum
+
+@implementation GPBStringEnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        rawValue:(int32_t)rawValue
+                                          forKey:(NSString *)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringEnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                   rawValues:&rawValue
+                                                                     forKeys:&key
+                                                                       count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])rawValues
+                                         forKeys:(const NSString * [])keys
+                                           count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringEnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                   rawValues:rawValues
+                                                                     forKeys:keys
+                                                                       count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBStringEnumDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBStringEnumDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const NSString * [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringEnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringEnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBStringEnumDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringEnumDictionary class]]) {
+    return NO;
+  }
+  return [_dictionary isEqual:other->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      block(aKey, [aValue intValue], stop);
+  }];
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSUInteger count = _dictionary.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  __block size_t result = 0;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }];
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  GPBType keyType = field.mapKeyType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+    #pragma unused(stop)
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, aKey, kMapKeyFieldNumber, keyType);
+    WriteDictEnumField(outputStream, [aValue intValue], kMapValueFieldNumber, valueType);
+  }];
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType {
+  size_t msgSize = ComputeDictStringFieldSize(key->valueString, kMapKeyFieldNumber, keyType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictStringField(outputStream, key->valueString, kMapKeyFieldNumber, keyType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(NSString *key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, @(value));
+  }];
+}
+
+- (BOOL)valueForKey:(NSString *)key value:(int32_t *)value {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)valueForKey:(NSString *)key rawValue:(int32_t *)rawValue {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  [_dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *aKey,
+                                                   NSNumber *aValue,
+                                                   BOOL *stop) {
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+      block(aKey, unwrapped, stop);
+  }];
+}
+
+- (void)addRawEntriesFromDictionary:(GPBStringEnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(NSString *)key {
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+- (void)removeValueForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setValue:(int32_t)value forKey:(NSString *)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBStringEnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:key];
+}
+
+@end
+
+//%PDDM-EXPAND-END (5 expansions)
+
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> UInt32
+
+@implementation GPBBoolUInt32Dictionary {
+ @package
+  uint32_t _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint32_t)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolUInt32Dictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint32_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolUInt32Dictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolUInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolUInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolUInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolUInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolUInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolUInt32Dictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %u", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %u", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(uint32_t *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueUInt32;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%u", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%u", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictUInt32FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictUInt32FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictUInt32Field(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolUInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(uint32_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Int32
+
+@implementation GPBBoolInt32Dictionary {
+ @package
+  int32_t _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int32_t)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolInt32Dictionary*)[self alloc] initWithValues:&value
+                                                        forKeys:&key
+                                                          count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int32_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolInt32Dictionary*)[self alloc] initWithValues:values
+                                                        forKeys:keys
+                                                          count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolInt32Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolInt32Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolInt32Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolInt32Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolInt32Dictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %d", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %d", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(int32_t *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueInt32;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%d", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%d", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictInt32Field(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> UInt64
+
+@implementation GPBBoolUInt64Dictionary {
+ @package
+  uint64_t _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(uint64_t)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolUInt64Dictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const uint64_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolUInt64Dictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolUInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolUInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolUInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolUInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolUInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolUInt64Dictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %llu", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %llu", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(uint64_t *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueUInt64;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%llu", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%llu", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictUInt64FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictUInt64FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictUInt64Field(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolUInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(uint64_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Int64
+
+@implementation GPBBoolInt64Dictionary {
+ @package
+  int64_t _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(int64_t)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolInt64Dictionary*)[self alloc] initWithValues:&value
+                                                        forKeys:&key
+                                                          count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const int64_t [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolInt64Dictionary*)[self alloc] initWithValues:values
+                                                        forKeys:keys
+                                                          count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolInt64Dictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolInt64Dictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolInt64Dictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolInt64Dictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolInt64Dictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %lld", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %lld", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(int64_t *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueInt64;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%lld", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%lld", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictInt64FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictInt64FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictInt64Field(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(int64_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Bool
+
+@implementation GPBBoolBoolDictionary {
+ @package
+  BOOL _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(BOOL)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolBoolDictionary*)[self alloc] initWithValues:&value
+                                                       forKeys:&key
+                                                         count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const BOOL [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolBoolDictionary*)[self alloc] initWithValues:values
+                                                       forKeys:keys
+                                                         count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolBoolDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolBoolDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolBoolDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolBoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolBoolDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolBoolDictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %d", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %d", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(BOOL *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueBool;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", (_values[0] ? @"true" : @"false"));
+  }
+  if (_valueSet[1]) {
+    block(@"true", (_values[1] ? @"true" : @"false"));
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictBoolFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictBoolFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictBoolField(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolBoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(BOOL)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Float, float)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Float
+
+@implementation GPBBoolFloatDictionary {
+ @package
+  float _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(float)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolFloatDictionary*)[self alloc] initWithValues:&value
+                                                        forKeys:&key
+                                                          count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const float [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolFloatDictionary*)[self alloc] initWithValues:values
+                                                        forKeys:keys
+                                                          count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolFloatDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolFloatDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const float [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolFloatDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolFloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolFloatDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolFloatDictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %f", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %f", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(float *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueFloat;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%.*g", FLT_DIG, _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%.*g", FLT_DIG, _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictFloatFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictFloatFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictFloatField(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolFloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(float)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Double, double)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Double
+
+@implementation GPBBoolDoubleDictionary {
+ @package
+  double _values[2];
+  BOOL _valueSet[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(double)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolDoubleDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const double [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolDoubleDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolDoubleDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolDoubleDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const double [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolDoubleDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolDoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolDoubleDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolDoubleDictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %lf", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %lf", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(double *)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueDouble;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%.*lg", DBL_DIG, _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%.*lg", DBL_DIG, _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictDoubleFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictDoubleFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictDoubleField(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolDoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(double)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_OBJECT_IMPL(Object, id)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Object
+
+@implementation GPBBoolObjectDictionary {
+ @package
+  id _values[2];
+}
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValues:NULL forKeys:NULL count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValue:(id)value
+                             forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolObjectDictionary*)[self alloc] initWithValues:&value
+                                                         forKeys:&key
+                                                           count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValues:(const id [])values
+                             forKeys:(const BOOL [])keys
+                               count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolObjectDictionary*)[self alloc] initWithValues:values
+                                                         forKeys:keys
+                                                           count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolObjectDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolObjectDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithCapacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValues:(const id [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      [_values[idx] release];
+      _values[idx] = (id)[values[i] retain];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolObjectDictionary *)dictionary {
+  self = [self initWithValues:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      _values[0] = [dictionary->_values[0] retain];
+      _values[1] = [dictionary->_values[1] retain];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  [_values[0] release];
+  [_values[1] release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolObjectDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolObjectDictionary class]]) {
+    return NO;
+  }
+  if (((_values[0] != nil) != (other->_values[0] != nil)) ||
+      ((_values[1] != nil) != (other->_values[1] != nil))) {
+    return NO;
+  }
+  if (((_values[0] != nil) && (![_values[0] isEqual:other->_values[0]])) ||
+      ((_values[1] != nil) && (![_values[1] isEqual:other->_values[1]]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return ((_values[0] != nil) ? 1 : 0) + ((_values[1] != nil) ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if ((_values[0] != nil)) {
+    [result appendFormat:@"NO: %@", _values[0]];
+  }
+  if ((_values[1] != nil)) {
+    [result appendFormat:@"YES: %@", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return ((_values[0] != nil) ? 1 : 0) + ((_values[1] != nil) ? 1 : 0);
+}
+
+- (id)valueForKey:(BOOL)key {
+  return _values[key ? 1 : 0];
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  [_values[idx] release];
+  _values[idx] = [value->valueString retain];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_values[0] != nil) {
+    block(@"false", _values[0]);
+  }
+  if ((_values[1] != nil)) {
+    block(@"true", _values[1]);
+  }
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, id value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_values[0] != nil) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && (_values[1] != nil)) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (BOOL)isInitialized {
+  if (_values[0] && ![_values[0] isInitialized]) {
+    return NO;
+  }
+  if (_values[1] && ![_values[1] isInitialized]) {
+    return NO;
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBBoolObjectDictionary *newDict =
+      [[GPBBoolObjectDictionary alloc] init];
+  for (int i = 0; i < 2; ++i) {
+    if (_values[i] != nil) {
+      newDict->_values[i] = [_values[i] copyWithZone:zone];
+    }
+  }
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_values[i] != nil) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictObjectFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_values[i] != nil) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictObjectFieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictObjectField(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_values[i] != nil) {
+        [_values[i] release];
+        _values[i] = [otherDictionary->_values[i] retain];
+      }
+    }
+  }
+}
+
+- (void)setValue:(id)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  [_values[idx] release];
+  _values[idx] = [value retain];
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  int idx = (aKey ? 1 : 0);
+  [_values[idx] release];
+  _values[idx] = nil;
+}
+
+- (void)removeAll {
+  for (int i = 0; i < 2; ++i) {
+    [_values[i] release];
+    _values[i] = nil;
+  }
+}
+
+@end
+
+//%PDDM-EXPAND-END (8 expansions)
+
+#pragma mark - Bool -> Enum
+
+@implementation GPBBoolEnumDictionary {
+ @package
+  GPBEnumValidationFunc _validationFunc;
+  int32_t _values[2];
+  BOOL _valueSet[2];
+}
+
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)dictionary {
+  return [[[self alloc] initWithValidationFunction:NULL
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:NULL
+                                           forKeys:NULL
+                                             count:0] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValue:(int32_t)rawValue
+                                          forKey:(BOOL)key {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolEnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                 rawValues:&rawValue
+                                                                   forKeys:&key
+                                                                     count:1] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                       rawValues:(const int32_t [])values
+                                         forKeys:(const BOOL [])keys
+                                           count:(NSUInteger)count {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolEnumDictionary*)[self alloc] initWithValidationFunction:func
+                                                                 rawValues:values
+                                                                   forKeys:keys
+                                                                     count:count] autorelease];
+}
+
++ (instancetype)dictionaryWithDictionary:(GPBBoolEnumDictionary *)dictionary {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues:forKeys:count:
+  // on to get the type correct.
+  return [[(GPBBoolEnumDictionary*)[self alloc] initWithDictionary:dictionary] autorelease];
+}
+
++ (instancetype)dictionaryWithValidationFunction:(GPBEnumValidationFunc)func
+                                        capacity:(NSUInteger)numItems {
+  return [[[self alloc] initWithValidationFunction:func capacity:numItems] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                rawValues:(const int32_t [])rawValues
+                                   forKeys:(const BOOL [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = rawValues[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolEnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+#pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolEnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(GPBBoolEnumDictionary *)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolEnumDictionary class]]) {
+    return NO;
+  }
+  if ((_valueSet[0] != other->_valueSet[0]) ||
+      (_valueSet[1] != other->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != other->_values[0])) ||
+      (_valueSet[1] && (_values[1] != other->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %d", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %d", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)valueForKey:(BOOL)key value:(int32_t*)value {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      int32_t result = _values[idx];
+      if (!_validationFunc(result)) {
+        result = kGPBUnrecognizedEnumeratorValue;
+      }
+      *value = result;
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (BOOL)valueForKey:(BOOL)key rawValue:(int32_t*)rawValue {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (rawValue) {
+      *rawValue = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)enumerateKeysAndValuesUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(BOOL key, int32_t rawValue, BOOL *stop))block {
+  BOOL stop = NO;
+  GPBEnumValidationFunc func = _validationFunc;
+  int32_t validatedValue;
+  if (_valueSet[0]) {
+    validatedValue = _values[0];
+    if (!func(validatedValue)) {
+      validatedValue = kGPBUnrecognizedEnumeratorValue;
+    }
+    block(NO, validatedValue, &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    validatedValue = _values[1];
+    if (!func(validatedValue)) {
+      validatedValue = kGPBUnrecognizedEnumeratorValue;
+    }
+    block(YES, validatedValue, &stop);
+  }
+}
+
+//%PDDM-EXPAND SERIAL_DATA_FOR_ENTRY_POD_Enum(Bool)
+// This block of code is generated, do not edit it directly.
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType {
+  size_t msgSize = ComputeDictBoolFieldSize(key->valueBool, kMapKeyFieldNumber, keyType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictBoolField(outputStream, key->valueBool, kMapKeyFieldNumber, keyType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBTypeEnum);
+  [outputStream release];
+  return data;
+}
+
+//%PDDM-EXPAND-END SERIAL_DATA_FOR_ENTRY_POD_Enum(Bool)
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBType valueType = GPBGetFieldType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBTypeBool);
+      WriteDictInt32Field(outputStream, _values[i], kMapValueFieldNumber, valueType);
+    }
+  }
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", @(_values[0]));
+  }
+  if (_valueSet[1]) {
+    block(@"true", @(_values[1]));
+  }
+}
+
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueInt32;
+  _valueSet[idx] = YES;
+}
+
+- (void)addRawEntriesFromDictionary:(GPBBoolEnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+  }
+}
+
+- (void)setValue:(int32_t)value forKey:(BOOL)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBBoolEnumDictionary: Attempt to set an unknown enum value (%d)",
+     value];
+  }
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+}
+
+- (void)setRawValue:(int32_t)rawValue forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = rawValue;
+  _valueSet[idx] = YES;
+}
+
+- (void)removeValueForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end

+ 577 - 0
objectivec/GPBDictionary_PackagePrivate.h

@@ -0,0 +1,577 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBDictionary.h"
+
+@class GPBCodedInputStream;
+@class GPBCodedOutputStream;
+@class GPBExtensionRegistry;
+@class GPBFieldDescriptor;
+
+//%PDDM-DEFINE DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(KEY_NAME)
+//%DICTIONARY_POD_PRIV_INTERFACES_FOR_KEY(KEY_NAME)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Object, Object)
+//%PDDM-DEFINE DICTIONARY_POD_PRIV_INTERFACES_FOR_KEY(KEY_NAME)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, UInt32, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Int32, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, UInt64, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Int64, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Bool, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Float, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Double, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Enum, Enum)
+
+//%PDDM-DEFINE DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, VALUE_NAME, HELPER)
+//%@interface GPB##KEY_NAME##VALUE_NAME##Dictionary ()
+//%- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+//%- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+//%                         asField:(GPBFieldDescriptor *)field;
+//%- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+//%- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+//%EXTRA_DICTIONARY_PRIVATE_INTERFACES_##HELPER()@end
+//%
+
+//%PDDM-DEFINE EXTRA_DICTIONARY_PRIVATE_INTERFACES_Basic()
+// Empty
+//%PDDM-DEFINE EXTRA_DICTIONARY_PRIVATE_INTERFACES_Object()
+//%- (BOOL)isInitialized;
+//%- (instancetype)deepCopyWithZone:(NSZone *)zone
+//%    __attribute__((ns_returns_retained));
+//%
+//%PDDM-DEFINE EXTRA_DICTIONARY_PRIVATE_INTERFACES_Enum()
+//%- (NSData *)serializedDataForUnknownValue:(int32_t)value
+//%                                   forKey:(GPBValue *)key
+//%                                  keyType:(GPBType)keyType;
+//%
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(UInt32)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBUInt32UInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32Int32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32UInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32Int64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32BoolDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32FloatDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32DoubleDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt32EnumDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType;
+@end
+
+@interface GPBUInt32ObjectDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(Int32)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBInt32UInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32Int32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32UInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32Int64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32BoolDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32FloatDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32DoubleDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt32EnumDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType;
+@end
+
+@interface GPBInt32ObjectDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(UInt64)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBUInt64UInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64Int32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64UInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64Int64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64BoolDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64FloatDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64DoubleDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBUInt64EnumDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType;
+@end
+
+@interface GPBUInt64ObjectDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(Int64)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBInt64UInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64Int32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64UInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64Int64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64BoolDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64FloatDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64DoubleDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBInt64EnumDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType;
+@end
+
+@interface GPBInt64ObjectDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(Bool)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBBoolUInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolUInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolBoolDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolFloatDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolDoubleDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBBoolEnumDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType;
+@end
+
+@interface GPBBoolObjectDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_POD_PRIV_INTERFACES_FOR_KEY(String)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBStringUInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringInt32Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringUInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringInt64Dictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringBoolDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringFloatDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringDoubleDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+@interface GPBStringEnumDictionary ()
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBValue:(GPBValue *)value forGPBValueKey:(GPBValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBValue *)key
+                                  keyType:(GPBType)keyType;
+@end
+
+//%PDDM-EXPAND-END (6 expansions)
+
+CF_EXTERN_C_BEGIN
+
+// Helper to compute size when an NSDictionary is used for the map instead
+// of a custom type.
+size_t GPBDictionaryComputeSizeInternalHelper(NSDictionary *dict,
+                                              GPBFieldDescriptor *field);
+
+// Helper to write out when an NSDictionary is used for the map instead
+// of a custom type.
+void GPBDictionaryWriteToStreamInternalHelper(
+    GPBCodedOutputStream *outputStream, NSDictionary *dict,
+    GPBFieldDescriptor *field);
+
+// Helper to check message initialization when an NSDictionary is used for
+// the map instead of a custom type.
+BOOL GPBDictionaryIsInitializedInternalHelper(NSDictionary *dict,
+                                              GPBFieldDescriptor *field);
+
+// Helper to read a map instead.
+void GPBDictionaryReadEntry(id mapDictionary, GPBCodedInputStream *stream,
+                            GPBExtensionRegistry *registry,
+                            GPBFieldDescriptor *field,
+                            GPBMessage *parentMessage);
+
+CF_EXTERN_C_END

+ 51 - 0
objectivec/GPBExtensionField.h

@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBWireFormat.h"
+#import "GPBTypes.h"
+
+@class GPBCodedInputStream;
+@class GPBCodedOutputStream;
+@class GPBExtensionRegistry;
+@class GPBDescriptor;
+@class GPBExtensionDescriptor;
+
+@interface GPBExtensionField : NSObject<NSCopying>
+
+@property(nonatomic, readonly) int32_t fieldNumber;
+@property(nonatomic, readonly) GPBWireFormat wireType;
+@property(nonatomic, readonly) BOOL isRepeated;
+@property(nonatomic, readonly) GPBDescriptor *containingType;
+@property(nonatomic, readonly) id defaultValue;
+@property(nonatomic, readonly) GPBExtensionDescriptor *descriptor;
+
+@end

+ 525 - 0
objectivec/GPBExtensionField.m

@@ -0,0 +1,525 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBExtensionField_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+GPB_INLINE size_t TypeSize(GPBType type) {
+  switch (type) {
+    case GPBTypeBool:
+      return 1;
+    case GPBTypeFixed32:
+    case GPBTypeSFixed32:
+    case GPBTypeFloat:
+      return 4;
+    case GPBTypeFixed64:
+    case GPBTypeSFixed64:
+    case GPBTypeDouble:
+      return 8;
+    default:
+      return 0;
+  }
+}
+
+GPB_INLINE BOOL ExtensionIsRepeated(GPBExtensionDescription *description) {
+  return (description->options & GPBExtensionRepeated) != 0;
+}
+
+GPB_INLINE BOOL ExtensionIsPacked(GPBExtensionDescription *description) {
+  return (description->options & GPBExtensionPacked) != 0;
+}
+
+GPB_INLINE BOOL ExtensionIsWireFormat(GPBExtensionDescription *description) {
+  return (description->options & GPBExtensionSetWireFormat) != 0;
+}
+
+static size_t ComputePBSerializedSizeNoTagOfObject(GPBType type, id object) {
+#define FIELD_CASE(TYPE, ACCESSOR) \
+  case GPBType##TYPE:              \
+    return GPBCompute##TYPE##SizeNoTag([(NSNumber *)object ACCESSOR]);
+#define FIELD_CASE2(TYPE) \
+  case GPBType##TYPE:     \
+    return GPBCompute##TYPE##SizeNoTag(object);
+  switch (type) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Data)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Message)
+    FIELD_CASE2(Group)
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static size_t ComputeSerializedSizeIncludingTagOfObject(
+    GPBExtensionDescription *description, id object) {
+#define FIELD_CASE(TYPE, ACCESSOR)                          \
+  case GPBType##TYPE:                                       \
+    return GPBCompute##TYPE##Size(description->fieldNumber, \
+                                  [(NSNumber *)object ACCESSOR]);
+#define FIELD_CASE2(TYPE) \
+  case GPBType##TYPE:     \
+    return GPBCompute##TYPE##Size(description->fieldNumber, object);
+  switch (description->type) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Data)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Group)
+    case GPBTypeMessage:
+      if (ExtensionIsWireFormat(description)) {
+        return GPBComputeMessageSetExtensionSize(description->fieldNumber,
+                                                 object);
+      } else {
+        return GPBComputeMessageSize(description->fieldNumber, object);
+      }
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static size_t ComputeSerializedSizeIncludingTagOfArray(
+    GPBExtensionDescription *description, NSArray *values) {
+  if (ExtensionIsPacked(description)) {
+    size_t size = 0;
+    size_t typeSize = TypeSize(description->type);
+    if (typeSize != 0) {
+      size = values.count * typeSize;
+    } else {
+      for (id value in values) {
+        size += ComputePBSerializedSizeNoTagOfObject(description->type, value);
+      }
+    }
+    return size + GPBComputeTagSize(description->fieldNumber) +
+           GPBComputeRawVarint32SizeForInteger(size);
+  } else {
+    size_t size = 0;
+    for (id value in values) {
+      size += ComputeSerializedSizeIncludingTagOfObject(description, value);
+    }
+    return size;
+  }
+}
+
+static void WriteObjectIncludingTagToCodedOutputStream(
+    id object, GPBExtensionDescription *description,
+    GPBCodedOutputStream *output) {
+#define FIELD_CASE(TYPE, ACCESSOR)                      \
+  case GPBType##TYPE:                                   \
+    [output write##TYPE:description->fieldNumber        \
+                  value:[(NSNumber *)object ACCESSOR]]; \
+    return;
+#define FIELD_CASE2(TYPE)                                       \
+  case GPBType##TYPE:                                           \
+    [output write##TYPE:description->fieldNumber value:object]; \
+    return;
+  switch (description->type) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Data)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Group)
+    case GPBTypeMessage:
+      if (ExtensionIsWireFormat(description)) {
+        [output writeMessageSetExtension:description->fieldNumber value:object];
+      } else {
+        [output writeMessage:description->fieldNumber value:object];
+      }
+      return;
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static void WriteObjectNoTagToCodedOutputStream(
+    id object, GPBExtensionDescription *description,
+    GPBCodedOutputStream *output) {
+#define FIELD_CASE(TYPE, ACCESSOR)                             \
+  case GPBType##TYPE:                                          \
+    [output write##TYPE##NoTag:[(NSNumber *)object ACCESSOR]]; \
+    return;
+#define FIELD_CASE2(TYPE)               \
+  case GPBType##TYPE:                   \
+    [output write##TYPE##NoTag:object]; \
+    return;
+  switch (description->type) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Data)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Message)
+    case GPBTypeGroup:
+      [output writeGroupNoTag:description->fieldNumber value:object];
+      return;
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static void WriteArrayIncludingTagsToCodedOutputStream(
+    NSArray *values, GPBExtensionDescription *description,
+    GPBCodedOutputStream *output) {
+  if (ExtensionIsPacked(description)) {
+    [output writeTag:description->fieldNumber
+              format:GPBWireFormatLengthDelimited];
+    size_t dataSize = 0;
+    size_t typeSize = TypeSize(description->type);
+    if (typeSize != 0) {
+      dataSize = values.count * typeSize;
+    } else {
+      for (id value in values) {
+        dataSize +=
+            ComputePBSerializedSizeNoTagOfObject(description->type, value);
+      }
+    }
+    [output writeRawVarintSizeTAs32:dataSize];
+    for (id value in values) {
+      WriteObjectNoTagToCodedOutputStream(value, description, output);
+    }
+  } else {
+    for (id value in values) {
+      WriteObjectIncludingTagToCodedOutputStream(value, description, output);
+    }
+  }
+}
+
+@implementation GPBExtensionField {
+  GPBExtensionDescription *description_;
+  GPBExtensionDescriptor *descriptor_;
+  GPBValue defaultValue_;
+}
+
+@synthesize containingType = containingType_;
+@synthesize descriptor = descriptor_;
+
+- (instancetype)init {
+  // Throw an exception if people attempt to not use the designated initializer.
+  self = [super init];
+  if (self != nil) {
+    [self doesNotRecognizeSelector:_cmd];
+    self = nil;
+  }
+  return self;
+}
+
+- (instancetype)initWithDescription:(GPBExtensionDescription *)description {
+  if ((self = [super init])) {
+    description_ = description;
+    if (description->extendedClass) {
+      Class containingClass = objc_lookUpClass(description->extendedClass);
+      NSAssert1(containingClass, @"Class %s not defined",
+                description->extendedClass);
+      containingType_ = [containingClass descriptor];
+    }
+#if DEBUG
+    const char *className = description->messageOrGroupClassName;
+    if (className) {
+      NSAssert1(objc_lookUpClass(className) != Nil, @"Class %s not defined",
+                className);
+    }
+#endif
+    descriptor_ = [[GPBExtensionDescriptor alloc]
+        initWithExtensionDescription:description];
+    GPBType type = description_->type;
+    if (type == GPBTypeData) {
+      // Data stored as a length prefixed c-string in descriptor records.
+      const uint8_t *bytes =
+          (const uint8_t *)description->defaultValue.valueData;
+      if (bytes) {
+        uint32_t length = *((uint32_t *)bytes);
+        // The length is stored in network byte order.
+        length = ntohl(length);
+        bytes += sizeof(length);
+        defaultValue_.valueData =
+            [[NSData alloc] initWithBytes:bytes length:length];
+      }
+    } else if (type == GPBTypeMessage || type == GPBTypeGroup) {
+      // The default is looked up in -defaultValue instead since extensions
+      // aren't
+      // common, we avoid the hit startup hit and it avoid initialization order
+      // issues.
+    } else {
+      defaultValue_ = description->defaultValue;
+    }
+  }
+  return self;
+}
+
+- (void)dealloc {
+  if ((description_->type == GPBTypeData) &&
+      !ExtensionIsRepeated(description_)) {
+    [defaultValue_.valueData release];
+  }
+  [descriptor_ release];
+  [super dealloc];
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> FieldNumber:%d ContainingType:%@",
+                                    [self class], self, self.fieldNumber,
+                                    self.containingType];
+}
+
+- (id)copyWithZone:(NSZone *)__unused zone {
+  return [self retain];
+}
+
+#pragma mark Properties
+
+- (int32_t)fieldNumber {
+  return description_->fieldNumber;
+}
+
+- (GPBWireFormat)wireType {
+  return GPBWireFormatForType(description_->type,
+                              ExtensionIsPacked(description_));
+}
+
+- (BOOL)isRepeated {
+  return ExtensionIsRepeated(description_);
+}
+
+- (id)defaultValue {
+  if (ExtensionIsRepeated(description_)) {
+    return nil;
+  }
+
+  switch (description_->type) {
+    case GPBTypeBool:
+      return @(defaultValue_.valueBool);
+    case GPBTypeFloat:
+      return @(defaultValue_.valueFloat);
+    case GPBTypeDouble:
+      return @(defaultValue_.valueDouble);
+    case GPBTypeInt32:
+    case GPBTypeSInt32:
+    case GPBTypeEnum:
+    case GPBTypeSFixed32:
+      return @(defaultValue_.valueInt32);
+    case GPBTypeInt64:
+    case GPBTypeSInt64:
+    case GPBTypeSFixed64:
+      return @(defaultValue_.valueInt64);
+    case GPBTypeUInt32:
+    case GPBTypeFixed32:
+      return @(defaultValue_.valueUInt32);
+    case GPBTypeUInt64:
+    case GPBTypeFixed64:
+      return @(defaultValue_.valueUInt64);
+    case GPBTypeData:
+      // Like message fields, the default is zero length data.
+      return (defaultValue_.valueData ? defaultValue_.valueData
+                                      : GPBEmptyNSData());
+    case GPBTypeString:
+      // Like message fields, the default is zero length string.
+      return (defaultValue_.valueString ? defaultValue_.valueString : @"");
+    case GPBTypeGroup:
+    case GPBTypeMessage:
+      NSAssert(0, @"Shouldn't get here");
+      return nil;
+  }
+}
+
+#pragma mark Internals
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
+                extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                          message:(GPBMessage *)message {
+  GPBCodedInputStreamState *state = &input->state_;
+  if (ExtensionIsPacked(description_)) {
+    int32_t length = GPBCodedInputStreamReadInt32(state);
+    size_t limit = GPBCodedInputStreamPushLimit(state, length);
+    while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+      id value = [self newSingleValueFromCodedInputStream:input
+                                        extensionRegistry:extensionRegistry
+                                            existingValue:nil];
+      [message addExtension:self value:value];
+      [value release];
+    }
+    GPBCodedInputStreamPopLimit(state, limit);
+  } else {
+    id existingValue = nil;
+    BOOL isRepeated = ExtensionIsRepeated(description_);
+    if (!isRepeated && GPBTypeIsMessage(description_->type)) {
+      existingValue = [message getExistingExtension:self];
+    }
+    id value = [self newSingleValueFromCodedInputStream:input
+                                      extensionRegistry:extensionRegistry
+                                          existingValue:existingValue];
+    if (isRepeated) {
+      [message addExtension:self value:value];
+    } else {
+      [message setExtension:self value:value];
+    }
+    [value release];
+  }
+}
+
+- (void)writeValue:(id)value
+    includingTagToCodedOutputStream:(GPBCodedOutputStream *)output {
+  if (ExtensionIsRepeated(description_)) {
+    WriteArrayIncludingTagsToCodedOutputStream(value, description_, output);
+  } else {
+    WriteObjectIncludingTagToCodedOutputStream(value, description_, output);
+  }
+}
+
+- (size_t)computeSerializedSizeIncludingTag:(id)value {
+  if (ExtensionIsRepeated(description_)) {
+    return ComputeSerializedSizeIncludingTagOfArray(description_, value);
+  } else {
+    return ComputeSerializedSizeIncludingTagOfObject(description_, value);
+  }
+}
+
+// Note that this returns a retained value intentionally.
+- (id)newSingleValueFromCodedInputStream:(GPBCodedInputStream *)input
+                        extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                            existingValue:(GPBMessage *)existingValue {
+  GPBCodedInputStreamState *state = &input->state_;
+  switch (description_->type) {
+    case GPBTypeBool:     return [[NSNumber alloc] initWithBool:GPBCodedInputStreamReadBool(state)];
+    case GPBTypeFixed32:  return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadFixed32(state)];
+    case GPBTypeSFixed32: return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSFixed32(state)];
+    case GPBTypeFloat:    return [[NSNumber alloc] initWithFloat:GPBCodedInputStreamReadFloat(state)];
+    case GPBTypeFixed64:  return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadFixed64(state)];
+    case GPBTypeSFixed64: return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSFixed64(state)];
+    case GPBTypeDouble:   return [[NSNumber alloc] initWithDouble:GPBCodedInputStreamReadDouble(state)];
+    case GPBTypeInt32:    return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadInt32(state)];
+    case GPBTypeInt64:    return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadInt64(state)];
+    case GPBTypeSInt32:   return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSInt32(state)];
+    case GPBTypeSInt64:   return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSInt64(state)];
+    case GPBTypeUInt32:   return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadUInt32(state)];
+    case GPBTypeUInt64:   return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadUInt64(state)];
+    case GPBTypeData:     return GPBCodedInputStreamReadRetainedData(state);
+    case GPBTypeString:   return GPBCodedInputStreamReadRetainedString(state);
+    case GPBTypeEnum:     return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadEnum(state)];
+    case GPBTypeGroup:
+    case GPBTypeMessage: {
+      GPBMessage *message;
+      if (existingValue) {
+        message = [existingValue retain];
+      } else {
+        GPBDescriptor *decriptor = [descriptor_.msgClass descriptor];
+        message = [[decriptor.messageClass alloc] init];
+      }
+
+      if (description_->type == GPBTypeGroup) {
+        [input readGroup:description_->fieldNumber
+                 message:message
+            extensionRegistry:extensionRegistry];
+      } else {
+        // description_->type == GPBTypeMessage
+        if (ExtensionIsWireFormat(description_)) {
+          // For MessageSet fields the message length will have already been
+          // read.
+          [message mergeFromCodedInputStream:input
+                           extensionRegistry:extensionRegistry];
+        } else {
+          [input readMessage:message extensionRegistry:extensionRegistry];
+        }
+      }
+
+      return message;
+    }
+  }
+
+  return nil;
+}
+
+- (NSComparisonResult)compareByFieldNumber:(GPBExtensionField *)other {
+  int32_t selfNumber = description_->fieldNumber;
+  int32_t otherNumber = other->description_->fieldNumber;
+  if (selfNumber < otherNumber) {
+    return NSOrderedAscending;
+  } else if (selfNumber == otherNumber) {
+    return NSOrderedSame;
+  } else {
+    return NSOrderedDescending;
+  }
+}
+
+@end

+ 51 - 0
objectivec/GPBExtensionField_PackagePrivate.h

@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBExtensionField.h"
+
+struct GPBExtensionDescription;
+
+@interface GPBExtensionField ()
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
+                extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                          message:(GPBMessage *)message;
+
+- (instancetype)initWithDescription:(struct GPBExtensionDescription *)description;
+
+- (size_t)computeSerializedSizeIncludingTag:(id)value;
+- (void)writeValue:(id)value
+    includingTagToCodedOutputStream:(GPBCodedOutputStream *)output;
+
+- (NSComparisonResult)compareByFieldNumber:(GPBExtensionField *)other;
+
+@end

+ 46 - 0
objectivec/GPBExtensionRegistry.h

@@ -0,0 +1,46 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+@class GPBDescriptor;
+@class GPBExtensionField;
+
+// A table of known extensions, searchable by name or field number.  When
+// parsing a protocol message that might have extensions, you must provide an
+// ExtensionRegistry in which you have registered any extensions that you want
+// to be able to parse.  Otherwise, those extensions will just be treated like
+// unknown fields.
+@interface GPBExtensionRegistry : NSObject
+
+- (GPBExtensionField *)getExtension:(GPBDescriptor *)containingType
+                        fieldNumber:(NSInteger)fieldNumber;
+
+@end

+ 98 - 0
objectivec/GPBExtensionRegistry.m

@@ -0,0 +1,98 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBExtensionRegistry_PackagePrivate.h"
+
+#import "GPBBootstrap.h"
+#import "GPBDescriptor.h"
+#import "GPBExtensionField.h"
+
+@implementation GPBExtensionRegistry {
+  // TODO(dmaclach): Reimplement with CFDictionaries that don't use
+  // objects as keys.
+  NSMutableDictionary *mutableClassMap_;
+}
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    mutableClassMap_ = [[NSMutableDictionary alloc] init];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [mutableClassMap_ release];
+  [super dealloc];
+}
+
+- (NSMutableDictionary *)extensionMapForContainingType:
+        (GPBDescriptor *)containingType {
+  NSMutableDictionary *extensionMap =
+      [mutableClassMap_ objectForKey:containingType];
+  if (extensionMap == nil) {
+    extensionMap = [NSMutableDictionary dictionary];
+    [mutableClassMap_ setObject:extensionMap forKey:containingType];
+  }
+  return extensionMap;
+}
+
+- (void)addExtension:(GPBExtensionField *)extension {
+  if (extension == nil) {
+    return;
+  }
+
+  GPBDescriptor *containingType = [extension containingType];
+  NSMutableDictionary *extensionMap =
+      [self extensionMapForContainingType:containingType];
+  [extensionMap setObject:extension forKey:@([extension fieldNumber])];
+}
+
+- (GPBExtensionField *)getExtension:(GPBDescriptor *)containingType
+                        fieldNumber:(NSInteger)fieldNumber {
+  NSDictionary *extensionMap = [mutableClassMap_ objectForKey:containingType];
+  return [extensionMap objectForKey:@(fieldNumber)];
+}
+
+- (void)addExtensions:(GPBExtensionRegistry *)registry {
+  if (registry == nil) {
+    // In the case where there are no extensions just ignore.
+    return;
+  }
+  NSMutableDictionary *otherClassMap = registry->mutableClassMap_;
+  for (GPBDescriptor *containingType in otherClassMap) {
+    NSMutableDictionary *extensionMap =
+        [self extensionMapForContainingType:containingType];
+    NSMutableDictionary *otherExtensionMap =
+        [registry extensionMapForContainingType:containingType];
+    [extensionMap addEntriesFromDictionary:otherExtensionMap];
+  }
+}
+
+@end

+ 40 - 0
objectivec/GPBExtensionRegistry_PackagePrivate.h

@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBExtensionRegistry.h"
+
+@interface GPBExtensionRegistry ()
+
+- (void)addExtension:(GPBExtensionField *)extension;
+- (void)addExtensions:(GPBExtensionRegistry *)registry;
+
+@end

+ 56 - 0
objectivec/GPBField.h

@@ -0,0 +1,56 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+@class GPBCodedOutputStream;
+@class GPBUInt32Array;
+@class GPBUInt64Array;
+@class GPBUnknownFieldSet;
+
+@interface GPBField : NSObject<NSCopying>
+
+@property(nonatomic, readonly, assign) int32_t number;
+
+// Only one of these will be set.
+@property(nonatomic, readonly, strong) GPBUInt64Array *varintList;
+@property(nonatomic, readonly, strong) GPBUInt32Array *fixed32List;
+@property(nonatomic, readonly, strong) GPBUInt64Array *fixed64List;
+@property(nonatomic, readonly, strong) NSArray *lengthDelimitedList;
+@property(nonatomic, readonly, strong) NSArray *groupList;
+
+// Only one of these should be used per Field.
+- (void)addVarint:(uint64_t)value;
+- (void)addFixed32:(uint32_t)value;
+- (void)addFixed64:(uint64_t)value;
+- (void)addLengthDelimited:(NSData *)value;
+- (void)addGroup:(GPBUnknownFieldSet *)value;
+
+@end

+ 328 - 0
objectivec/GPBField.m

@@ -0,0 +1,328 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBField_PackagePrivate.h"
+
+#import "GPBArray.h"
+#import "GPBCodedOutputStream.h"
+
+@interface GPBField () {
+ @protected
+  int32_t number_;
+  GPBUInt64Array *mutableVarintList_;
+  GPBUInt32Array *mutableFixed32List_;
+  GPBUInt64Array *mutableFixed64List_;
+  NSMutableArray *mutableLengthDelimitedList_;
+  NSMutableArray *mutableGroupList_;
+}
+@end
+
+@implementation GPBField
+
+@synthesize number = number_;
+@synthesize varintList = mutableVarintList_;
+@synthesize fixed32List = mutableFixed32List_;
+@synthesize fixed64List = mutableFixed64List_;
+@synthesize lengthDelimitedList = mutableLengthDelimitedList_;
+@synthesize groupList = mutableGroupList_;
+
+- (instancetype)initWithNumber:(int32_t)number {
+  if ((self = [super init])) {
+    number_ = number;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [mutableVarintList_ release];
+  [mutableFixed32List_ release];
+  [mutableFixed64List_ release];
+  [mutableLengthDelimitedList_ release];
+  [mutableGroupList_ release];
+
+  [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBField *result = [[GPBField allocWithZone:zone] initWithNumber:number_];
+  result->mutableFixed32List_ = [mutableFixed32List_ copyWithZone:zone];
+  result->mutableFixed64List_ = [mutableFixed64List_ copyWithZone:zone];
+  result->mutableLengthDelimitedList_ =
+      [mutableLengthDelimitedList_ copyWithZone:zone];
+  result->mutableVarintList_ = [mutableVarintList_ copyWithZone:zone];
+  if (mutableGroupList_.count) {
+    result->mutableGroupList_ = [[NSMutableArray allocWithZone:zone]
+        initWithCapacity:mutableGroupList_.count];
+    for (GPBUnknownFieldSet *group in mutableGroupList_) {
+      GPBUnknownFieldSet *copied = [group copyWithZone:zone];
+      [result->mutableGroupList_ addObject:copied];
+      [copied release];
+    }
+  }
+  return result;
+}
+
+- (BOOL)isEqual:(id)object {
+  if (self == object) return YES;
+  if (![object isKindOfClass:[GPBField class]]) return NO;
+  GPBField *field = (GPBField *)object;
+  BOOL equalVarint =
+      (mutableVarintList_.count == 0 && field->mutableVarintList_.count == 0) ||
+      [mutableVarintList_ isEqual:field->mutableVarintList_];
+  if (!equalVarint) return NO;
+  BOOL equalFixed32 = (mutableFixed32List_.count == 0 &&
+                       field->mutableFixed32List_.count == 0) ||
+                      [mutableFixed32List_ isEqual:field->mutableFixed32List_];
+  if (!equalFixed32) return NO;
+  BOOL equalFixed64 = (mutableFixed64List_.count == 0 &&
+                       field->mutableFixed64List_.count == 0) ||
+                      [mutableFixed64List_ isEqual:field->mutableFixed64List_];
+  if (!equalFixed64) return NO;
+  BOOL equalLDList =
+      (mutableLengthDelimitedList_.count == 0 &&
+       field->mutableLengthDelimitedList_.count == 0) ||
+      [mutableLengthDelimitedList_ isEqual:field->mutableLengthDelimitedList_];
+  if (!equalLDList) return NO;
+  BOOL equalGroupList =
+      (mutableGroupList_.count == 0 && field->mutableGroupList_.count == 0) ||
+      [mutableGroupList_ isEqual:field->mutableGroupList_];
+  if (!equalGroupList) return NO;
+  return YES;
+}
+
+- (NSUInteger)hash {
+  // Just mix the hashes of the possible sub arrays.
+  const int prime = 31;
+  NSUInteger result = prime + [mutableVarintList_ hash];
+  result = prime * result + [mutableFixed32List_ hash];
+  result = prime * result + [mutableFixed64List_ hash];
+  result = prime * result + [mutableLengthDelimitedList_ hash];
+  result = prime * result + [mutableGroupList_ hash];
+  return result;
+}
+
+- (void)writeToOutput:(GPBCodedOutputStream *)output {
+  NSUInteger count = mutableVarintList_.count;
+  if (count > 0) {
+    [output writeUInt64s:number_ values:mutableVarintList_ tag:0];
+  }
+  count = mutableFixed32List_.count;
+  if (count > 0) {
+    [output writeFixed32s:number_ values:mutableFixed32List_ tag:0];
+  }
+  count = mutableFixed64List_.count;
+  if (count > 0) {
+    [output writeFixed64s:number_ values:mutableFixed64List_ tag:0];
+  }
+  count = mutableLengthDelimitedList_.count;
+  if (count > 0) {
+    [output writeDatas:number_ values:mutableLengthDelimitedList_];
+  }
+  count = mutableGroupList_.count;
+  if (count > 0) {
+    [output writeUnknownGroups:number_ values:mutableGroupList_];
+  }
+}
+
+- (size_t)serializedSize {
+  __block size_t result = 0;
+  int32_t number = number_;
+  [mutableVarintList_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        result += GPBComputeUInt64Size(number, value);
+      }];
+
+  [mutableFixed32List_
+      enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        result += GPBComputeFixed32Size(number, value);
+      }];
+
+  [mutableFixed64List_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        result += GPBComputeFixed64Size(number, value);
+      }];
+
+  for (NSData *data in mutableLengthDelimitedList_) {
+    result += GPBComputeDataSize(number, data);
+  }
+
+  for (GPBUnknownFieldSet *set in mutableGroupList_) {
+    result += GPBComputeUnknownGroupSize(number, set);
+  }
+
+  return result;
+}
+
+- (void)writeAsMessageSetExtensionToOutput:(GPBCodedOutputStream *)output {
+  for (NSData *data in mutableLengthDelimitedList_) {
+    [output writeRawMessageSetExtension:number_ value:data];
+  }
+}
+
+- (size_t)serializedSizeAsMessageSetExtension {
+  size_t result = 0;
+  for (NSData *data in mutableLengthDelimitedList_) {
+    result += GPBComputeRawMessageSetExtensionSize(number_, data);
+  }
+  return result;
+}
+
+- (NSString *)description {
+  NSMutableString *description = [NSMutableString
+      stringWithFormat:@"<%@ %p>: Field: %d {\n", [self class], self, number_];
+  [mutableVarintList_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        [description appendFormat:@"\t%llu\n", value];
+      }];
+
+  [mutableFixed32List_
+      enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        [description appendFormat:@"\t%u\n", value];
+      }];
+
+  [mutableFixed64List_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        [description appendFormat:@"\t%llu\n", value];
+      }];
+
+  for (NSData *data in mutableLengthDelimitedList_) {
+    [description appendFormat:@"\t%@\n", data];
+  }
+
+  for (GPBUnknownFieldSet *set in mutableGroupList_) {
+    [description appendFormat:@"\t%@\n", set];
+  }
+  [description appendString:@"}"];
+  return description;
+}
+
+- (void)mergeFromField:(GPBField *)other {
+  GPBUInt64Array *otherVarintList = other.varintList;
+  if (otherVarintList.count > 0) {
+    if (mutableVarintList_ == nil) {
+      mutableVarintList_ = [otherVarintList copy];
+    } else {
+      [mutableVarintList_ addValuesFromArray:otherVarintList];
+    }
+  }
+
+  GPBUInt32Array *otherFixed32List = other.fixed32List;
+  if (otherFixed32List.count > 0) {
+    if (mutableFixed32List_ == nil) {
+      mutableFixed32List_ = [otherFixed32List copy];
+    } else {
+      [mutableFixed32List_ addValuesFromArray:otherFixed32List];
+    }
+  }
+
+  GPBUInt64Array *otherFixed64List = other.fixed64List;
+  if (otherFixed64List.count > 0) {
+    if (mutableFixed64List_ == nil) {
+      mutableFixed64List_ = [otherFixed64List copy];
+    } else {
+      [mutableFixed64List_ addValuesFromArray:otherFixed64List];
+    }
+  }
+
+  NSArray *otherLengthDelimitedList = other.lengthDelimitedList;
+  if (otherLengthDelimitedList.count > 0) {
+    if (mutableLengthDelimitedList_ == nil) {
+      mutableLengthDelimitedList_ = [otherLengthDelimitedList mutableCopy];
+    } else {
+      [mutableLengthDelimitedList_
+          addObjectsFromArray:otherLengthDelimitedList];
+    }
+  }
+
+  NSArray *otherGroupList = other.groupList;
+  if (otherGroupList.count > 0) {
+    if (mutableGroupList_ == nil) {
+      mutableGroupList_ =
+          [[NSMutableArray alloc] initWithCapacity:otherGroupList.count];
+    }
+    // Make our own mutable copies.
+    for (GPBUnknownFieldSet *group in otherGroupList) {
+      GPBUnknownFieldSet *copied = [group copy];
+      [mutableGroupList_ addObject:copied];
+      [copied release];
+    }
+  }
+}
+
+- (void)addVarint:(uint64_t)value {
+  if (mutableVarintList_ == nil) {
+    mutableVarintList_ = [[GPBUInt64Array alloc] initWithValues:&value count:1];
+  } else {
+    [mutableVarintList_ addValue:value];
+  }
+}
+
+- (void)addFixed32:(uint32_t)value {
+  if (mutableFixed32List_ == nil) {
+    mutableFixed32List_ =
+        [[GPBUInt32Array alloc] initWithValues:&value count:1];
+  } else {
+    [mutableFixed32List_ addValue:value];
+  }
+}
+
+- (void)addFixed64:(uint64_t)value {
+  if (mutableFixed64List_ == nil) {
+    mutableFixed64List_ =
+        [[GPBUInt64Array alloc] initWithValues:&value count:1];
+  } else {
+    [mutableFixed64List_ addValue:value];
+  }
+}
+
+- (void)addLengthDelimited:(NSData *)value {
+  if (mutableLengthDelimitedList_ == nil) {
+    mutableLengthDelimitedList_ =
+        [[NSMutableArray alloc] initWithObjects:&value count:1];
+  } else {
+    [mutableLengthDelimitedList_ addObject:value];
+  }
+}
+
+- (void)addGroup:(GPBUnknownFieldSet *)value {
+  if (mutableGroupList_ == nil) {
+    mutableGroupList_ = [[NSMutableArray alloc] initWithObjects:&value count:1];
+  } else {
+    [mutableGroupList_ addObject:value];
+  }
+}
+
+@end

+ 49 - 0
objectivec/GPBField_PackagePrivate.h

@@ -0,0 +1,49 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBField.h"
+
+@class GPBCodedOutputStream;
+
+@interface GPBField ()
+
+- (instancetype)initWithNumber:(int32_t)number;
+
+- (void)writeToOutput:(GPBCodedOutputStream *)output;
+- (size_t)serializedSize;
+
+- (void)writeAsMessageSetExtensionToOutput:(GPBCodedOutputStream *)output;
+- (size_t)serializedSizeAsMessageSetExtension;
+
+- (void)mergeFromField:(GPBField *)other;
+
+@end

+ 151 - 0
objectivec/GPBMessage.h

@@ -0,0 +1,151 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBRootObject.h"
+
+@class GPBDescriptor;
+@class GPBCodedInputStream;
+@class GPBCodedOutputStream;
+@class GPBExtensionField;
+@class GPBFieldDescriptor;
+@class GPBUnknownFieldSet;
+
+// In DEBUG ONLY, an NSException is thrown when a parsed message doesn't
+// contain required fields. This key allows you to retrieve the parsed message
+// from the exception's |userInfo| dictionary.
+#ifdef DEBUG
+extern NSString *const GPBExceptionMessageKey;
+#endif  // DEBUG
+
+// NOTE:
+// If you add a instance method/property to this class that may conflict with
+// methods declared in protos, you need to update objective_helpers.cc.
+// The main cases are methods that take no arguments, or setFoo:/hasFoo: type
+// methods.
+@interface GPBMessage : GPBRootObject<NSCoding, NSCopying>
+
+@property(nonatomic, readonly) GPBUnknownFieldSet *unknownFields;
+
+// Are all required fields in the message and all embedded messages set.
+@property(nonatomic, readonly, getter=isInitialized) BOOL initialized;
+
+// Returns an autoreleased instance.
++ (instancetype)message;
+
+// Create a message based on a variety of inputs.
+// In DEBUG ONLY
+// @throws NSInternalInconsistencyException The message is missing one or more
+//         required fields (i.e. -[isInitialized] returns false). Use
+//         GGPBExceptionMessageKey to retrieve the message from |userInfo|.
++ (instancetype)parseFromData:(NSData *)data;
++ (instancetype)parseFromData:(NSData *)data
+            extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
++ (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
+                        extensionRegistry:
+                            (GPBExtensionRegistry *)extensionRegistry;
+
+// Create a message based on delimited input.
++ (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                                 extensionRegistry:
+                                     (GPBExtensionRegistry *)extensionRegistry;
+
+- (instancetype)initWithData:(NSData *)data;
+- (instancetype)initWithData:(NSData *)data
+           extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+- (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
+                       extensionRegistry:
+                           (GPBExtensionRegistry *)extensionRegistry;
+
+// Serializes the message and writes it to output.
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
+- (void)writeToOutputStream:(NSOutputStream *)output;
+
+// Serializes the message and writes it to output, but writes the size of the
+// message as a variant before writing the message.
+- (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output;
+- (void)writeDelimitedToOutputStream:(NSOutputStream *)output;
+
+// Serializes the message to an NSData. Note that this value is not cached, so
+// if you are using it repeatedly, cache it yourself.
+// In DEBUG ONLY:
+// @throws NSInternalInconsistencyException The message is missing one or more
+//         required fields (i.e. -[isInitialized] returns false). Use
+//         GPBExceptionMessageKey to retrieve the message from |userInfo|.
+- (NSData *)data;
+
+// Same as -[data], except a delimiter is added to the start of the data
+// indicating the size of the message data that follows.
+- (NSData *)delimitedData;
+
+// Returns the size of the object if it were serialized.
+// This is not a cached value. If you are following a pattern like this:
+// size_t size = [aMsg serializedSize];
+// NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
+// [foo writeSize:size];
+// [foo appendData:[aMsg data]];
+// you would be better doing:
+// NSData *data = [aMsg data];
+// NSUInteger size = [aMsg length];
+// NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
+// [foo writeSize:size];
+// [foo appendData:data];
+- (size_t)serializedSize;
+
+// Return the descriptor for the message
++ (GPBDescriptor *)descriptor;
+- (GPBDescriptor *)descriptor;
+
+// Extensions use boxed values (NSNumbers) for PODs, NSMutableArrays for
+// repeated. If the extension is a Message, just like fields, one will be
+// auto created for you and returned.
+- (BOOL)hasExtension:(GPBExtensionField *)extension;
+- (id)getExtension:(GPBExtensionField *)extension;
+- (void)setExtension:(GPBExtensionField *)extension value:(id)value;
+- (void)addExtension:(GPBExtensionField *)extension value:(id)value;
+- (void)setExtension:(GPBExtensionField *)extension
+               index:(NSUInteger)index
+               value:(id)value;
+- (void)clearExtension:(GPBExtensionField *)extension;
+
+- (void)setUnknownFields:(GPBUnknownFieldSet *)unknownFields;
+
+// Resets all fields to their default values.
+- (void)clear;
+
+// Parses a message of this type from the input and merges it with this
+// message.
+- (void)mergeFromData:(NSData *)data
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+
+// Merges the fields from another message (of the same type) into this
+// message.
+- (void)mergeFrom:(GPBMessage *)other;
+
+@end

+ 4735 - 0
objectivec/GPBMessage.m

@@ -0,0 +1,4735 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBMessage_PackagePrivate.h"
+
+#import <objc/runtime.h>
+#import <objc/message.h>
+
+#import "GPBArray_PackagePrivate.h"
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBExtensionField_PackagePrivate.h"
+#import "GPBExtensionRegistry_PackagePrivate.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+#ifdef DEBUG
+NSString *const GPBExceptionMessageKey =
+    GPBNSStringifySymbol(GPBExceptionMessage);
+#endif  // DEBUG
+
+//
+// PLEASE REMEMBER:
+//
+// This is the base class for *all* messages generated, so any selector defined,
+// *public* or *private* could end up colliding with a proto message field. So
+// avoid using selectors that could match a property, use C functions to hide
+// them, etc.
+//
+
+@interface GPBMessage () {
+ @package
+  GPBUnknownFieldSet *unknownFields_;
+  NSMutableDictionary *extensionMap_;
+  NSMutableDictionary *autocreatedExtensionMap_;
+
+  // If the object was autocreated, we remember the creator so that if we get
+  // mutated, we can inform the creator to make our field visible.
+  GPBMessage *autocreator_;
+  GPBFieldDescriptor *autocreatorField_;
+  GPBExtensionField *autocreatorExtension_;
+}
+@end
+
+static id CreateArrayForField(GPBFieldDescriptor *field,
+                              GPBMessage *autocreator)
+    __attribute__((ns_returns_retained));
+static id GetOrCreateArrayIvarWithField(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax);
+static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
+static id GetOrCreateMapIvarWithField(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      GPBFileSyntax syntax);
+static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap,
+                                              NSZone *zone)
+    __attribute__((ns_returns_retained));
+
+static void CheckExtension(GPBMessage *self, GPBExtensionField *extension) {
+  if ([[self class] descriptor] != [extension containingType]) {
+    [NSException
+         raise:NSInvalidArgumentException
+        format:@"Extension %@ used on wrong class (%@ instead of %@)",
+               extension.descriptor.singletonName,
+               [[self class] descriptor].name, [extension containingType].name];
+  }
+}
+
+static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap,
+                                              NSZone *zone) {
+  if (extensionMap.count == 0) {
+    return nil;
+  }
+  NSMutableDictionary *result = [[NSMutableDictionary allocWithZone:zone]
+      initWithCapacity:extensionMap.count];
+
+  for (GPBExtensionField *field in extensionMap) {
+    id value = [extensionMap objectForKey:field];
+    GPBExtensionDescriptor *fieldDescriptor = field.descriptor;
+    BOOL isMessageExtension = GPBExtensionIsMessage(fieldDescriptor);
+
+    if ([field isRepeated]) {
+      if (isMessageExtension) {
+        NSMutableArray *list =
+            [[NSMutableArray alloc] initWithCapacity:[value count]];
+        for (GPBMessage *listValue in value) {
+          GPBMessage *copiedValue = [listValue copyWithZone:zone];
+          [list addObject:copiedValue];
+          [copiedValue release];
+        }
+        [result setObject:list forKey:field];
+        [list release];
+      } else {
+        NSMutableArray *copiedValue = [value mutableCopyWithZone:zone];
+        [result setObject:copiedValue forKey:field];
+        [copiedValue release];
+      }
+    } else {
+      if (isMessageExtension) {
+        GPBMessage *copiedValue = [value copyWithZone:zone];
+        [result setObject:copiedValue forKey:field];
+        [copiedValue release];
+      } else {
+        [result setObject:value forKey:field];
+      }
+    }
+  }
+
+  return result;
+}
+
+static id CreateArrayForField(GPBFieldDescriptor *field,
+                              GPBMessage *autocreator) {
+  id result;
+  GPBType fieldDataType = GPBGetFieldType(field);
+  switch (fieldDataType) {
+    case GPBTypeBool:
+      result = [[GPBBoolArray alloc] init];
+      break;
+    case GPBTypeFixed32:
+    case GPBTypeUInt32:
+      result = [[GPBUInt32Array alloc] init];
+      break;
+    case GPBTypeInt32:
+    case GPBTypeSFixed32:
+    case GPBTypeSInt32:
+      result = [[GPBInt32Array alloc] init];
+      break;
+    case GPBTypeFixed64:
+    case GPBTypeUInt64:
+      result = [[GPBUInt64Array alloc] init];
+      break;
+    case GPBTypeInt64:
+    case GPBTypeSFixed64:
+    case GPBTypeSInt64:
+      result = [[GPBInt64Array alloc] init];
+      break;
+    case GPBTypeFloat:
+      result = [[GPBFloatArray alloc] init];
+      break;
+    case GPBTypeDouble:
+      result = [[GPBDoubleArray alloc] init];
+      break;
+
+    case GPBTypeEnum:
+      result = [[GPBEnumArray alloc]
+                  initWithValidationFunction:field.enumDescriptor.enumVerifier];
+      break;
+
+    case GPBTypeData:
+    case GPBTypeGroup:
+    case GPBTypeMessage:
+    case GPBTypeString:
+      if (autocreator) {
+        result = [[GPBAutocreatedArray alloc] init];
+      } else {
+        result = [[NSMutableArray alloc] init];
+      }
+      break;
+  }
+
+  if (autocreator) {
+    if (GPBTypeIsObject(fieldDataType)) {
+      GPBAutocreatedArray *autoArray = result;
+      autoArray->_autocreator =  autocreator;
+    } else {
+      GPBInt32Array *gpbArray = result;
+      gpbArray->_autocreator = autocreator;
+    }
+  }
+
+  return result;
+}
+
+
+#if !defined(__clang_analyzer__)
+// These functions are blocked from the analyzer because the analyzer sees the
+// GPBSetRetainedObjectIvarWithFieldInternal() call as consuming the array/map,
+// so use of the array/map after the call returns is flagged as a use after
+// free.
+// But GPBSetRetainedObjectIvarWithFieldInternal() is "consuming" the retain
+// count be holding onto the object (it is transfering it), the object is
+// still valid after returning from the call.  The other way to avoid this
+// would be to add a -retain/-autorelease, but that would force every
+// repeated/map field parsed into the autorelease pool which is both a memory
+// and performance hit.
+
+static id GetOrCreateArrayIvarWithField(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax) {
+  id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!array) {
+    // No lock needed, this is called from places expecting to mutate
+    // so no threading protection is needed.
+    array = CreateArrayForField(field, nil);
+    GPBSetRetainedObjectIvarWithFieldInternal(self, field, array, syntax);
+  }
+  return array;
+}
+
+// This is like GPBGetObjectIvarWithField(), but for arrays, it should
+// only be used to wire the method into the class.
+static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+  id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!array) {
+    // Check again after getting the lock.
+    OSSpinLockLock(&self->readOnlyMutex_);
+    array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+    if (!array) {
+      array = CreateArrayForField(field, self);
+      GPBSetAutocreatedRetainedObjectIvarWithField(self, field, array);
+    }
+    OSSpinLockUnlock(&self->readOnlyMutex_);
+  }
+  return array;
+}
+
+static id GetOrCreateMapIvarWithField(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      GPBFileSyntax syntax) {
+  id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!dict) {
+    GPBType keyType = field.mapKeyType;
+    GPBType valueType = GPBGetFieldType(field);
+    switch (keyType) {
+      case GPBTypeBool:
+        switch (valueType) {
+          case GPBTypeBool:
+            dict = [[GPBBoolBoolDictionary alloc] init];
+            break;
+          case GPBTypeFixed32:
+          case GPBTypeUInt32:
+            dict = [[GPBBoolUInt32Dictionary alloc] init];
+            break;
+          case GPBTypeInt32:
+          case GPBTypeSFixed32:
+          case GPBTypeSInt32:
+            dict = [[GPBBoolInt32Dictionary alloc] init];
+            break;
+          case GPBTypeFixed64:
+          case GPBTypeUInt64:
+            dict = [[GPBBoolUInt64Dictionary alloc] init];
+            break;
+          case GPBTypeInt64:
+          case GPBTypeSFixed64:
+          case GPBTypeSInt64:
+            dict = [[GPBBoolInt64Dictionary alloc] init];
+            break;
+          case GPBTypeFloat:
+            dict = [[GPBBoolFloatDictionary alloc] init];
+            break;
+          case GPBTypeDouble:
+            dict = [[GPBBoolDoubleDictionary alloc] init];
+            break;
+          case GPBTypeEnum:
+            dict = [[GPBBoolEnumDictionary alloc]
+                initWithValidationFunction:field.enumDescriptor.enumVerifier];
+            break;
+          case GPBTypeData:
+          case GPBTypeMessage:
+          case GPBTypeString:
+            dict = [[GPBBoolObjectDictionary alloc] init];
+            break;
+          case GPBTypeGroup:
+            NSCAssert(NO, @"shouldn't happen");
+            return nil;
+        }
+        break;
+      case GPBTypeFixed32:
+      case GPBTypeUInt32:
+        switch (valueType) {
+          case GPBTypeBool:
+            dict = [[GPBUInt32BoolDictionary alloc] init];
+            break;
+          case GPBTypeFixed32:
+          case GPBTypeUInt32:
+            dict = [[GPBUInt32UInt32Dictionary alloc] init];
+            break;
+          case GPBTypeInt32:
+          case GPBTypeSFixed32:
+          case GPBTypeSInt32:
+            dict = [[GPBUInt32Int32Dictionary alloc] init];
+            break;
+          case GPBTypeFixed64:
+          case GPBTypeUInt64:
+            dict = [[GPBUInt32UInt64Dictionary alloc] init];
+            break;
+          case GPBTypeInt64:
+          case GPBTypeSFixed64:
+          case GPBTypeSInt64:
+            dict = [[GPBUInt32Int64Dictionary alloc] init];
+            break;
+          case GPBTypeFloat:
+            dict = [[GPBUInt32FloatDictionary alloc] init];
+            break;
+          case GPBTypeDouble:
+            dict = [[GPBUInt32DoubleDictionary alloc] init];
+            break;
+          case GPBTypeEnum:
+            dict = [[GPBUInt32EnumDictionary alloc]
+                initWithValidationFunction:field.enumDescriptor.enumVerifier];
+            break;
+          case GPBTypeData:
+          case GPBTypeMessage:
+          case GPBTypeString:
+            dict = [[GPBUInt32ObjectDictionary alloc] init];
+            break;
+          case GPBTypeGroup:
+            NSCAssert(NO, @"shouldn't happen");
+            return nil;
+        }
+        break;
+      case GPBTypeInt32:
+      case GPBTypeSFixed32:
+      case GPBTypeSInt32:
+        switch (valueType) {
+          case GPBTypeBool:
+            dict = [[GPBInt32BoolDictionary alloc] init];
+            break;
+          case GPBTypeFixed32:
+          case GPBTypeUInt32:
+            dict = [[GPBInt32UInt32Dictionary alloc] init];
+            break;
+          case GPBTypeInt32:
+          case GPBTypeSFixed32:
+          case GPBTypeSInt32:
+            dict = [[GPBInt32Int32Dictionary alloc] init];
+            break;
+          case GPBTypeFixed64:
+          case GPBTypeUInt64:
+            dict = [[GPBInt32UInt64Dictionary alloc] init];
+            break;
+          case GPBTypeInt64:
+          case GPBTypeSFixed64:
+          case GPBTypeSInt64:
+            dict = [[GPBInt32Int64Dictionary alloc] init];
+            break;
+          case GPBTypeFloat:
+            dict = [[GPBInt32FloatDictionary alloc] init];
+            break;
+          case GPBTypeDouble:
+            dict = [[GPBInt32DoubleDictionary alloc] init];
+            break;
+          case GPBTypeEnum:
+            dict = [[GPBInt32EnumDictionary alloc]
+                initWithValidationFunction:field.enumDescriptor.enumVerifier];
+            break;
+          case GPBTypeData:
+          case GPBTypeMessage:
+          case GPBTypeString:
+            dict = [[GPBInt32ObjectDictionary alloc] init];
+            break;
+          case GPBTypeGroup:
+            NSCAssert(NO, @"shouldn't happen");
+            return nil;
+        }
+        break;
+      case GPBTypeFixed64:
+      case GPBTypeUInt64:
+        switch (valueType) {
+          case GPBTypeBool:
+            dict = [[GPBUInt64BoolDictionary alloc] init];
+            break;
+          case GPBTypeFixed32:
+          case GPBTypeUInt32:
+            dict = [[GPBUInt64UInt32Dictionary alloc] init];
+            break;
+          case GPBTypeInt32:
+          case GPBTypeSFixed32:
+          case GPBTypeSInt32:
+            dict = [[GPBUInt64Int32Dictionary alloc] init];
+            break;
+          case GPBTypeFixed64:
+          case GPBTypeUInt64:
+            dict = [[GPBUInt64UInt64Dictionary alloc] init];
+            break;
+          case GPBTypeInt64:
+          case GPBTypeSFixed64:
+          case GPBTypeSInt64:
+            dict = [[GPBUInt64Int64Dictionary alloc] init];
+            break;
+          case GPBTypeFloat:
+            dict = [[GPBUInt64FloatDictionary alloc] init];
+            break;
+          case GPBTypeDouble:
+            dict = [[GPBUInt64DoubleDictionary alloc] init];
+            break;
+          case GPBTypeEnum:
+            dict = [[GPBUInt64EnumDictionary alloc]
+                initWithValidationFunction:field.enumDescriptor.enumVerifier];
+            break;
+          case GPBTypeData:
+          case GPBTypeMessage:
+          case GPBTypeString:
+            dict = [[GPBUInt64ObjectDictionary alloc] init];
+            break;
+          case GPBTypeGroup:
+            NSCAssert(NO, @"shouldn't happen");
+            return nil;
+        }
+        break;
+      case GPBTypeInt64:
+      case GPBTypeSFixed64:
+      case GPBTypeSInt64:
+        switch (valueType) {
+          case GPBTypeBool:
+            dict = [[GPBInt64BoolDictionary alloc] init];
+            break;
+          case GPBTypeFixed32:
+          case GPBTypeUInt32:
+            dict = [[GPBInt64UInt32Dictionary alloc] init];
+            break;
+          case GPBTypeInt32:
+          case GPBTypeSFixed32:
+          case GPBTypeSInt32:
+            dict = [[GPBInt64Int32Dictionary alloc] init];
+            break;
+          case GPBTypeFixed64:
+          case GPBTypeUInt64:
+            dict = [[GPBInt64UInt64Dictionary alloc] init];
+            break;
+          case GPBTypeInt64:
+          case GPBTypeSFixed64:
+          case GPBTypeSInt64:
+            dict = [[GPBInt64Int64Dictionary alloc] init];
+            break;
+          case GPBTypeFloat:
+            dict = [[GPBInt64FloatDictionary alloc] init];
+            break;
+          case GPBTypeDouble:
+            dict = [[GPBInt64DoubleDictionary alloc] init];
+            break;
+          case GPBTypeEnum:
+            dict = [[GPBInt64EnumDictionary alloc]
+                initWithValidationFunction:field.enumDescriptor.enumVerifier];
+            break;
+          case GPBTypeData:
+          case GPBTypeMessage:
+          case GPBTypeString:
+            dict = [[GPBInt64ObjectDictionary alloc] init];
+            break;
+          case GPBTypeGroup:
+            NSCAssert(NO, @"shouldn't happen");
+            return nil;
+        }
+        break;
+      case GPBTypeString:
+        switch (valueType) {
+          case GPBTypeBool:
+            dict = [[GPBStringBoolDictionary alloc] init];
+            break;
+          case GPBTypeFixed32:
+          case GPBTypeUInt32:
+            dict = [[GPBStringUInt32Dictionary alloc] init];
+            break;
+          case GPBTypeInt32:
+          case GPBTypeSFixed32:
+          case GPBTypeSInt32:
+            dict = [[GPBStringInt32Dictionary alloc] init];
+            break;
+          case GPBTypeFixed64:
+          case GPBTypeUInt64:
+            dict = [[GPBStringUInt64Dictionary alloc] init];
+            break;
+          case GPBTypeInt64:
+          case GPBTypeSFixed64:
+          case GPBTypeSInt64:
+            dict = [[GPBStringInt64Dictionary alloc] init];
+            break;
+          case GPBTypeFloat:
+            dict = [[GPBStringFloatDictionary alloc] init];
+            break;
+          case GPBTypeDouble:
+            dict = [[GPBStringDoubleDictionary alloc] init];
+            break;
+          case GPBTypeEnum:
+            dict = [[GPBStringEnumDictionary alloc]
+                initWithValidationFunction:field.enumDescriptor.enumVerifier];
+            break;
+          case GPBTypeData:
+          case GPBTypeMessage:
+          case GPBTypeString:
+            dict = [[NSMutableDictionary alloc] init];
+            break;
+          case GPBTypeGroup:
+            NSCAssert(NO, @"shouldn't happen");
+            return nil;
+        }
+        break;
+
+      case GPBTypeFloat:
+      case GPBTypeDouble:
+      case GPBTypeEnum:
+      case GPBTypeData:
+      case GPBTypeGroup:
+      case GPBTypeMessage:
+        NSCAssert(NO, @"shouldn't happen");
+        return nil;
+    }
+
+    GPBSetRetainedObjectIvarWithFieldInternal(self, field, dict, syntax);
+  }
+  return dict;
+}
+
+#endif  // !defined(__clang_analyzer__)
+
+GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass,
+                                            GPBMessage *autocreator,
+                                            GPBFieldDescriptor *field) {
+  GPBMessage *message = [[msgClass alloc] init];
+  message->autocreator_ = autocreator;
+  message->autocreatorField_ = [field retain];
+  return message;
+}
+
+static GPBMessage *CreateMessageWithAutocreatorForExtension(
+    Class msgClass, GPBMessage *autocreator, GPBExtensionField *extension)
+    __attribute__((ns_returns_retained));
+
+static GPBMessage *CreateMessageWithAutocreatorForExtension(
+    Class msgClass, GPBMessage *autocreator, GPBExtensionField *extension) {
+  GPBMessage *message = [[msgClass alloc] init];
+  message->autocreator_ = autocreator;
+  message->autocreatorExtension_ = [extension retain];
+  return message;
+}
+
+BOOL GPBWasMessageAutocreatedBy(GPBMessage *message, GPBMessage *parent) {
+  return (message->autocreator_ == parent);
+}
+
+void GPBBecomeVisibleToAutocreator(GPBMessage *self) {
+  // Message objects that are implicitly created by accessing a message field
+  // are initially not visible via the hasX selector. This method makes them
+  // visible.
+  if (self->autocreator_) {
+    // This will recursively make all parent messages visible until it reaches a
+    // super-creator that's visible.
+    if (self->autocreatorField_) {
+      GPBFileSyntax syntax = [self->autocreator_ descriptor].file.syntax;
+      GPBSetObjectIvarWithFieldInternal(self->autocreator_,
+                                        self->autocreatorField_, self, syntax);
+    } else {
+      [self->autocreator_ setExtension:self->autocreatorExtension_ value:self];
+    }
+  }
+}
+
+void GPBAutocreatedArrayModified(GPBMessage *self, id array) {
+  // When one of our autocreated arrays adds elements, make it visible.
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (field.fieldType == GPBFieldTypeRepeated) {
+      id curArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (curArray == array) {
+        if (GPBFieldTypeIsObject(field)) {
+          GPBAutocreatedArray *autoArray = array;
+          autoArray->_autocreator = nil;
+        } else {
+          GPBInt32Array *gpbArray = array;
+          gpbArray->_autocreator = nil;
+        }
+        GPBBecomeVisibleToAutocreator(self);
+        return;
+      }
+    }
+  }
+  NSCAssert(NO, @"Unknown array.");
+}
+
+void GPBClearMessageAutocreator(GPBMessage *self) {
+  if ((self == nil) || !self->autocreator_) {
+    return;
+  }
+
+#if DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+  // Either the autocreator must have its "has" flag set to YES, or it must be
+  // NO and not equal to ourselves.
+  BOOL autocreatorHas =
+      (self->autocreatorField_
+           ? GPBGetHasIvarField(self->autocreator_, self->autocreatorField_)
+           : [self->autocreator_ hasExtension:self->autocreatorExtension_]);
+  GPBMessage *autocreatorFieldValue =
+      (self->autocreatorField_
+           ? GPBGetObjectIvarWithFieldNoAutocreate(self->autocreator_,
+                                                   self->autocreatorField_)
+           : [self->autocreator_->autocreatedExtensionMap_
+                 objectForKey:self->autocreatorExtension_]);
+  NSCAssert(autocreatorHas || autocreatorFieldValue != self,
+            @"Cannot clear autocreator because it still refers to self.");
+
+#endif  // DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+
+  self->autocreator_ = nil;
+  [self->autocreatorField_ release];
+  self->autocreatorField_ = nil;
+  [self->autocreatorExtension_ release];
+  self->autocreatorExtension_ = nil;
+}
+
+static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
+  if (!self->unknownFields_) {
+    self->unknownFields_ = [[GPBUnknownFieldSet alloc] init];
+    GPBBecomeVisibleToAutocreator(self);
+  }
+  return self->unknownFields_;
+}
+
+#ifdef DEBUG
+static void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) {
+  if (!message.initialized) {
+    NSString *reason =
+        [NSString stringWithFormat:@"Uninitialized Message %@", message];
+    NSDictionary *userInfo =
+        message ? @{GPBExceptionMessageKey : message} : nil;
+    NSException *exception =
+        [NSException exceptionWithName:NSInternalInconsistencyException
+                                reason:reason
+                              userInfo:userInfo];
+    [exception raise];
+  }
+}
+#else
+GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) {
+#pragma unused(message)
+}
+#endif  // DEBUG
+
+@implementation GPBMessage
+
++ (void)initialize {
+  Class pbMessageClass = [GPBMessage class];
+  if ([self class] == pbMessageClass) {
+    // This is here to start up the "base" class descriptor.
+    [self descriptor];
+  } else if ([self superclass] == pbMessageClass) {
+    // This is here to start up all the "message" subclasses. Just needs to be
+    // done for the messages, not any of the subclasses.
+    // This must be done in initialize to enforce thread safety of start up of
+    // the protocol buffer library. All of the extension registries must be
+    // created in either "+load" or "+initialize".
+    [self descriptor];
+    [self extensionRegistry];
+  }
+}
+
++ (instancetype)allocWithZone:(NSZone *)zone {
+  // Override alloc to allocate our classes with the additional storage
+  // required for the instance variables.
+  GPBDescriptor *descriptor = [self descriptor];
+  return NSAllocateObject(self, descriptor->storageSize_, zone);
+}
+
++ (instancetype)alloc {
+  return [self allocWithZone:nil];
+}
+
++ (GPBDescriptor *)descriptor {
+  // This is thread safe because it is called from +initialize.
+  static GPBDescriptor *descriptor = NULL;
+  static GPBFileDescriptor *fileDescriptor = NULL;
+  if (!descriptor) {
+    // Use a dummy file that marks it as proto2 syntax so when used generically
+    // it supports unknowns/etc.
+    fileDescriptor =
+        [[GPBFileDescriptor alloc] initWithPackage:@"internal"
+                                            syntax:GPBFileSyntaxProto2];
+
+    descriptor = [GPBDescriptor allocDescriptorForClass:[GPBMessage class]
+                                              rootClass:Nil
+                                                   file:fileDescriptor
+                                                 fields:NULL
+                                             fieldCount:0
+                                                 oneofs:NULL
+                                             oneofCount:0
+                                                  enums:NULL
+                                              enumCount:0
+                                                 ranges:NULL
+                                             rangeCount:0
+                                            storageSize:0
+                                             wireFormat:NO];
+  }
+  return descriptor;
+}
+
++ (instancetype)message {
+  return [[[self alloc] init] autorelease];
+}
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    messageStorage_ = (GPBMessage_StoragePtr)(
+        ((uint8_t *)self) + class_getInstanceSize([self class]));
+
+    readOnlyMutex_ = OS_SPINLOCK_INIT;
+  }
+
+  return self;
+}
+
+- (instancetype)initWithData:(NSData *)data {
+  return [self initWithData:data extensionRegistry:nil];
+}
+
+- (instancetype)initWithData:(NSData *)data
+           extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  if ((self = [self init])) {
+    [self mergeFromData:data extensionRegistry:extensionRegistry];
+    DebugRaiseExceptionIfNotInitialized(self);
+  }
+  return self;
+}
+
+- (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
+                       extensionRegistry:
+                           (GPBExtensionRegistry *)extensionRegistry {
+  if ((self = [self init])) {
+    [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+    DebugRaiseExceptionIfNotInitialized(self);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self internalClear:NO];
+  NSCAssert(!autocreator_, @"Autocreator was not cleared before dealloc.");
+  [super dealloc];
+}
+
+- (void)copyFieldsInto:(GPBMessage *)message
+                  zone:(NSZone *)zone
+            descriptor:(GPBDescriptor *)descriptor {
+  // Copy all the storage...
+  memcpy(message->messageStorage_, messageStorage_, descriptor->storageSize_);
+
+  GPBFileSyntax syntax = descriptor.file.syntax;
+
+  // Loop over the fields doing fixup...
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      id value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (value) {
+        // We need to copy the array/map, but the catch is for message fields,
+        // we also need to ensure all the messages as those need copying also.
+        id newValue;
+        if (GPBFieldTypeIsMessage(field)) {
+          if (field.fieldType == GPBFieldTypeRepeated) {
+            NSArray *existingArray = (NSArray *)value;
+            NSMutableArray *newArray =
+                [[NSMutableArray alloc] initWithCapacity:existingArray.count];
+            newValue = newArray;
+            for (GPBMessage *msg in existingArray) {
+              GPBMessage *copiedMsg = [msg copyWithZone:zone];
+              [newArray addObject:copiedMsg];
+              [copiedMsg release];
+            }
+          } else {
+            if (field.mapKeyType == GPBTypeString) {
+              // Map is an NSDictionary.
+              NSDictionary *existingDict = value;
+              NSMutableDictionary *newDict = [[NSMutableDictionary alloc]
+                  initWithCapacity:existingDict.count];
+              newValue = newDict;
+              [existingDict enumerateKeysAndObjectsUsingBlock:^(NSString *key,
+                                                                GPBMessage *msg,
+                                                                BOOL *stop) {
+#pragma unused(stop)
+                GPBMessage *copiedMsg = [msg copyWithZone:zone];
+                [newDict setObject:copiedMsg forKey:key];
+                [copiedMsg release];
+              }];
+            } else {
+              // Is one of the GPB*ObjectDictionary classes.  Type doesn't
+              // matter, just need one to invoke the selector.
+              GPBInt32ObjectDictionary *existingDict = value;
+              newValue = [existingDict deepCopyWithZone:zone];
+            }
+          }
+        } else {
+          // Not messages (but is a map/array)...
+          if (field.fieldType == GPBFieldTypeRepeated) {
+            if (GPBFieldTypeIsObject(field)) {
+              // NSArray
+              newValue = [value mutableCopyWithZone:zone];
+            } else {
+              // GPB*Array
+              newValue = [value copyWithZone:zone];
+            }
+          } else {
+            if (field.mapKeyType == GPBTypeString) {
+              // NSDictionary
+              newValue = [value mutableCopyWithZone:zone];
+            } else {
+              // Is one of the GPB*Dictionary classes.  Type doesn't matter,
+              // just need one to invoke the selector.
+              GPBInt32Int32Dictionary *existingDict = value;
+              newValue = [existingDict copyWithZone:zone];
+            }
+          }
+        }
+        // We retain here because the memcpy picked up the pointer value and
+        // the next call to SetRetainedObject... will release the current value.
+        [value retain];
+        GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue,
+                                                  syntax);
+      }
+    } else if (GPBFieldTypeIsMessage(field)) {
+      // For object types, if we have a value, copy it.  If we don't,
+      // zero it to remove the pointer to something that was autocreated
+      // (and the ptr just got memcpyed).
+      if (GPBGetHasIvarField(self, field)) {
+        GPBMessage *value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBMessage *newValue = [value copyWithZone:zone];
+        // We retain here because the memcpy picked up the pointer value and
+        // the next call to SetRetainedObject... will release the current value.
+        [value retain];
+        GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue,
+                                                  syntax);
+      } else {
+        uint8_t *storage = (uint8_t *)message->messageStorage_;
+        id *typePtr = (id *)&storage[field->description_->offset];
+        *typePtr = NULL;
+      }
+    } else if (GPBFieldTypeIsObject(field) && GPBGetHasIvarField(self, field)) {
+      // A set string/data value (message picked off above), copy it.
+      id value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      id newValue = [value copyWithZone:zone];
+      // We retain here because the memcpy picked up the pointer value and
+      // the next call to SetRetainedObject... will release the current value.
+      [value retain];
+      GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue,
+                                                syntax);
+    } else {
+      // memcpy took care of the rest of the primative fields if they were set.
+    }
+  }  // for (field in descriptor->fields_)
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBMessage *result = [[descriptor.messageClass allocWithZone:zone] init];
+
+  [self copyFieldsInto:result zone:zone descriptor:descriptor];
+  // Make immutable copies of the extra bits.
+  result->unknownFields_ = [unknownFields_ copyWithZone:zone];
+  result->extensionMap_ = CloneExtensionMap(extensionMap_, zone);
+  return result;
+}
+
+- (void)clear {
+  [self internalClear:YES];
+}
+
+- (void)internalClear:(BOOL)zeroStorage {
+  GPBDescriptor *descriptor = [self descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      id arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (arrayOrMap) {
+        if (field.fieldType == GPBFieldTypeRepeated) {
+          if (GPBFieldTypeIsObject(field)) {
+            GPBAutocreatedArray *autoArray = arrayOrMap;
+            if (autoArray->_autocreator == self) {
+              autoArray->_autocreator = nil;
+            }
+          } else {
+            // Type doesn't matter, it is a GPB*Array.
+            GPBInt32Array *gpbArray = arrayOrMap;
+            if (gpbArray->_autocreator == self) {
+              gpbArray->_autocreator = nil;
+            }
+          }
+        }
+        [arrayOrMap release];
+      }
+    } else if (GPBFieldTypeIsMessage(field)) {
+      GPBClearAutocreatedMessageIvarWithField(self, field);
+      GPBMessage *value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      [value release];
+    } else if (GPBFieldTypeIsObject(field) && GPBGetHasIvarField(self, field)) {
+      id value = GPBGetObjectIvarWithField(self, field);
+      [value release];
+    }
+  }
+
+  // GPBClearMessageAutocreator() expects that its caller has already been
+  // removed from autocreatedExtensionMap_ so we set to nil first.
+  NSArray *autocreatedValues = [autocreatedExtensionMap_ allValues];
+  [autocreatedExtensionMap_ release];
+  autocreatedExtensionMap_ = nil;
+
+  // Since we're clearing all of our extensions, make sure that we clear the
+  // autocreator on any that we've created so they no longer refer to us.
+  for (GPBMessage *value in autocreatedValues) {
+    NSCAssert(GPBWasMessageAutocreatedBy(value, self),
+              @"Autocreated extension does not refer back to self.");
+    GPBClearMessageAutocreator(value);
+  }
+
+  [extensionMap_ release];
+  extensionMap_ = nil;
+  [unknownFields_ release];
+  unknownFields_ = nil;
+
+  // Note that clearing does not affect autocreator_. If we are being cleared
+  // because of a dealloc, then autocreator_ should be nil anyway. If we are
+  // being cleared because someone explicitly clears us, we don't want to
+  // sever our relationship with our autocreator.
+
+  if (zeroStorage) {
+    memset(messageStorage_, 0, descriptor->storageSize_);
+  }
+}
+
+- (BOOL)isInitialized {
+  GPBDescriptor *descriptor = [self descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (field.isRequired) {
+      if (!GPBGetHasIvarField(self, field)) {
+        return NO;
+      }
+    }
+    if (GPBFieldTypeIsMessage(field)) {
+      GPBFieldType fieldType = field.fieldType;
+      if (fieldType == GPBFieldTypeSingle) {
+        if (field.isRequired) {
+          GPBMessage *message = GPBGetMessageIvarWithField(self, field);
+          if (!message.initialized) {
+            return NO;
+          }
+        } else {
+          NSAssert(field.isOptional,
+                   @"If not required or optional, what was it?");
+          if (GPBGetHasIvarField(self, field)) {
+            GPBMessage *message = GPBGetMessageIvarWithField(self, field);
+            if (!message.initialized) {
+              return NO;
+            }
+          }
+        }
+      } else if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        for (GPBMessage *message in array) {
+          if (!message.initialized) {
+            return NO;
+          }
+        }
+      } else {  // fieldType == GPBFieldTypeMap
+        if (field.mapKeyType == GPBTypeString) {
+          NSDictionary *map =
+              GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+          if (map && !GPBDictionaryIsInitializedInternalHelper(map, field)) {
+            return NO;
+          }
+        } else {
+          // Real type is GPB*ObjectDictionary, exact type doesn't matter.
+          GPBInt32ObjectDictionary *map =
+              GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+          if (map && ![map isInitialized]) {
+            return NO;
+          }
+        }
+      }
+    }
+  }
+
+  __block BOOL result = YES;
+  [extensionMap_
+      enumerateKeysAndObjectsUsingBlock:^(GPBExtensionField *extension, id obj,
+                                          BOOL *stop) {
+        GPBExtensionDescriptor *extDesc = extension.descriptor;
+        if (GPBExtensionIsMessage(extDesc)) {
+          if (extDesc.isRepeated) {
+            for (GPBMessage *msg in obj) {
+              if (!msg.initialized) {
+                result = NO;
+                *stop = YES;
+                break;
+              }
+            }
+          } else {
+            GPBMessage *asMsg = obj;
+            if (!asMsg.initialized) {
+              result = NO;
+              *stop = YES;
+            }
+          }
+        }
+      }];
+  return result;
+}
+
+- (GPBDescriptor *)descriptor {
+  return [[self class] descriptor];
+}
+
+- (NSData *)data {
+  DebugRaiseExceptionIfNotInitialized(self);
+  NSMutableData *data = [NSMutableData dataWithLength:[self serializedSize]];
+  GPBCodedOutputStream *stream =
+      [[GPBCodedOutputStream alloc] initWithData:data];
+  [self writeToCodedOutputStream:stream];
+  [stream release];
+  return data;
+}
+
+- (NSData *)delimitedData {
+  size_t serializedSize = [self serializedSize];
+  size_t varintSize = GPBComputeRawVarint32SizeForInteger(serializedSize);
+  NSMutableData *data =
+      [NSMutableData dataWithLength:(serializedSize + varintSize)];
+  GPBCodedOutputStream *stream =
+      [[GPBCodedOutputStream alloc] initWithData:data];
+  [self writeDelimitedToCodedOutputStream:stream];
+  [stream release];
+  return data;
+}
+
+- (void)writeToOutputStream:(NSOutputStream *)output {
+  GPBCodedOutputStream *stream =
+      [[GPBCodedOutputStream alloc] initWithOutputStream:output];
+  [self writeToCodedOutputStream:stream];
+  [stream release];
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
+  GPBDescriptor *descriptor = [self descriptor];
+  NSArray *fieldsArray = descriptor->fields_;
+  NSUInteger fieldCount = fieldsArray.count;
+  const GPBExtensionRange *extensionRanges = descriptor.extensionRanges;
+  NSUInteger extensionRangesCount = descriptor.extensionRangesCount;
+  for (NSUInteger i = 0, j = 0; i < fieldCount || j < extensionRangesCount;) {
+    if (i == fieldCount) {
+      [self writeExtensionsToCodedOutputStream:output
+                                         range:extensionRanges[j++]];
+    } else if (j == extensionRangesCount ||
+               GPBFieldNumber(fieldsArray[i]) < extensionRanges[j].start) {
+      [self writeField:fieldsArray[i++] toCodedOutputStream:output];
+    } else {
+      [self writeExtensionsToCodedOutputStream:output
+                                         range:extensionRanges[j++]];
+    }
+  }
+  if (descriptor.isWireFormat) {
+    [unknownFields_ writeAsMessageSetTo:output];
+  } else {
+    [unknownFields_ writeToCodedOutputStream:output];
+  }
+}
+
+- (void)writeDelimitedToOutputStream:(NSOutputStream *)output {
+  GPBCodedOutputStream *codedOutput =
+      [[GPBCodedOutputStream alloc] initWithOutputStream:output];
+  [self writeDelimitedToCodedOutputStream:codedOutput];
+  [codedOutput release];
+}
+
+- (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output {
+  [output writeRawVarintSizeTAs32:[self serializedSize]];
+  [self writeToCodedOutputStream:output];
+}
+
+- (void)writeField:(GPBFieldDescriptor *)field
+    toCodedOutputStream:(GPBCodedOutputStream *)output {
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeSingle) {
+    BOOL has = GPBGetHasIvarField(self, field);
+    if (!has) {
+      return;
+    }
+  }
+  uint32_t fieldNumber = GPBFieldNumber(field);
+
+//%PDDM-DEFINE FIELD_CASE(TYPE, REAL_TYPE)
+//%FIELD_CASE_FULL(TYPE, REAL_TYPE, REAL_TYPE)
+//%PDDM-DEFINE FIELD_CASE_FULL(TYPE, REAL_TYPE, ARRAY_TYPE)
+//%    case GPBType##TYPE:
+//%      if (fieldType == GPBFieldTypeRepeated) {
+//%        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+//%        GPB##ARRAY_TYPE##Array *array =
+//%            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        [output write##TYPE##s:fieldNumber values:array tag:tag];
+//%      } else if (fieldType == GPBFieldTypeSingle) {
+//%        [output write##TYPE:fieldNumber
+//%              TYPE$S  value:GPBGet##REAL_TYPE##IvarWithField(self, field)];
+//%      } else {  // fieldType == GPBFieldTypeMap
+//%        // Exact type here doesn't matter.
+//%        GPBInt32##ARRAY_TYPE##Dictionary *dict =
+//%            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        [dict writeToCodedOutputStream:output asField:field];
+//%      }
+//%      break;
+//%
+//%PDDM-DEFINE FIELD_CASE2(TYPE)
+//%    case GPBType##TYPE:
+//%      if (fieldType == GPBFieldTypeRepeated) {
+//%        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        [output write##TYPE##s:fieldNumber values:array];
+//%      } else if (fieldType == GPBFieldTypeSingle) {
+//%        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+//%        // again.
+//%        [output write##TYPE:fieldNumber
+//%              TYPE$S  value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+//%      } else {  // fieldType == GPBFieldTypeMap
+//%        // Exact type here doesn't matter.
+//%        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        GPBType mapKeyType = field.mapKeyType;
+//%        if (mapKeyType == GPBTypeString) {
+//%          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+//%        } else {
+//%          [dict writeToCodedOutputStream:output asField:field];
+//%        }
+//%      }
+//%      break;
+//%
+
+  switch (GPBGetFieldType(field)) {
+
+//%PDDM-EXPAND FIELD_CASE(Bool, Bool)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeBool:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBBoolArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeBools:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeBool:fieldNumber
+                    value:GPBGetBoolIvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32BoolDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Fixed32, UInt32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeFixed32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeFixed32s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeFixed32:fieldNumber
+                       value:GPBGetUInt32IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SFixed32, Int32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeSFixed32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSFixed32s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSFixed32:fieldNumber
+                        value:GPBGetInt32IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Float, Float)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeFloat:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBFloatArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeFloats:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeFloat:fieldNumber
+                     value:GPBGetFloatIvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32FloatDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Fixed64, UInt64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeFixed64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeFixed64s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeFixed64:fieldNumber
+                       value:GPBGetUInt64IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SFixed64, Int64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeSFixed64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSFixed64s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSFixed64:fieldNumber
+                        value:GPBGetInt64IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Double, Double)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeDouble:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBDoubleArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeDoubles:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeDouble:fieldNumber
+                      value:GPBGetDoubleIvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32DoubleDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Int32, Int32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeInt32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeInt32s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeInt32:fieldNumber
+                     value:GPBGetInt32IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Int64, Int64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeInt64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeInt64s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeInt64:fieldNumber
+                     value:GPBGetInt64IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SInt32, Int32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeSInt32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSInt32s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSInt32:fieldNumber
+                      value:GPBGetInt32IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SInt64, Int64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeSInt64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSInt64s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSInt64:fieldNumber
+                      value:GPBGetInt64IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(UInt32, UInt32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeUInt32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeUInt32s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeUInt32:fieldNumber
+                      value:GPBGetUInt32IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(UInt64, UInt64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeUInt64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeUInt64s:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeUInt64:fieldNumber
+                      value:GPBGetUInt64IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE_FULL(Enum, Int32, Enum)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeEnum:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBEnumArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeEnums:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeEnum:fieldNumber
+                    value:GPBGetInt32IvarWithField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32EnumDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(Data)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeData:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeDatas:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeData:fieldNumber
+                    value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBType mapKeyType = field.mapKeyType;
+        if (mapKeyType == GPBTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(String)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeString:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeStrings:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeString:fieldNumber
+                      value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBType mapKeyType = field.mapKeyType;
+        if (mapKeyType == GPBTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(Message)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeMessage:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeMessages:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeMessage:fieldNumber
+                       value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBType mapKeyType = field.mapKeyType;
+        if (mapKeyType == GPBTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(Group)
+// This block of code is generated, do not edit it directly.
+
+    case GPBTypeGroup:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeGroups:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeGroup:fieldNumber
+                     value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBType mapKeyType = field.mapKeyType;
+        if (mapKeyType == GPBTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND-END (18 expansions)
+  }
+}
+
+#pragma mark - Extensions
+
+- (id)getExtension:(GPBExtensionField *)extension {
+  CheckExtension(self, extension);
+  id value = [extensionMap_ objectForKey:extension];
+  if (value != nil) {
+    return value;
+  }
+
+  GPBExtensionDescriptor *extDesc = extension.descriptor;
+
+  // No default for repeated.
+  if (extDesc.isRepeated) {
+    return nil;
+  }
+  // Non messages get their default.
+  if (!GPBExtensionIsMessage(extDesc)) {
+    return [extension defaultValue];
+  }
+
+  // Check for an autocreated value.
+  OSSpinLockLock(&readOnlyMutex_);
+  value = [autocreatedExtensionMap_ objectForKey:extension];
+  if (!value) {
+    // Auto create the message extensions to match normal fields.
+    value = CreateMessageWithAutocreatorForExtension(extDesc.msgClass, self,
+                                                     extension);
+
+    if (autocreatedExtensionMap_ == nil) {
+      autocreatedExtensionMap_ = [[NSMutableDictionary alloc] init];
+    }
+
+    // We can't simply call setExtension here because that would clear the new
+    // value's autocreator.
+    [autocreatedExtensionMap_ setObject:value forKey:extension];
+    [value release];
+  }
+
+  OSSpinLockUnlock(&readOnlyMutex_);
+  return value;
+}
+
+- (id)getExistingExtension:(GPBExtensionField *)extension {
+  // This is an internal method so we don't need to call CheckExtension().
+  return [extensionMap_ objectForKey:extension];
+}
+
+- (BOOL)hasExtension:(GPBExtensionField *)extension {
+#if DEBUG
+  CheckExtension(self, extension);
+#endif  // DEBUG
+  return nil != [extensionMap_ objectForKey:extension];
+}
+
+- (NSArray *)extensionsCurrentlySet {
+  return [extensionMap_ allKeys];
+}
+
+- (void)writeExtensionsToCodedOutputStream:(GPBCodedOutputStream *)output
+                                     range:(GPBExtensionRange)range {
+  NSArray *sortedExtensions = [[extensionMap_ allKeys]
+      sortedArrayUsingSelector:@selector(compareByFieldNumber:)];
+  uint32_t start = range.start;
+  uint32_t end = range.end;
+  for (GPBExtensionField *extension in sortedExtensions) {
+    uint32_t fieldNumber = [extension fieldNumber];
+    if (fieldNumber >= start && fieldNumber < end) {
+      id value = [extensionMap_ objectForKey:extension];
+      [extension writeValue:value includingTagToCodedOutputStream:output];
+    }
+  }
+}
+
+- (NSArray *)sortedExtensionsInUse {
+  return [[extensionMap_ allKeys]
+      sortedArrayUsingSelector:@selector(compareByFieldNumber:)];
+}
+
+- (void)setExtension:(GPBExtensionField *)extension value:(id)value {
+  if (!value) {
+    [self clearExtension:extension];
+    return;
+  }
+
+  CheckExtension(self, extension);
+
+  if ([extension isRepeated]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Must call addExtension() for repeated types."];
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ = [[NSMutableDictionary alloc] init];
+  }
+
+  [extensionMap_ setObject:value forKey:extension];
+
+  GPBExtensionDescriptor *descriptor = extension.descriptor;
+
+  if (GPBExtensionIsMessage(descriptor) && !descriptor.isRepeated) {
+    GPBMessage *autocreatedValue =
+        [[autocreatedExtensionMap_ objectForKey:extension] retain];
+    // Must remove from the map before calling GPBClearMessageAutocreator() so
+    // that GPBClearMessageAutocreator() knows its safe to clear.
+    [autocreatedExtensionMap_ removeObjectForKey:extension];
+    GPBClearMessageAutocreator(autocreatedValue);
+    [autocreatedValue release];
+  }
+
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+- (void)addExtension:(GPBExtensionField *)extension value:(id)value {
+  CheckExtension(self, extension);
+
+  if (![extension isRepeated]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Must call setExtension() for singular types."];
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ = [[NSMutableDictionary alloc] init];
+  }
+  NSMutableArray *list = [extensionMap_ objectForKey:extension];
+  if (list == nil) {
+    list = [NSMutableArray array];
+    [extensionMap_ setObject:list forKey:extension];
+  }
+
+  [list addObject:value];
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+- (void)setExtension:(GPBExtensionField *)extension
+               index:(NSUInteger)idx
+               value:(id)value {
+  CheckExtension(self, extension);
+
+  if (![extension isRepeated]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Must call setExtension() for singular types."];
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ = [[NSMutableDictionary alloc] init];
+  }
+
+  NSMutableArray *list = [extensionMap_ objectForKey:extension];
+
+  [list replaceObjectAtIndex:idx withObject:value];
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+- (void)clearExtension:(GPBExtensionField *)extension {
+  CheckExtension(self, extension);
+
+  // Only become visible if there was actually a value to clear.
+  if ([extensionMap_ objectForKey:extension]) {
+    [extensionMap_ removeObjectForKey:extension];
+    GPBBecomeVisibleToAutocreator(self);
+  }
+}
+
+#pragma mark - mergeFrom
+
+- (void)mergeFromData:(NSData *)data
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
+  [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+  [input checkLastTagWas:0];
+  [input release];
+}
+
+#pragma mark - mergeDelimitedFrom
+
+- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                         extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  GPBCodedInputStreamState *state = &input->state_;
+  if (GPBCodedInputStreamIsAtEnd(state)) {
+    return;
+  }
+  NSData *data = GPBCodedInputStreamReadRetainedDataNoCopy(state);
+  if (data == nil) {
+    return;
+  }
+  [self mergeFromData:data extensionRegistry:extensionRegistry];
+  [data release];
+}
+
+#pragma mark - Parse From Data Support
+
++ (instancetype)parseFromData:(NSData *)data {
+  return [self parseFromData:data extensionRegistry:nil];
+}
+
++ (instancetype)parseFromData:(NSData *)data
+            extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  return [[[self alloc] initWithData:data
+                   extensionRegistry:extensionRegistry] autorelease];
+}
+
++ (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
+                        extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  return
+      [[[self alloc] initWithCodedInputStream:input
+                            extensionRegistry:extensionRegistry] autorelease];
+}
+
+#pragma mark - Parse Delimited From Data Support
+
++ (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                                 extensionRegistry:
+                                     (GPBExtensionRegistry *)extensionRegistry {
+  GPBMessage *message = [[[self alloc] init] autorelease];
+  [message mergeDelimitedFromCodedInputStream:input
+                            extensionRegistry:extensionRegistry];
+  DebugRaiseExceptionIfNotInitialized(message);
+  return message;
+}
+
+#pragma mark - Unknown Field Support
+
+- (GPBUnknownFieldSet *)unknownFields {
+  return unknownFields_;
+}
+
+- (void)setUnknownFields:(GPBUnknownFieldSet *)unknownFields {
+  if (unknownFields != unknownFields_) {
+    [unknownFields_ release];
+    unknownFields_ = [unknownFields copy];
+    GPBBecomeVisibleToAutocreator(self);
+  }
+}
+
+- (void)parseMessageSet:(GPBCodedInputStream *)input
+      extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  uint32_t typeId = 0;
+  NSData *rawBytes = nil;
+  GPBExtensionField *extension = nil;
+  GPBCodedInputStreamState *state = &input->state_;
+  while (true) {
+    uint32_t tag = GPBCodedInputStreamReadTag(state);
+    if (tag == 0) {
+      break;
+    }
+
+    if (tag == GPBWireFormatMessageSetTypeIdTag) {
+      typeId = GPBCodedInputStreamReadUInt32(state);
+      if (typeId != 0) {
+        extension = [extensionRegistry getExtension:[self descriptor]
+                                        fieldNumber:typeId];
+      }
+    } else if (tag == GPBWireFormatMessageSetMessageTag) {
+      rawBytes = [GPBCodedInputStreamReadRetainedDataNoCopy(state) autorelease];
+    } else {
+      if (![input skipField:tag]) {
+        break;
+      }
+    }
+  }
+
+  [input checkLastTagWas:GPBWireFormatMessageSetItemEndTag];
+
+  if (rawBytes != nil && typeId != 0) {
+    if (extension != nil) {
+      GPBCodedInputStream *newInput =
+          [[GPBCodedInputStream alloc] initWithData:rawBytes];
+      [extension mergeFromCodedInputStream:newInput
+                         extensionRegistry:extensionRegistry
+                                   message:self];
+      [newInput release];
+    } else {
+      GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+      [unknownFields mergeMessageSetMessage:typeId data:rawBytes];
+    }
+  }
+}
+
+- (BOOL)parseUnknownField:(GPBCodedInputStream *)input
+        extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                      tag:(uint32_t)tag {
+  GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
+  int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
+
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBExtensionField *extension =
+      [extensionRegistry getExtension:descriptor fieldNumber:fieldNumber];
+
+  if (extension == nil) {
+    if (descriptor.wireFormat && GPBWireFormatMessageSetItemTag == tag) {
+      [self parseMessageSet:input extensionRegistry:extensionRegistry];
+      return YES;
+    }
+  } else {
+    if ([extension wireType] == wireType) {
+      [extension mergeFromCodedInputStream:input
+                         extensionRegistry:extensionRegistry
+                                   message:self];
+      return YES;
+    }
+  }
+  if ([GPBUnknownFieldSet isFieldTag:tag]) {
+    GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+    return [unknownFields mergeFieldFrom:tag input:input];
+  } else {
+    return NO;
+  }
+}
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data {
+  GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+  [unknownFields addUnknownMapEntry:fieldNum value:data];
+}
+
+#pragma mark - MergeFromCodedInputStream Support
+
+typedef struct MergeFromCodedInputStreamContext {
+  GPBMessage *self;
+  GPBCodedInputStream *stream;
+  GPBMessage *result;
+  GPBExtensionRegistry *registry;
+  uint32_t tag;
+  GPBFileSyntax syntax;
+} MergeFromCodedInputStreamContext;
+
+//%PDDM-DEFINE MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(NAME, TYPE, ARRAY_TYPE)
+//%static BOOL DynamicMergeFromCodedInputStream##NAME(GPBFieldDescriptor *field,
+//%                                           NAME$S  void *voidContext) {
+//%  MergeFromCodedInputStreamContext *context =
+//%      (MergeFromCodedInputStreamContext *)voidContext;
+//%  GPBCodedInputStreamState *state = &context->stream->state_;
+//%  GPBFieldType fieldType = field.fieldType;
+//%  if (fieldType == GPBFieldTypeRepeated) {
+//%    GPB##ARRAY_TYPE##Array *array =
+//%        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+//%    if (field.isPackable) {
+//%      int32_t length = GPBCodedInputStreamReadInt32(state);
+//%      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+//%      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+//%        TYPE val = GPBCodedInputStreamRead##NAME(state);
+//%        [array addValue:val];
+//%      }
+//%      GPBCodedInputStreamPopLimit(state, limit);
+//%    } else {
+//%      TYPE val = GPBCodedInputStreamRead##NAME(state);
+//%      [array addValue:val];
+//%    }
+//%  } else if (fieldType == GPBFieldTypeSingle) {
+//%    TYPE val = GPBCodedInputStreamRead##NAME(state);
+//%    GPBSet##ARRAY_TYPE##IvarWithFieldInternal(context->result, field, val,
+//%           ARRAY_TYPE$S                     context->syntax);
+//%  } else {  // fieldType == GPBFieldTypeMap
+//%    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+//%    GPBInt32Int32Dictionary *map =
+//%        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+//%    [context->stream readMapEntry:map
+//%                extensionRegistry:nil
+//%                            field:field
+//%                    parentMessage:context->result];
+//%  }
+//%  return NO;
+//%}
+//%
+//%PDDM-DEFINE MERGE_FROM_CODED_INPUT_STREAM_OBJ_FUNC(NAME)
+//%static BOOL DynamicMergeFromCodedInputStream##NAME(GPBFieldDescriptor *field,
+//%                                           NAME$S  void *voidContext) {
+//%  MergeFromCodedInputStreamContext *context = voidContext;
+//%  GPBCodedInputStreamState *state = &context->stream->state_;
+//%  GPBFieldType fieldType = field.fieldType;
+//%  if (fieldType == GPBFieldTypeMap) {
+//%    // GPB*Dictionary or NSDictionary, exact type doesn't matter at this point.
+//%    id map =
+//%        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+//%    [context->stream readMapEntry:map
+//%                extensionRegistry:nil
+//%                            field:field
+//%                    parentMessage:context->result];
+//%  } else {
+//%    id val = GPBCodedInputStreamReadRetained##NAME(state);
+//%    if (fieldType == GPBFieldTypeRepeated) {
+//%      NSMutableArray *array =
+//%          GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+//%      [array addObject:val];
+//%      [val release];
+//%    } else {  // fieldType == GPBFieldTypeSingle
+//%      GPBSetRetainedObjectIvarWithFieldInternal(context->result, field, val,
+//%                                                context->syntax);
+//%    }
+//%  }
+//%  return NO;
+//%}
+//%
+
+static BOOL DynamicMergeFromCodedInputStreamGroup(GPBFieldDescriptor *field,
+                                                  void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  int number = GPBFieldNumber(field);
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBMessage *message = [[field.msgClass alloc] init];
+    NSMutableArray *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    [context->stream readGroup:number
+                       message:message
+             extensionRegistry:context->registry];
+    [array addObject:message];
+    [message release];
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL has = GPBGetHasIvarField(context->result, field);
+    if (has) {
+      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+      // again.
+      GPBMessage *message =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->result, field);
+      [context->stream readGroup:number
+                         message:message
+               extensionRegistry:context->registry];
+    } else {
+      GPBMessage *message = [[field.msgClass alloc] init];
+      [context->stream readGroup:number
+                         message:message
+               extensionRegistry:context->registry];
+      GPBSetRetainedObjectIvarWithFieldInternal(context->result, field, message,
+                                                context->syntax);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    NSCAssert(NO, @"Shouldn't happen");
+    return YES;
+  }
+  return NO;
+}
+
+static BOOL DynamicMergeFromCodedInputStreamMessage(GPBFieldDescriptor *field,
+                                                    void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBMessage *message = [[field.msgClass alloc] init];
+    NSMutableArray *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    [context->stream readMessage:message extensionRegistry:context->registry];
+    [array addObject:message];
+    [message release];
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL has = GPBGetHasIvarField(context->result, field);
+    if (has) {
+      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+      // again.
+      GPBMessage *message =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->result, field);
+      [context->stream readMessage:message extensionRegistry:context->registry];
+    } else {
+      GPBMessage *message = [[field.msgClass alloc] init];
+      [context->stream readMessage:message extensionRegistry:context->registry];
+      GPBSetRetainedObjectIvarWithFieldInternal(context->result, field, message,
+                                                context->syntax);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // GPB*Dictionary or NSDictionary, exact type doesn't matter.
+    id map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:context->registry
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+static BOOL DynamicMergeFromCodedInputStreamEnum(GPBFieldDescriptor *field,
+                                                 void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  int number = GPBFieldNumber(field);
+  BOOL hasPreservingUnknownEnumSemantics =
+      GPBHasPreservingUnknownEnumSemantics(context->syntax);
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBEnumArray *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int32_t val = GPBCodedInputStreamReadEnum(state);
+        if (hasPreservingUnknownEnumSemantics || [field isValidEnumValue:val]) {
+          [array addRawValue:val];
+        } else {
+          GPBUnknownFieldSet *unknownFields =
+              GetOrMakeUnknownFields(context->self);
+          [unknownFields mergeVarintField:number value:val];
+        }
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int32_t val = GPBCodedInputStreamReadEnum(state);
+      if (hasPreservingUnknownEnumSemantics || [field isValidEnumValue:val]) {
+        [array addRawValue:val];
+      } else {
+        GPBUnknownFieldSet *unknownFields =
+            GetOrMakeUnknownFields(context->self);
+        [unknownFields mergeVarintField:number value:val];
+      }
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int32_t val = GPBCodedInputStreamReadEnum(state);
+    if (hasPreservingUnknownEnumSemantics || [field isValidEnumValue:val]) {
+      GPBSetInt32IvarWithFieldInternal(context->result, field, val,
+                                       context->syntax);
+    } else {
+      GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(context->self);
+      [unknownFields mergeVarintField:number value:val];
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a
+    // GPB*EnumDictionary.
+    GPBInt32EnumDictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:context->registry
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Bool, BOOL, Bool)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamBool(GPBFieldDescriptor *field,
+                                                 void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBBoolArray *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        BOOL val = GPBCodedInputStreamReadBool(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      BOOL val = GPBCodedInputStreamReadBool(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL val = GPBCodedInputStreamReadBool(state);
+    GPBSetBoolIvarWithFieldInternal(context->result, field, val,
+                                    context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Int32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamInt32(GPBFieldDescriptor *field,
+                                                  void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt32Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int32_t val = GPBCodedInputStreamReadInt32(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int32_t val = GPBCodedInputStreamReadInt32(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int32_t val = GPBCodedInputStreamReadInt32(state);
+    GPBSetInt32IvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(SInt32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamSInt32(GPBFieldDescriptor *field,
+                                                   void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt32Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int32_t val = GPBCodedInputStreamReadSInt32(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int32_t val = GPBCodedInputStreamReadSInt32(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int32_t val = GPBCodedInputStreamReadSInt32(state);
+    GPBSetInt32IvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(SFixed32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamSFixed32(GPBFieldDescriptor *field,
+                                                     void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt32Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int32_t val = GPBCodedInputStreamReadSFixed32(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int32_t val = GPBCodedInputStreamReadSFixed32(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int32_t val = GPBCodedInputStreamReadSFixed32(state);
+    GPBSetInt32IvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(UInt32, uint32_t, UInt32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamUInt32(GPBFieldDescriptor *field,
+                                                   void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt32Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        uint32_t val = GPBCodedInputStreamReadUInt32(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      uint32_t val = GPBCodedInputStreamReadUInt32(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    uint32_t val = GPBCodedInputStreamReadUInt32(state);
+    GPBSetUInt32IvarWithFieldInternal(context->result, field, val,
+                                      context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Fixed32, uint32_t, UInt32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamFixed32(GPBFieldDescriptor *field,
+                                                    void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt32Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        uint32_t val = GPBCodedInputStreamReadFixed32(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      uint32_t val = GPBCodedInputStreamReadFixed32(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    uint32_t val = GPBCodedInputStreamReadFixed32(state);
+    GPBSetUInt32IvarWithFieldInternal(context->result, field, val,
+                                      context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Int64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamInt64(GPBFieldDescriptor *field,
+                                                  void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt64Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int64_t val = GPBCodedInputStreamReadInt64(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int64_t val = GPBCodedInputStreamReadInt64(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int64_t val = GPBCodedInputStreamReadInt64(state);
+    GPBSetInt64IvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(SFixed64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamSFixed64(GPBFieldDescriptor *field,
+                                                     void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt64Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int64_t val = GPBCodedInputStreamReadSFixed64(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int64_t val = GPBCodedInputStreamReadSFixed64(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int64_t val = GPBCodedInputStreamReadSFixed64(state);
+    GPBSetInt64IvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(SInt64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamSInt64(GPBFieldDescriptor *field,
+                                                   void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt64Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        int64_t val = GPBCodedInputStreamReadSInt64(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      int64_t val = GPBCodedInputStreamReadSInt64(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    int64_t val = GPBCodedInputStreamReadSInt64(state);
+    GPBSetInt64IvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(UInt64, uint64_t, UInt64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamUInt64(GPBFieldDescriptor *field,
+                                                   void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt64Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        uint64_t val = GPBCodedInputStreamReadUInt64(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      uint64_t val = GPBCodedInputStreamReadUInt64(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    uint64_t val = GPBCodedInputStreamReadUInt64(state);
+    GPBSetUInt64IvarWithFieldInternal(context->result, field, val,
+                                      context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Fixed64, uint64_t, UInt64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamFixed64(GPBFieldDescriptor *field,
+                                                    void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt64Array *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        uint64_t val = GPBCodedInputStreamReadFixed64(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      uint64_t val = GPBCodedInputStreamReadFixed64(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    uint64_t val = GPBCodedInputStreamReadFixed64(state);
+    GPBSetUInt64IvarWithFieldInternal(context->result, field, val,
+                                      context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Float, float, Float)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamFloat(GPBFieldDescriptor *field,
+                                                  void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBFloatArray *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        float val = GPBCodedInputStreamReadFloat(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      float val = GPBCodedInputStreamReadFloat(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    float val = GPBCodedInputStreamReadFloat(state);
+    GPBSetFloatIvarWithFieldInternal(context->result, field, val,
+                                     context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_POD_FUNC(Double, double, Double)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamDouble(GPBFieldDescriptor *field,
+                                                   void *voidContext) {
+  MergeFromCodedInputStreamContext *context =
+      (MergeFromCodedInputStreamContext *)voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBDoubleArray *array =
+        GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+    if (field.isPackable) {
+      int32_t length = GPBCodedInputStreamReadInt32(state);
+      size_t limit = GPBCodedInputStreamPushLimit(state, length);
+      while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+        double val = GPBCodedInputStreamReadDouble(state);
+        [array addValue:val];
+      }
+      GPBCodedInputStreamPopLimit(state, limit);
+    } else {
+      double val = GPBCodedInputStreamReadDouble(state);
+      [array addValue:val];
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    double val = GPBCodedInputStreamReadDouble(state);
+    GPBSetDoubleIvarWithFieldInternal(context->result, field, val,
+                                      context->syntax);
+  } else {  // fieldType == GPBFieldTypeMap
+    // The exact type doesn't matter, just need to know it is a GPB*Dictionary.
+    GPBInt32Int32Dictionary *map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_OBJ_FUNC(String)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamString(GPBFieldDescriptor *field,
+                                                   void *voidContext) {
+  MergeFromCodedInputStreamContext *context = voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeMap) {
+    // GPB*Dictionary or NSDictionary, exact type doesn't matter at this point.
+    id map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  } else {
+    id val = GPBCodedInputStreamReadRetainedString(state);
+    if (fieldType == GPBFieldTypeRepeated) {
+      NSMutableArray *array =
+          GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+      [array addObject:val];
+      [val release];
+    } else {  // fieldType == GPBFieldTypeSingle
+      GPBSetRetainedObjectIvarWithFieldInternal(context->result, field, val,
+                                                context->syntax);
+    }
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND MERGE_FROM_CODED_INPUT_STREAM_OBJ_FUNC(Data)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicMergeFromCodedInputStreamData(GPBFieldDescriptor *field,
+                                                 void *voidContext) {
+  MergeFromCodedInputStreamContext *context = voidContext;
+  GPBCodedInputStreamState *state = &context->stream->state_;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeMap) {
+    // GPB*Dictionary or NSDictionary, exact type doesn't matter at this point.
+    id map =
+        GetOrCreateMapIvarWithField(context->result, field, context->syntax);
+    [context->stream readMapEntry:map
+                extensionRegistry:nil
+                            field:field
+                    parentMessage:context->result];
+  } else {
+    id val = GPBCodedInputStreamReadRetainedData(state);
+    if (fieldType == GPBFieldTypeRepeated) {
+      NSMutableArray *array =
+          GetOrCreateArrayIvarWithField(context->result, field, context->syntax);
+      [array addObject:val];
+      [val release];
+    } else {  // fieldType == GPBFieldTypeSingle
+      GPBSetRetainedObjectIvarWithFieldInternal(context->result, field, val,
+                                                context->syntax);
+    }
+  }
+  return NO;
+}
+
+//%PDDM-EXPAND-END (15 expansions)
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
+                extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBFileSyntax syntax = descriptor.file.syntax;
+  MergeFromCodedInputStreamContext context = {
+    self, input, self, extensionRegistry, 0, syntax
+  };
+  GPBApplyStrictFunctions funcs =
+      GPBAPPLY_STRICT_FUNCTIONS_INIT(DynamicMergeFromCodedInputStream);
+  NSUInteger startingIndex = 0;
+  NSArray *fields = descriptor->fields_;
+  NSUInteger count = fields.count;
+  while (YES) {
+    BOOL merged = NO;
+    context.tag = GPBCodedInputStreamReadTag(&input->state_);
+    for (NSUInteger i = 0; i < count; ++i) {
+      if (startingIndex >= count) startingIndex = 0;
+      GPBFieldDescriptor *fieldDescriptor = fields[startingIndex];
+      if (GPBFieldTag(fieldDescriptor) == context.tag) {
+        GPBApplyFunction function = funcs[GPBGetFieldType(fieldDescriptor)];
+        function(fieldDescriptor, &context);
+        merged = YES;
+        break;
+      } else {
+        startingIndex += 1;
+      }
+    }
+    if (!merged) {
+      if (context.tag == 0) {
+        // zero signals EOF / limit reached
+        return;
+      } else {
+        if (GPBPreserveUnknownFields(syntax)) {
+          if (![self parseUnknownField:input
+                     extensionRegistry:extensionRegistry
+                                   tag:context.tag]) {
+            // it's an endgroup tag
+            return;
+          }
+        } else {
+          if (![input skipField:context.tag]) {
+            return;
+          }
+        }
+      }
+    }
+  }
+}
+
+#pragma mark - MergeFrom Support
+
+typedef struct MergeFromContext {
+  GPBMessage *other;
+  GPBMessage *result;
+  GPBFileSyntax syntax;
+} MergeFromContext;
+
+//%PDDM-DEFINE GPB_MERGE_FROM_FUNC(NAME)
+//%static BOOL MergeFrom##NAME(GPBFieldDescriptor *field, void *voidContext) {
+//%  MergeFromContext *context = (MergeFromContext *)voidContext;
+//%  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+//%  if (otherHas) {
+//%    GPBSet##NAME##IvarWithFieldInternal(
+//%        context->result, field, GPBGet##NAME##IvarWithField(context->other, field),
+//%        context->syntax);
+//%  }
+//%  return YES;
+//%}
+//%
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(Bool)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromBool(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetBoolIvarWithFieldInternal(
+        context->result, field, GPBGetBoolIvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromInt32(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetInt32IvarWithFieldInternal(
+        context->result, field, GPBGetInt32IvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(UInt32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromUInt32(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetUInt32IvarWithFieldInternal(
+        context->result, field, GPBGetUInt32IvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromInt64(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetInt64IvarWithFieldInternal(
+        context->result, field, GPBGetInt64IvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(UInt64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromUInt64(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetUInt64IvarWithFieldInternal(
+        context->result, field, GPBGetUInt64IvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(Float)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromFloat(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetFloatIvarWithFieldInternal(
+        context->result, field, GPBGetFloatIvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND GPB_MERGE_FROM_FUNC(Double)
+// This block of code is generated, do not edit it directly.
+
+static BOOL MergeFromDouble(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  BOOL otherHas = GPBGetHasIvarField(context->other, field);
+  if (otherHas) {
+    GPBSetDoubleIvarWithFieldInternal(
+        context->result, field, GPBGetDoubleIvarWithField(context->other, field),
+        context->syntax);
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND-END (7 expansions)
+
+static BOOL MergeFromObject(GPBFieldDescriptor *field, void *voidContext) {
+  MergeFromContext *context = (MergeFromContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    // In the case of a list, they need to be appended, and there is no
+    // _hasIvar to worry about setting.
+    id otherArray =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->other, field);
+    if (otherArray) {
+      GPBType fieldDataType = field->description_->type;
+      if (GPBTypeIsObject(fieldDataType)) {
+        NSMutableArray *resultArray = GetOrCreateArrayIvarWithField(
+            context->result, field, context->syntax);
+        [resultArray addObjectsFromArray:otherArray];
+      } else if (fieldDataType == GPBTypeEnum) {
+        GPBEnumArray *resultArray = GetOrCreateArrayIvarWithField(
+            context->result, field, context->syntax);
+        [resultArray addRawValuesFromArray:otherArray];
+      } else {
+        // The array type doesn't matter, that all implment
+        // -addValuesFromArray:.
+        GPBInt32Array *resultArray = GetOrCreateArrayIvarWithField(
+            context->result, field, context->syntax);
+        [resultArray addValuesFromArray:otherArray];
+      }
+    }
+    return YES;
+  }
+  if (fieldType == GPBFieldTypeMap) {
+    // In the case of a map, they need to be merged, and there is no
+    // _hasIvar to worry about setting.
+    id otherDict = GPBGetObjectIvarWithFieldNoAutocreate(context->other, field);
+    if (otherDict) {
+      GPBType keyType = field.mapKeyType;
+      GPBType valueType = field->description_->type;
+      if (GPBTypeIsObject(keyType) && GPBTypeIsObject(valueType)) {
+        NSMutableDictionary *resultDict = GetOrCreateMapIvarWithField(
+            context->result, field, context->syntax);
+        [resultDict addEntriesFromDictionary:otherDict];
+      } else if (valueType == GPBTypeEnum) {
+        // The exact type doesn't matter, just need to know it is a
+        // GPB*EnumDictionary.
+        GPBInt32EnumDictionary *resultDict = GetOrCreateMapIvarWithField(
+            context->result, field, context->syntax);
+        [resultDict addRawEntriesFromDictionary:otherDict];
+      } else {
+        // The exact type doesn't matter, they all implement
+        // -addEntriesFromDictionary:.
+        GPBInt32Int32Dictionary *resultDict = GetOrCreateMapIvarWithField(
+            context->result, field, context->syntax);
+        [resultDict addEntriesFromDictionary:otherDict];
+      }
+    }
+    return YES;
+  }
+
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNumber = GPBFieldNumber(field);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNumber);
+  if (!otherHas) {
+    return YES;
+  }
+  // GPBGetObjectIvarWithFieldNoAutocreate skips the has check, faster.
+  id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(context->other, field);
+  if (GPBFieldTypeIsMessage(field)) {
+    if (GPBGetHasIvar(context->result, hasIndex, fieldNumber)) {
+      GPBMessage *message =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->result, field);
+      [message mergeFrom:otherVal];
+    } else {
+      GPBMessage *message = [otherVal copy];
+      GPBSetRetainedObjectIvarWithFieldInternal(context->result, field, message,
+                                                context->syntax);
+    }
+  } else {
+    GPBSetObjectIvarWithFieldInternal(context->result, field, otherVal,
+                                      context->syntax);
+  }
+  return YES;
+}
+
+- (void)mergeFrom:(GPBMessage *)other {
+  Class selfClass = [self class];
+  Class otherClass = [other class];
+  if (!([selfClass isSubclassOfClass:otherClass] ||
+        [otherClass isSubclassOfClass:selfClass])) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Classes must match %@ != %@", selfClass, otherClass];
+  }
+
+  GPBApplyFunctions funcs = GPBAPPLY_FUNCTIONS_INIT(MergeFrom);
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  MergeFromContext context = {other, self, syntax};
+  GPBApplyFunctionsToMessageFields(&funcs, self, &context);
+
+  // We assume someting got done, and become visible.
+  GPBBecomeVisibleToAutocreator(self);
+
+  // Unknown fields.
+  if (!unknownFields_) {
+    [self setUnknownFields:context.other.unknownFields];
+  } else {
+    [unknownFields_ mergeUnknownFields:context.other.unknownFields];
+  }
+
+  if (other->extensionMap_.count == 0) {
+    return;
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ =
+        CloneExtensionMap(other->extensionMap_, NSZoneFromPointer(self));
+  } else {
+    for (GPBExtensionField *thisField in other->extensionMap_) {
+      id otherValue = [other->extensionMap_ objectForKey:thisField];
+      id value = [extensionMap_ objectForKey:thisField];
+      GPBExtensionDescriptor *thisFieldDescriptor = thisField.descriptor;
+      BOOL isMessageExtension = GPBExtensionIsMessage(thisFieldDescriptor);
+
+      if ([thisField isRepeated]) {
+        NSMutableArray *list = value;
+        if (list == nil) {
+          list = [[NSMutableArray alloc] init];
+          [extensionMap_ setObject:list forKey:thisField];
+          [list release];
+        }
+        if (isMessageExtension) {
+          for (GPBMessage *otherListValue in otherValue) {
+            GPBMessage *copiedValue = [otherListValue copy];
+            [list addObject:copiedValue];
+            [copiedValue release];
+          }
+        } else {
+          [list addObjectsFromArray:otherValue];
+        }
+      } else {
+        if (isMessageExtension) {
+          if (value) {
+            [(GPBMessage *)value mergeFrom:(GPBMessage *)otherValue];
+          } else {
+            GPBMessage *copiedValue = [otherValue copy];
+            [extensionMap_ setObject:copiedValue forKey:thisField];
+            [copiedValue release];
+          }
+        } else {
+          [extensionMap_ setObject:otherValue forKey:thisField];
+        }
+      }
+
+      if (isMessageExtension && !thisFieldDescriptor.isRepeated) {
+        GPBMessage *autocreatedValue =
+            [[autocreatedExtensionMap_ objectForKey:thisField] retain];
+        // Must remove from the map before calling GPBClearMessageAutocreator()
+        // so that GPBClearMessageAutocreator() knows its safe to clear.
+        [autocreatedExtensionMap_ removeObjectForKey:thisField];
+        GPBClearMessageAutocreator(autocreatedValue);
+        [autocreatedValue release];
+      }
+    }
+  }
+}
+
+#pragma mark - IsEqual Support
+
+typedef struct IsEqualContext {
+  GPBMessage *other;
+  GPBMessage *self;
+  BOOL outIsEqual;
+} IsEqualContext;
+
+// If both self and other "has" a value then compare.
+//%PDDM-DEFINE IS_EQUAL_FUNC(NAME, TYPE)
+//%static BOOL IsEqual##NAME(GPBFieldDescriptor *field, void *voidContext) {
+//%  IsEqualContext *context = (IsEqualContext *)voidContext;
+//%  int32_t hasIndex = GPBFieldHasIndex(field);
+//%  uint32_t fieldNum = GPBFieldNumber(field);
+//%  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+//%  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+//%  if (selfHas != otherHas) {
+//%    context->outIsEqual = NO;
+//%    return NO;
+//%  }
+//%  if (!selfHas) {
+//%    return YES;
+//%  }
+//%  TYPE selfVal = GPBGet##NAME##IvarWithField(context->self, field);
+//%  TYPE otherVal = GPBGet##NAME##IvarWithField(context->other, field);
+//%  if (selfVal != otherVal) {
+//%    context->outIsEqual = NO;
+//%    return NO;
+//%  }
+//%  return YES;
+//%}
+//%
+//%PDDM-EXPAND IS_EQUAL_FUNC(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualBool(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  BOOL selfVal = GPBGetBoolIvarWithField(context->self, field);
+  BOOL otherVal = GPBGetBoolIvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND IS_EQUAL_FUNC(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  int32_t selfVal = GPBGetInt32IvarWithField(context->self, field);
+  int32_t otherVal = GPBGetInt32IvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND IS_EQUAL_FUNC(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualUInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  uint32_t selfVal = GPBGetUInt32IvarWithField(context->self, field);
+  uint32_t otherVal = GPBGetUInt32IvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND IS_EQUAL_FUNC(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  int64_t selfVal = GPBGetInt64IvarWithField(context->self, field);
+  int64_t otherVal = GPBGetInt64IvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND IS_EQUAL_FUNC(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualUInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  uint64_t selfVal = GPBGetUInt64IvarWithField(context->self, field);
+  uint64_t otherVal = GPBGetUInt64IvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND IS_EQUAL_FUNC(Float, float)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualFloat(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  float selfVal = GPBGetFloatIvarWithField(context->self, field);
+  float otherVal = GPBGetFloatIvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND IS_EQUAL_FUNC(Double, double)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IsEqualDouble(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  double selfVal = GPBGetDoubleIvarWithField(context->self, field);
+  double otherVal = GPBGetDoubleIvarWithField(context->other, field);
+  if (selfVal != otherVal) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND-END (7 expansions)
+
+static BOOL IsEqualObject(GPBFieldDescriptor *field, void *voidContext) {
+  IsEqualContext *context = (IsEqualContext *)voidContext;
+  if (GPBFieldIsMapOrArray(field)) {
+    // In the case of a list/map, there is no _hasIvar to worry about checking.
+    // NOTE: These are NSArray/GPB*Array/NSDictionary/GPB*Dictionary, but the
+    // type doesn't really matter as the object just has to support
+    // -count/-isEqual:.
+    NSArray *resultMapOrArray =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSArray *otherMapOrArray =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->other, field);
+    context->outIsEqual =
+        (resultMapOrArray.count == 0 && otherMapOrArray.count == 0) ||
+        [resultMapOrArray isEqual:otherMapOrArray];
+    return context->outIsEqual;
+  }
+  int32_t hasIndex = GPBFieldHasIndex(field);
+  uint32_t fieldNum = GPBFieldNumber(field);
+  BOOL selfHas = GPBGetHasIvar(context->self, hasIndex, fieldNum);
+  BOOL otherHas = GPBGetHasIvar(context->other, hasIndex, fieldNum);
+  if (selfHas != otherHas) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  if (!selfHas) {
+    return YES;
+  }
+  // GPBGetObjectIvarWithFieldNoAutocreate skips the has check, faster.
+  NSObject *selfVal =
+      GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+  NSObject *otherVal =
+      GPBGetObjectIvarWithFieldNoAutocreate(context->other, field);
+
+  // This covers case where selfVal is set to nil, as well as shortcuts the call
+  // to isEqual: in common cases.
+  if (selfVal == otherVal) {
+    return YES;
+  }
+  if (![selfVal isEqual:otherVal]) {
+    context->outIsEqual = NO;
+    return NO;
+  }
+  return YES;
+}
+
+- (BOOL)isEqual:(GPBMessage *)other {
+  if (other == self) {
+    return YES;
+  }
+  if (![other isKindOfClass:[self class]] &&
+      ![self isKindOfClass:[other class]]) {
+    return NO;
+  }
+  GPBApplyFunctions funcs = GPBAPPLY_FUNCTIONS_INIT(IsEqual);
+  IsEqualContext context = {other, self, YES};
+  GPBApplyFunctionsToMessageFields(&funcs, self, &context);
+  if (!context.outIsEqual) {
+    return NO;
+  }
+
+  // nil and empty are equal
+  if (extensionMap_.count != 0 || other->extensionMap_.count != 0) {
+    if (![extensionMap_ isEqual:other->extensionMap_]) {
+      return NO;
+    }
+  }
+
+  // nil and empty are equal
+  GPBUnknownFieldSet *otherUnknowns = other.unknownFields;
+  if ([unknownFields_ countOfFields] != 0 ||
+      [otherUnknowns countOfFields] != 0) {
+    if (![unknownFields_ isEqual:otherUnknowns]) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+// It is very difficult to implement a generic hash for ProtoBuf messages that
+// will perform well. If you need hashing on your ProtoBufs (eg you are using
+// them as dictionary keys) you will probably want to implement a ProtoBuf
+// message specific hash as a category on your protobuf class. Do not make it a
+// category on GPBMessage as you will conflict with this hash, and will possibly
+// override hash for all generated protobufs. A good implementation of hash will
+// be really fast, so we would recommend only hashing protobufs that have an
+// identifier field of some kind that you can easily hash. If you implement
+// hash, we would strongly recommend overriding isEqual: in your category as
+// well, as the default implementation of isEqual: is extremely slow, and may
+// drastically affect performance in large sets.
+- (NSUInteger)hash {
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  const NSUInteger prime = 19;
+
+  // Start with the descriptor and then mix it with the field numbers that
+  // are set.  Hopefully that will give a spread based on classes and what
+  // fields are set.
+  NSUInteger result = (NSUInteger)descriptor;
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      // Exact type doesn't matter, just check if there are any elements.
+      NSArray *mapOrArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (mapOrArray.count) {
+        result = prime * result + GPBFieldNumber(field);
+      }
+    } else if (GPBGetHasIvarField(self, field)) {
+      result = prime * result + GPBFieldNumber(field);
+    }
+  }
+  return result;
+}
+
+#pragma mark - Description Support
+
+- (NSString *)description {
+  NSString *textFormat = GPBTextFormatForMessage(self, @"    ");
+  NSString *description = [NSString
+      stringWithFormat:@"<%@ %p>: {\n%@}", [self class], self, textFormat];
+  return description;
+}
+
+#if DEBUG
+
+// Xcode 5.1 added support for custom quick look info.
+// https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/CustomClassDisplay_in_QuickLook/CH01-quick_look_for_custom_objects/CH01-quick_look_for_custom_objects.html#//apple_ref/doc/uid/TP40014001-CH2-SW1
+- (id)debugQuickLookObject {
+  return GPBTextFormatForMessage(self, nil);
+}
+
+#endif  // DEBUG
+
+#pragma mark - SerializedSize Support
+
+// Serialized size is only calculated once, and then is stored into
+// memoizedSerializedSize.
+
+typedef struct SerializedSizeContext {
+  GPBMessage *self;
+  size_t outSize;
+} SerializedSizeContext;
+
+//%PDDM-DEFINE SERIALIZED_SIZE_POD_FUNC(NAME, TYPE, REAL_TYPE)
+//%SERIALIZED_SIZE_POD_FUNC_FULL(NAME, TYPE, REAL_TYPE, REAL_TYPE, )
+//%PDDM-DEFINE SERIALIZED_SIZE_POD_FUNC_FULL(NAME, TYPE, REAL_TYPE, ARRAY_TYPE, ARRAY_ACCESSOR_NAME)
+//%static BOOL DynamicSerializedSize##NAME(GPBFieldDescriptor *field,
+//%                                NAME$S  void *voidContext) {
+//%  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+//%  GPBFieldType fieldType = field.fieldType;
+//%  if (fieldType == GPBFieldTypeRepeated) {
+//%    GPB##ARRAY_TYPE##Array *array =
+//%        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+//%    NSUInteger count = array.count;
+//%    if (count == 0) return YES;
+//%    __block size_t dataSize = 0;
+//%    [array enumerate##ARRAY_ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%      #pragma unused(idx, stop)
+//%      dataSize += GPBCompute##NAME##SizeNoTag(value);
+//%    }];
+//%    context->outSize += dataSize;
+//%    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+//%    if (field.isPackable) {
+//%      context->outSize += tagSize;
+//%      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+//%    } else {
+//%      context->outSize += count * tagSize;
+//%    }
+//%  } else if (fieldType == GPBFieldTypeSingle) {
+//%    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+//%    if (selfHas) {
+//%      TYPE selfVal = GPBGet##REAL_TYPE##IvarWithField(context->self, field);
+//%      context->outSize += GPBCompute##NAME##Size(GPBFieldNumber(field), selfVal);
+//%    }
+//%  } else {  // fieldType == GPBFieldTypeMap
+//%    // Type will be GPB*##REAL_TYPE##Dictionary, exact type doesn't matter.
+//%    GPBInt32##REAL_TYPE##Dictionary *map =
+//%        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+//%    context->outSize += [map computeSerializedSizeAsField:field];
+//%  }
+//%  return YES;
+//%}
+//%
+//%PDDM-DEFINE SERIALIZED_SIZE_OBJECT_FUNC(NAME)
+//%static BOOL DynamicSerializedSize##NAME(GPBFieldDescriptor *field,
+//%                                NAME$S  void *voidContext) {
+//%  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+//%  GPBFieldType fieldType = field.fieldType;
+//%  if (fieldType == GPBFieldTypeRepeated) {
+//%    NSArray *array =
+//%        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+//%    for (id value in array) {
+//%      context->outSize += GPBCompute##NAME##SizeNoTag(value);
+//%    }
+//%    size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field),
+//%                                                 GPBGetFieldType(field));
+//%    context->outSize += array.count * tagSize;
+//%  } else if (fieldType == GPBFieldTypeSingle) {
+//%    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+//%    if (selfHas) {
+//%      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+//%      // again.
+//%      id selfVal = GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+//%      context->outSize += GPBCompute##NAME##Size(GPBFieldNumber(field), selfVal);
+//%    }
+//%  } else {  // fieldType == GPBFieldTypeMap
+//%    GPBType mapKeyType = field.mapKeyType;
+//%    if (mapKeyType == GPBTypeString) {
+//%      // If key type was string, then the map is an NSDictionary.
+//%      NSDictionary *map =
+//%          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+//%      context->outSize += GPBDictionaryComputeSizeInternalHelper(map, field);
+//%    } else {
+//%      // Type will be GPB*##NAME##Dictionary, exact type doesn't matter.
+//%      GPBInt32ObjectDictionary *map =
+//%          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+//%      context->outSize += [map computeSerializedSizeAsField:field];
+//%    }
+//%  }
+//%  return YES;
+//%}
+//%
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Bool, BOOL, Bool)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeBool(GPBFieldDescriptor *field,
+                                      void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBBoolArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeBoolSizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      BOOL selfVal = GPBGetBoolIvarWithField(context->self, field);
+      context->outSize += GPBComputeBoolSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*BoolDictionary, exact type doesn't matter.
+    GPBInt32BoolDictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Int32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeInt32(GPBFieldDescriptor *field,
+                                       void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt32Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeInt32SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int32_t selfVal = GPBGetInt32IvarWithField(context->self, field);
+      context->outSize += GPBComputeInt32Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int32Dictionary, exact type doesn't matter.
+    GPBInt32Int32Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(SInt32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeSInt32(GPBFieldDescriptor *field,
+                                        void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt32Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeSInt32SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int32_t selfVal = GPBGetInt32IvarWithField(context->self, field);
+      context->outSize += GPBComputeSInt32Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int32Dictionary, exact type doesn't matter.
+    GPBInt32Int32Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(SFixed32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeSFixed32(GPBFieldDescriptor *field,
+                                          void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt32Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeSFixed32SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int32_t selfVal = GPBGetInt32IvarWithField(context->self, field);
+      context->outSize += GPBComputeSFixed32Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int32Dictionary, exact type doesn't matter.
+    GPBInt32Int32Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC_FULL(Enum, int32_t, Int32, Enum, Raw)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeEnum(GPBFieldDescriptor *field,
+                                      void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBEnumArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeEnumSizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int32_t selfVal = GPBGetInt32IvarWithField(context->self, field);
+      context->outSize += GPBComputeEnumSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int32Dictionary, exact type doesn't matter.
+    GPBInt32Int32Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(UInt32, uint32_t, UInt32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeUInt32(GPBFieldDescriptor *field,
+                                        void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt32Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeUInt32SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      uint32_t selfVal = GPBGetUInt32IvarWithField(context->self, field);
+      context->outSize += GPBComputeUInt32Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*UInt32Dictionary, exact type doesn't matter.
+    GPBInt32UInt32Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Fixed32, uint32_t, UInt32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeFixed32(GPBFieldDescriptor *field,
+                                         void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt32Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeFixed32SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      uint32_t selfVal = GPBGetUInt32IvarWithField(context->self, field);
+      context->outSize += GPBComputeFixed32Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*UInt32Dictionary, exact type doesn't matter.
+    GPBInt32UInt32Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Int64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeInt64(GPBFieldDescriptor *field,
+                                       void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt64Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeInt64SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int64_t selfVal = GPBGetInt64IvarWithField(context->self, field);
+      context->outSize += GPBComputeInt64Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int64Dictionary, exact type doesn't matter.
+    GPBInt32Int64Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(SFixed64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeSFixed64(GPBFieldDescriptor *field,
+                                          void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt64Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeSFixed64SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int64_t selfVal = GPBGetInt64IvarWithField(context->self, field);
+      context->outSize += GPBComputeSFixed64Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int64Dictionary, exact type doesn't matter.
+    GPBInt32Int64Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(SInt64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeSInt64(GPBFieldDescriptor *field,
+                                        void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBInt64Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeSInt64SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      int64_t selfVal = GPBGetInt64IvarWithField(context->self, field);
+      context->outSize += GPBComputeSInt64Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*Int64Dictionary, exact type doesn't matter.
+    GPBInt32Int64Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(UInt64, uint64_t, UInt64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeUInt64(GPBFieldDescriptor *field,
+                                        void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt64Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeUInt64SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      uint64_t selfVal = GPBGetUInt64IvarWithField(context->self, field);
+      context->outSize += GPBComputeUInt64Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*UInt64Dictionary, exact type doesn't matter.
+    GPBInt32UInt64Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Fixed64, uint64_t, UInt64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeFixed64(GPBFieldDescriptor *field,
+                                         void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBUInt64Array *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeFixed64SizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      uint64_t selfVal = GPBGetUInt64IvarWithField(context->self, field);
+      context->outSize += GPBComputeFixed64Size(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*UInt64Dictionary, exact type doesn't matter.
+    GPBInt32UInt64Dictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Float, float, Float)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeFloat(GPBFieldDescriptor *field,
+                                       void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBFloatArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeFloatSizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      float selfVal = GPBGetFloatIvarWithField(context->self, field);
+      context->outSize += GPBComputeFloatSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*FloatDictionary, exact type doesn't matter.
+    GPBInt32FloatDictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_POD_FUNC(Double, double, Double)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeDouble(GPBFieldDescriptor *field,
+                                        void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    GPBDoubleArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    NSUInteger count = array.count;
+    if (count == 0) return YES;
+    __block size_t dataSize = 0;
+    [array enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+      #pragma unused(idx, stop)
+      dataSize += GPBComputeDoubleSizeNoTag(value);
+    }];
+    context->outSize += dataSize;
+    size_t tagSize = GPBComputeTagSize(GPBFieldNumber(field));
+    if (field.isPackable) {
+      context->outSize += tagSize;
+      context->outSize += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+    } else {
+      context->outSize += count * tagSize;
+    }
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      double selfVal = GPBGetDoubleIvarWithField(context->self, field);
+      context->outSize += GPBComputeDoubleSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    // Type will be GPB*DoubleDictionary, exact type doesn't matter.
+    GPBInt32DoubleDictionary *map =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    context->outSize += [map computeSerializedSizeAsField:field];
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_OBJECT_FUNC(String)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeString(GPBFieldDescriptor *field,
+                                        void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    NSArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    for (id value in array) {
+      context->outSize += GPBComputeStringSizeNoTag(value);
+    }
+    size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field),
+                                                 GPBGetFieldType(field));
+    context->outSize += array.count * tagSize;
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+      // again.
+      id selfVal = GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBComputeStringSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    GPBType mapKeyType = field.mapKeyType;
+    if (mapKeyType == GPBTypeString) {
+      // If key type was string, then the map is an NSDictionary.
+      NSDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBDictionaryComputeSizeInternalHelper(map, field);
+    } else {
+      // Type will be GPB*StringDictionary, exact type doesn't matter.
+      GPBInt32ObjectDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += [map computeSerializedSizeAsField:field];
+    }
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_OBJECT_FUNC(Data)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeData(GPBFieldDescriptor *field,
+                                      void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    NSArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    for (id value in array) {
+      context->outSize += GPBComputeDataSizeNoTag(value);
+    }
+    size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field),
+                                                 GPBGetFieldType(field));
+    context->outSize += array.count * tagSize;
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+      // again.
+      id selfVal = GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBComputeDataSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    GPBType mapKeyType = field.mapKeyType;
+    if (mapKeyType == GPBTypeString) {
+      // If key type was string, then the map is an NSDictionary.
+      NSDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBDictionaryComputeSizeInternalHelper(map, field);
+    } else {
+      // Type will be GPB*DataDictionary, exact type doesn't matter.
+      GPBInt32ObjectDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += [map computeSerializedSizeAsField:field];
+    }
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_OBJECT_FUNC(Message)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeMessage(GPBFieldDescriptor *field,
+                                         void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    NSArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    for (id value in array) {
+      context->outSize += GPBComputeMessageSizeNoTag(value);
+    }
+    size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field),
+                                                 GPBGetFieldType(field));
+    context->outSize += array.count * tagSize;
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+      // again.
+      id selfVal = GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBComputeMessageSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    GPBType mapKeyType = field.mapKeyType;
+    if (mapKeyType == GPBTypeString) {
+      // If key type was string, then the map is an NSDictionary.
+      NSDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBDictionaryComputeSizeInternalHelper(map, field);
+    } else {
+      // Type will be GPB*MessageDictionary, exact type doesn't matter.
+      GPBInt32ObjectDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += [map computeSerializedSizeAsField:field];
+    }
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND SERIALIZED_SIZE_OBJECT_FUNC(Group)
+// This block of code is generated, do not edit it directly.
+
+static BOOL DynamicSerializedSizeGroup(GPBFieldDescriptor *field,
+                                       void *voidContext) {
+  SerializedSizeContext *context = (SerializedSizeContext *)voidContext;
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeRepeated) {
+    NSArray *array =
+        GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+    for (id value in array) {
+      context->outSize += GPBComputeGroupSizeNoTag(value);
+    }
+    size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field),
+                                                 GPBGetFieldType(field));
+    context->outSize += array.count * tagSize;
+  } else if (fieldType == GPBFieldTypeSingle) {
+    BOOL selfHas = GPBGetHasIvarField(context->self, field);
+    if (selfHas) {
+      // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+      // again.
+      id selfVal = GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBComputeGroupSize(GPBFieldNumber(field), selfVal);
+    }
+  } else {  // fieldType == GPBFieldTypeMap
+    GPBType mapKeyType = field.mapKeyType;
+    if (mapKeyType == GPBTypeString) {
+      // If key type was string, then the map is an NSDictionary.
+      NSDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += GPBDictionaryComputeSizeInternalHelper(map, field);
+    } else {
+      // Type will be GPB*GroupDictionary, exact type doesn't matter.
+      GPBInt32ObjectDictionary *map =
+          GPBGetObjectIvarWithFieldNoAutocreate(context->self, field);
+      context->outSize += [map computeSerializedSizeAsField:field];
+    }
+  }
+  return YES;
+}
+
+//%PDDM-EXPAND-END (18 expansions)
+
+- (size_t)serializedSize {
+  // Fields.
+  SerializedSizeContext context = {self, 0};
+  GPBApplyStrictFunctions funcs =
+      GPBAPPLY_STRICT_FUNCTIONS_INIT(DynamicSerializedSize);
+  GPBApplyStrictFunctionsToMessageFields(&funcs, self, &context);
+  // Add any unknown fields.
+  const GPBDescriptor *descriptor = [self descriptor];
+  if (descriptor.wireFormat) {
+    context.outSize += [unknownFields_ serializedSizeAsMessageSet];
+  } else {
+    context.outSize += [unknownFields_ serializedSize];
+  }
+  // Add any extensions.
+  for (GPBExtensionField *extension in extensionMap_) {
+    id value = [extensionMap_ objectForKey:extension];
+    context.outSize += [extension computeSerializedSizeIncludingTag:value];
+  }
+
+  return context.outSize;
+}
+
+#pragma mark - Resolve Methods Support
+
+typedef struct IvarAccessorMethodContext {
+  GPBFileSyntax syntax;
+  IMP impToAdd;
+  SEL encodingSelector;
+} IvarAccessorMethodContext;
+
+//%PDDM-DEFINE IVAR_ACCESSOR_FUNC_COMMON(NAME, TYPE, TRUE_NAME)
+//%static BOOL IvarGet##NAME(GPBFieldDescriptor *field, void *voidContext) {
+//%  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+//%  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+//%    return GPBGet##TRUE_NAME##IvarWithField(obj, field);
+//%  });
+//%  context->encodingSelector = @selector(get##NAME);
+//%  return NO;
+//%}
+//%
+//%PDDM-DEFINE IVAR_ACCESSOR_FUNC_OBJECT(NAME, TYPE)
+//%IVAR_ACCESSOR_FUNC_COMMON(NAME, TYPE, Object)
+//%static BOOL IvarSet##NAME(GPBFieldDescriptor *field, void *voidContext) {
+//%  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+//%  // Local for syntax so the block doesn't capture context and use random
+//%  // memory in the future.
+//%  const GPBFileSyntax syntax = context->syntax;
+//%  context->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) {
+//%    return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+//%  });
+//%  context->encodingSelector = @selector(set##NAME:);
+//%  return NO;
+//%}
+//%
+//%PDDM-DEFINE IVAR_ACCESSOR_FUNC_PER_VERSION(NAME, TYPE)
+//%IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(NAME, TYPE, NAME)
+//%PDDM-DEFINE IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(NAME, TYPE, TRUE_NAME)
+//%IVAR_ACCESSOR_FUNC_COMMON(NAME, TYPE, TRUE_NAME)
+//%static BOOL IvarSet##NAME(GPBFieldDescriptor *field, void *voidContext) {
+//%  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+//%  // Local for syntax so the block doesn't capture context and use random
+//%  // memory in the future.
+//%  const GPBFileSyntax syntax = context->syntax;
+//%  context->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) {
+//%    return GPBSet##TRUE_NAME##IvarWithFieldInternal(obj, field, value, syntax);
+//%  });
+//%  context->encodingSelector = @selector(set##NAME:);
+//%  return NO;
+//%}
+//%
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetBool(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetBoolIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getBool);
+  return NO;
+}
+
+static BOOL IvarSetBool(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, BOOL value) {
+    return GPBSetBoolIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setBool:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetInt32IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getInt32);
+  return NO;
+}
+
+static BOOL IvarSetInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int32_t value) {
+    return GPBSetInt32IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setInt32:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(SInt32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetSInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetInt32IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getSInt32);
+  return NO;
+}
+
+static BOOL IvarSetSInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int32_t value) {
+    return GPBSetInt32IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setSInt32:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(SFixed32, int32_t, Int32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetSFixed32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetInt32IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getSFixed32);
+  return NO;
+}
+
+static BOOL IvarSetSFixed32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int32_t value) {
+    return GPBSetInt32IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setSFixed32:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetUInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetUInt32IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getUInt32);
+  return NO;
+}
+
+static BOOL IvarSetUInt32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, uint32_t value) {
+    return GPBSetUInt32IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setUInt32:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(Fixed32, uint32_t, UInt32)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetFixed32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetUInt32IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getFixed32);
+  return NO;
+}
+
+static BOOL IvarSetFixed32(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, uint32_t value) {
+    return GPBSetUInt32IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setFixed32:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetInt64IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getInt64);
+  return NO;
+}
+
+static BOOL IvarSetInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int64_t value) {
+    return GPBSetInt64IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setInt64:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(SFixed64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetSFixed64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetInt64IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getSFixed64);
+  return NO;
+}
+
+static BOOL IvarSetSFixed64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int64_t value) {
+    return GPBSetInt64IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setSFixed64:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(SInt64, int64_t, Int64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetSInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetInt64IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getSInt64);
+  return NO;
+}
+
+static BOOL IvarSetSInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int64_t value) {
+    return GPBSetInt64IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setSInt64:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetUInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetUInt64IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getUInt64);
+  return NO;
+}
+
+static BOOL IvarSetUInt64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, uint64_t value) {
+    return GPBSetUInt64IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setUInt64:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION_ALIASED(Fixed64, uint64_t, UInt64)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetFixed64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetUInt64IvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getFixed64);
+  return NO;
+}
+
+static BOOL IvarSetFixed64(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, uint64_t value) {
+    return GPBSetUInt64IvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setFixed64:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(Float, float)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetFloat(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetFloatIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getFloat);
+  return NO;
+}
+
+static BOOL IvarSetFloat(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, float value) {
+    return GPBSetFloatIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setFloat:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_PER_VERSION(Double, double)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetDouble(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetDoubleIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getDouble);
+  return NO;
+}
+
+static BOOL IvarSetDouble(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, double value) {
+    return GPBSetDoubleIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setDouble:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_OBJECT(String, id)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetString(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetObjectIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getString);
+  return NO;
+}
+
+static BOOL IvarSetString(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, id value) {
+    return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setString:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_OBJECT(Data, id)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetData(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetObjectIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getData);
+  return NO;
+}
+
+static BOOL IvarSetData(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, id value) {
+    return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setData:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_OBJECT(Message, id)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetMessage(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetObjectIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getMessage);
+  return NO;
+}
+
+static BOOL IvarSetMessage(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, id value) {
+    return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setMessage:);
+  return NO;
+}
+
+//%PDDM-EXPAND IVAR_ACCESSOR_FUNC_OBJECT(Group, id)
+// This block of code is generated, do not edit it directly.
+
+static BOOL IvarGetGroup(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetObjectIvarWithField(obj, field);
+  });
+  context->encodingSelector = @selector(getGroup);
+  return NO;
+}
+
+static BOOL IvarSetGroup(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, id value) {
+    return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setGroup:);
+  return NO;
+}
+
+//%PDDM-EXPAND-END (17 expansions)
+
+// Enum gets custom hooks because get needs the syntax to Get.
+
+static BOOL IvarGetEnum(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj) {
+    return GPBGetEnumIvarWithFieldInternal(obj, field, syntax);
+  });
+  context->encodingSelector = @selector(getEnum);
+  return NO;
+}
+
+static BOOL IvarSetEnum(GPBFieldDescriptor *field, void *voidContext) {
+  IvarAccessorMethodContext *context = (IvarAccessorMethodContext *)voidContext;
+  // Local for syntax so the block doesn't capture context and use random
+  // memory in the future.
+  const GPBFileSyntax syntax = context->syntax;
+  context->impToAdd = imp_implementationWithBlock(^(id obj, int32_t value) {
+    return GPBSetEnumIvarWithFieldInternal(obj, field, value, syntax);
+  });
+  context->encodingSelector = @selector(setEnum:);
+  return NO;
+}
+
++ (BOOL)resolveInstanceMethod:(SEL)sel {
+  const GPBDescriptor *descriptor = [self descriptor];
+  if (!descriptor) {
+    return NO;
+  }
+
+  // NOTE: hasSel_/setHasSel_ will be NULL if the field for the given message
+  // should not have has support (done in GPBDescriptor.m), so there is no need
+  // for checks here to see if has*/setHas* are allowed.
+
+  IvarAccessorMethodContext context = {descriptor.file.syntax, NULL, NULL};
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    BOOL isMapOrArray = GPBFieldIsMapOrArray(field);
+    if (!isMapOrArray) {
+      if (sel == field->getSel_) {
+        static const GPBApplyStrictFunctions funcs =
+            GPBAPPLY_STRICT_FUNCTIONS_INIT(IvarGet);
+        funcs[GPBGetFieldType(field)](field, &context);
+        break;
+      } else if (sel == field->hasSel_) {
+        int32_t index = GPBFieldHasIndex(field);
+        uint32_t fieldNum = GPBFieldNumber(field);
+        context.impToAdd = imp_implementationWithBlock(^(id obj) {
+          return GPBGetHasIvar(obj, index, fieldNum);
+        });
+        context.encodingSelector = @selector(getBool);
+        break;
+      } else if (sel == field->setHasSel_) {
+        context.impToAdd = imp_implementationWithBlock(^(id obj, BOOL value) {
+          if (value) {
+            [NSException raise:NSInvalidArgumentException
+                        format:@"has fields can only be set to NO"];
+          }
+          GPBClearMessageField(obj, field);
+        });
+        context.encodingSelector = @selector(setBool:);
+        break;
+      } else if (sel == field->setSel_) {
+        GPBApplyStrictFunctions funcs = GPBAPPLY_STRICT_FUNCTIONS_INIT(IvarSet);
+        funcs[GPBGetFieldType(field)](field, &context);
+        break;
+      } else {
+        GPBOneofDescriptor *oneof = field->containingOneof_;
+        if (oneof && (sel == oneof->caseSel_)) {
+          int32_t index = oneof->oneofDescription_->index;
+          context.impToAdd = imp_implementationWithBlock(^(id obj) {
+            return GPBGetHasOneof(obj, index);
+          });
+          context.encodingSelector = @selector(getEnum);
+          break;
+        }
+      }
+    } else {
+      if (sel == field->getSel_) {
+        context.impToAdd = imp_implementationWithBlock(^(id obj) {
+          return GetArrayIvarWithField(obj, field);
+        });
+        context.encodingSelector = @selector(getArray);
+        break;
+      } else if (sel == field->setSel_) {
+        // Local for syntax so the block doesn't capture context and use random
+        // memory in the future.
+        const GPBFileSyntax syntax = context.syntax;
+        context.impToAdd = imp_implementationWithBlock(^(id obj, id value) {
+          return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+        });
+        context.encodingSelector = @selector(setArray:);
+        break;
+      }
+    }
+  }
+  if (context.impToAdd) {
+    const char *encoding =
+        GPBMessageEncodingForSelector(context.encodingSelector, YES);
+    BOOL methodAdded = class_addMethod(descriptor.messageClass, sel,
+                                       context.impToAdd, encoding);
+    return methodAdded;
+  }
+  return [super resolveInstanceMethod:sel];
+}
+
+#pragma mark - NSCoding Support
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+  self = [self init];
+  if (self) {
+    [self mergeFromData:[aDecoder decodeDataObject] extensionRegistry:nil];
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+  [aCoder encodeDataObject:[self data]];
+}
+
+#pragma mark - KVC Support
+
++ (BOOL)accessInstanceVariablesDirectly {
+  // Make sure KVC doesn't use instance variables.
+  return NO;
+}
+
+@end

+ 124 - 0
objectivec/GPBMessage_PackagePrivate.h

@@ -0,0 +1,124 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This header is private to the ProtobolBuffers library and must NOT be
+// included by any sources outside this library. The contents of this file are
+// subject to change at any time without notice.
+
+#import "GPBMessage.h"
+
+#import <libkern/OSAtomic.h>
+
+#import "GPBBootstrap.h"
+
+typedef struct GPBMessage_Storage {
+  uint32_t _has_storage_[0];
+} GPBMessage_Storage;
+
+typedef struct GPBMessage_Storage *GPBMessage_StoragePtr;
+
+@interface GPBMessage () {
+ @package
+  // NOTE: Because of the +allocWithZone code using NSAllocateObject(),
+  // this structure should ideally always be kept pointer aligned where the
+  // real storage starts is also pointer aligned. The compiler/runtime already
+  // do this, but it may not be documented.
+
+  // A pointer to the actual fields of the subclasses. The actual structure
+  // pointed to by this pointer will depend on the subclass.
+  // All of the actual structures will start the same as
+  // GPBMessage_Storage with _has_storage__ as the first field.
+  // Kept public because static functions need to access it.
+  GPBMessage_StoragePtr messageStorage_;
+
+  // A lock to provide mutual exclusion from internal data that can be modified
+  // by *read* operations such as getters (autocreation of message fields and
+  // message extensions, not setting of values). Used to guarantee thread safety
+  // for concurrent reads on the message.
+  OSSpinLock readOnlyMutex_;
+}
+
+// Gets an extension value without autocreating the result if not found. (i.e.
+// returns nil if the extension is not set)
+- (id)getExistingExtension:(GPBExtensionField *)extension;
+
+// Returns an array of GPBExtensionField* for all the extensions currently
+// in use on the message.  They are sorted by field number.
+- (NSArray *)sortedExtensionsInUse;
+
+// Parses a message of this type from the input and merges it with this
+// message.
+//
+// Warning:  This does not verify that all required fields are present in
+// the input message.
+// Note:  The caller should call
+// -[CodedInputStream checkLastTagWas:] after calling this to
+// verify that the last tag seen was the appropriate end-group tag,
+// or zero for EOF.
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
+                extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+
+// Parses the next delimited message of this type from the input and merges it
+// with this message.
+- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                         extensionRegistry:
+                             (GPBExtensionRegistry *)extensionRegistry;
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data;
+
+@end
+
+CF_EXTERN_C_BEGIN
+
+// Returns a new instance that was automatically created by |autocreator| for
+// its field |field|.
+GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass,
+                                            GPBMessage *autocreator,
+                                            GPBFieldDescriptor *field)
+    __attribute__((ns_returns_retained));
+
+// Returns whether |message| autocreated this message. This is NO if the message
+// was not autocreated by |message| or if it has been mutated since
+// autocreation.
+BOOL GPBWasMessageAutocreatedBy(GPBMessage *message, GPBMessage *parent);
+
+// Call this when you mutate a message. It will cause the message to become
+// visible to its autocreator.
+void GPBBecomeVisibleToAutocreator(GPBMessage *self);
+
+// Call this when an array is mutabled so the parent message that autocreated
+// it can react.
+void GPBAutocreatedArrayModified(GPBMessage *self, id array);
+
+// Clear the autocreator, if any. Asserts if the autocreator still has an
+// autocreated reference to this message.
+void GPBClearMessageAutocreator(GPBMessage *self);
+
+CF_EXTERN_C_END

+ 45 - 0
objectivec/GPBProtocolBuffers.h

@@ -0,0 +1,45 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBBootstrap.h"
+
+#import "GPBArray.h"
+#import "GPBCodedInputStream.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBDescriptor.h"
+#import "GPBDictionary.h"
+#import "GPBExtensionField.h"
+#import "GPBExtensionRegistry.h"
+#import "GPBField.h"
+#import "GPBMessage.h"
+#import "GPBRootObject.h"
+#import "GPBUnknownFieldSet.h"
+#import "GPBUtilities.h"
+#import "GPBWireFormat.h"

+ 49 - 0
objectivec/GPBProtocolBuffers.m

@@ -0,0 +1,49 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// If you want to build protocol buffers in your own project without adding the
+// project dependency, you can just add this file.
+
+#import "GPBArray.m"
+#import "GPBCodedInputStream.m"
+#import "GPBCodedOutputStream.m"
+#import "GPBDescriptor.m"
+#import "GPBDictionary.m"
+#import "GPBExtensionField.m"
+#import "GPBExtensionRegistry.m"
+#import "GPBField.m"
+#import "GPBMessage.m"
+#import "GPBRootObject.m"
+#import "GPBUnknownFieldSet.m"
+#import "GPBUtilities.m"
+#import "GPBWellKnownTypes.m"
+#import "GPBWireFormat.m"
+
+#import "google/protobuf/Descriptor.pbobjc.m"

+ 41 - 0
objectivec/GPBProtocolBuffers_RuntimeSupport.h

@@ -0,0 +1,41 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This header is meant to only be used by the generated source, it should not
+// be included in code using protocol buffers.
+
+#import "GPBProtocolBuffers.h"
+
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBExtensionField_PackagePrivate.h"
+#import "GPBExtensionRegistry_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBRootObject_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"

+ 42 - 0
objectivec/GPBRootObject.h

@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+@class GPBExtensionRegistry;
+
+// All Root Objects derive from GPBRootObject. It supplies a registry
+// for derived classes to register their extensions to.
+@interface GPBRootObject : NSObject
+
+// Per class registry.
++ (GPBExtensionRegistry *)extensionRegistry;
+
+@end

+ 177 - 0
objectivec/GPBRootObject.m

@@ -0,0 +1,177 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBRootObject_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#import "GPBDescriptor.h"
+#import "GPBExtensionField.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+@interface GPBExtensionDescriptor (GPBRootObject)
+// Get singletonName as a c string.
+- (const char *)singletonNameC;
+@end
+
+@implementation GPBRootObject
+
+// Taken from http://www.burtleburtle.net/bob/hash/doobs.html
+// Public Domain
+static uint32_t jenkins_one_at_a_time_hash(const char *key) {
+  uint32_t hash = 0;
+  for (uint32_t i = 0; key[i] != '\0'; ++i) {
+    hash += key[i];
+    hash += (hash << 10);
+    hash ^= (hash >> 6);
+  }
+  hash += (hash << 3);
+  hash ^= (hash >> 11);
+  hash += (hash << 15);
+  return hash;
+}
+
+// Key methods for our custom CFDictionary.
+// Note that the dictionary lasts for the lifetime of our app, so no need
+// to worry about deallocation. All of the items are added to it at
+// startup, and so the keys don't need to be retained/released.
+// Keys are NULL terminated char *.
+static const void *GPBRootExtensionKeyRetain(CFAllocatorRef allocator,
+                                             const void *value) {
+#pragma unused(allocator)
+  return value;
+}
+
+static void GPBRootExtensionKeyRelease(CFAllocatorRef allocator,
+                                       const void *value) {
+#pragma unused(allocator)
+#pragma unused(value)
+}
+
+static CFStringRef GPBRootExtensionCopyKeyDescription(const void *value) {
+  const char *key = (const char *)value;
+  return CFStringCreateWithCString(kCFAllocatorDefault, key,
+                                   kCFStringEncodingUTF8);
+}
+
+static Boolean GPBRootExtensionKeyEqual(const void *value1,
+                                        const void *value2) {
+  const char *key1 = (const char *)value1;
+  const char *key2 = (const char *)value2;
+  return strcmp(key1, key2) == 0;
+}
+
+static CFHashCode GPBRootExtensionKeyHash(const void *value) {
+  const char *key = (const char *)value;
+  return jenkins_one_at_a_time_hash(key);
+}
+
+static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
+
++ (void)initialize {
+  if (!gExtensionSingletonDictionary) {
+    CFDictionaryKeyCallBacks keyCallBacks = {
+      // See description above for reason for using custom dictionary.
+      0,
+      GPBRootExtensionKeyRetain,
+      GPBRootExtensionKeyRelease,
+      GPBRootExtensionCopyKeyDescription,
+      GPBRootExtensionKeyEqual,
+      GPBRootExtensionKeyHash,
+    };
+    gExtensionSingletonDictionary =
+        CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks,
+                                  &kCFTypeDictionaryValueCallBacks);
+  }
+}
+
++ (GPBExtensionRegistry *)extensionRegistry {
+  // Is overridden in all the subclasses that provide extensions to provide the
+  // per class one.
+  return nil;
+}
+
++ (void)globallyRegisterExtension:(GPBExtensionField *)field {
+  const char *key = [field.descriptor singletonNameC];
+  // Register happens at startup, so there is no thread safety issue in
+  // modifying the dictionary.
+  CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
+}
+
+static id ExtensionForName(id self, SEL _cmd) {
+  // Really fast way of doing "classname_selName".
+  // This came up as a hotspot (creation of NSString *) when accessing a
+  // lot of extensions.
+  const char *className = class_getName(self);
+  const char *selName = sel_getName(_cmd);
+  size_t classNameLen = strlen(className);
+  size_t selNameLen = strlen(selName);
+  char key[classNameLen + selNameLen + 2];
+  memcpy(key, className, classNameLen);
+  key[classNameLen] = '_';
+  memcpy(&key[classNameLen + 1], selName, selNameLen);
+  key[classNameLen + 1 + selNameLen] = '\0';
+  id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
+  // We can't remove the key from the dictionary here (as an optimization),
+  // because resolveClassMethod can happen on any thread and we'd then need
+  // a lock.
+  return extension;
+}
+
++ (BOOL)resolveClassMethod:(SEL)sel {
+  // Another option would be to register the extensions with the class at
+  // globallyRegisterExtension:
+  // Timing the two solutions, this solution turned out to be much faster
+  // and reduced startup time, and runtime memory.
+  // On an iPhone 5s:
+  // ResolveClassMethod: 1515583 nanos
+  // globallyRegisterExtension: 2453083 nanos
+  // The advantage to globallyRegisterExtension is that it would reduce the
+  // size of the protos somewhat because the singletonNameC wouldn't need
+  // to include the class name. For a class with a lot of extensions it
+  // can add up. You could also significantly reduce the code complexity of this
+  // file.
+  id extension = ExtensionForName(self, sel);
+  if (extension != nil) {
+    const char *encoding =
+        GPBMessageEncodingForSelector(@selector(getClassValue), NO);
+    Class metaClass = objc_getMetaClass(class_getName(self));
+    IMP imp = imp_implementationWithBlock(^(id obj) {
+#pragma unused(obj)
+      return extension;
+    });
+    return class_addMethod(metaClass, sel, imp, encoding);
+  }
+  return [super resolveClassMethod:sel];
+}
+
+@end

+ 42 - 0
objectivec/GPBRootObject_PackagePrivate.h

@@ -0,0 +1,42 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBRootObject.h"
+
+@class GPBExtensionField;
+
+@interface GPBRootObject ()
+
+// Globally register.
++ (void)globallyRegisterExtension:(GPBExtensionField *)field;
+
+@end

+ 102 - 0
objectivec/GPBTypes.h

@@ -0,0 +1,102 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBBootstrap.h"
+
+@class GPBEnumDescriptor;
+@class GPBMessage;
+@class GPBInt32Array;
+
+// Function used to verify that a given value can be represented by an
+// enum type.
+typedef BOOL (*GPBEnumValidationFunc)(int32_t);
+
+// Function used to fetch an EnumDescriptor.
+typedef GPBEnumDescriptor *(*GPBEnumDescriptorFunc)(void);
+
+// Magic values used for when an the at runtime to indicate an enum value
+// that wasn't know at compile time.
+enum {
+  kGPBUnrecognizedEnumeratorValue = (int32_t)0xFBADBEEF,
+};
+
+// A union for storing all possible Protobuf values.
+// Note that owner is responsible for memory management of object types.
+typedef union {
+  BOOL valueBool;
+  int32_t valueInt32;
+  int64_t valueInt64;
+  uint32_t valueUInt32;
+  uint64_t valueUInt64;
+  float valueFloat;
+  double valueDouble;
+  GPB_UNSAFE_UNRETAINED NSData *valueData;
+  GPB_UNSAFE_UNRETAINED NSString *valueString;
+  GPB_UNSAFE_UNRETAINED GPBMessage *valueMessage;
+  int32_t valueEnum;
+} GPBValue;
+
+// Do not change the order of this enum (or add things to it) without thinking
+// about it very carefully. There are several things that depend on the order.
+typedef enum {
+  GPBTypeBool = 0,
+  GPBTypeFixed32,
+  GPBTypeSFixed32,
+  GPBTypeFloat,
+  GPBTypeFixed64,
+  GPBTypeSFixed64,
+  GPBTypeDouble,
+  GPBTypeInt32,
+  GPBTypeInt64,
+  GPBTypeSInt32,
+  GPBTypeSInt64,
+  GPBTypeUInt32,
+  GPBTypeUInt64,
+  GPBTypeData,  // Maps to Bytes Protobuf type
+  GPBTypeString,
+  GPBTypeMessage,
+  GPBTypeGroup,
+  GPBTypeEnum,
+} GPBType;
+
+enum {
+  // A count of the number of types in GPBType. Separated out from the GPBType
+  // enum to avoid warnings regarding not handling GPBTypeCount in switch
+  // statements.
+  GPBTypeCount = GPBTypeEnum + 1
+};
+
+// An extension range.
+typedef struct GPBExtensionRange {
+  uint32_t start;  // inclusive
+  uint32_t end;    // exclusive
+} GPBExtensionRange;

+ 46 - 0
objectivec/GPBUnknownFieldSet.h

@@ -0,0 +1,46 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+@class GPBField;
+
+@interface GPBUnknownFieldSet : NSObject<NSCopying>
+
+- (BOOL)hasField:(int32_t)number;
+- (GPBField *)getField:(int32_t)number;
+- (NSUInteger)countOfFields;
+
+- (void)addField:(GPBField *)field;
+
+// Returns an NSArray of the GPBFields sorted by the field numbers.
+- (NSArray *)sortedFields;
+
+@end

+ 422 - 0
objectivec/GPBUnknownFieldSet.m

@@ -0,0 +1,422 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBField_PackagePrivate.h"
+#import "GPBUtilities.h"
+#import "GPBWireFormat.h"
+
+#pragma mark CFDictionaryKeyCallBacks
+
+// We use a custom dictionary here because our keys are numbers and
+// conversion back and forth from NSNumber was costing us performance.
+// If/when we move to C++ this could be done using a std::map and some
+// careful retain/release calls.
+
+static const void *GPBUnknownFieldSetKeyRetain(CFAllocatorRef allocator,
+                                               const void *value) {
+#pragma unused(allocator)
+  return value;
+}
+
+static void GPBUnknownFieldSetKeyRelease(CFAllocatorRef allocator,
+                                         const void *value) {
+#pragma unused(allocator)
+#pragma unused(value)
+}
+
+static CFStringRef GPBUnknownFieldSetCopyKeyDescription(const void *value) {
+  return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"),
+                                  (int)value);
+}
+
+static Boolean GPBUnknownFieldSetKeyEqual(const void *value1,
+                                          const void *value2) {
+  return value1 == value2;
+}
+
+static CFHashCode GPBUnknownFieldSetKeyHash(const void *value) {
+  return (CFHashCode)value;
+}
+
+#pragma mark Helpers
+
+static void checkNumber(int32_t number) {
+  if (number == 0) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Zero is not a valid field number."];
+  }
+}
+
+@implementation GPBUnknownFieldSet {
+ @package
+  CFMutableDictionaryRef fields_;
+}
+
+static void CopyWorker(const void *key, const void *value, void *context) {
+#pragma unused(key)
+  GPBField *field = value;
+  GPBUnknownFieldSet *result = context;
+
+  GPBField *copied = [field copy];
+  [result addField:copied];
+  [copied release];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBUnknownFieldSet *result = [[GPBUnknownFieldSet allocWithZone:zone] init];
+  if (fields_) {
+    CFDictionaryApplyFunction(fields_, CopyWorker, result);
+  }
+  return result;
+}
+
+- (void)dealloc {
+  if (fields_) {
+    CFRelease(fields_);
+  }
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)object {
+  BOOL equal = NO;
+  if ([object isKindOfClass:[GPBUnknownFieldSet class]]) {
+    GPBUnknownFieldSet *set = (GPBUnknownFieldSet *)object;
+    if ((fields_ == NULL) && (set->fields_ == NULL)) {
+      equal = YES;
+    } else if ((fields_ != NULL) && (set->fields_ != NULL)) {
+      equal = CFEqual(fields_, set->fields_);
+    }
+  }
+  return equal;
+}
+
+- (NSUInteger)hash {
+  // Return the hash of the fields dictionary (or just some value).
+  if (fields_) {
+    return CFHash(fields_);
+  }
+  return (NSUInteger)[GPBUnknownFieldSet class];
+}
+
+#pragma mark - Public Methods
+
+- (BOOL)hasField:(int32_t)number {
+  ssize_t key = number;
+  return fields_ ? (CFDictionaryGetValue(fields_, (void *)key) != nil) : NO;
+}
+
+- (GPBField *)getField:(int32_t)number {
+  ssize_t key = number;
+  GPBField *result = fields_ ? CFDictionaryGetValue(fields_, (void *)key) : nil;
+  return result;
+}
+
+- (NSUInteger)countOfFields {
+  return fields_ ? CFDictionaryGetCount(fields_) : 0;
+}
+
+- (NSArray *)sortedFields {
+  if (!fields_) return nil;
+  size_t count = CFDictionaryGetCount(fields_);
+  ssize_t keys[count];
+  GPBField *values[count];
+  CFDictionaryGetKeysAndValues(fields_, (const void **)keys,
+                               (const void **)values);
+  struct GPBFieldPair {
+    ssize_t key;
+    GPBField *value;
+  } pairs[count];
+  for (size_t i = 0; i < count; ++i) {
+    pairs[i].key = keys[i];
+    pairs[i].value = values[i];
+  };
+  qsort_b(pairs, count, sizeof(struct GPBFieldPair),
+          ^(const void *first, const void *second) {
+            const struct GPBFieldPair *a = first;
+            const struct GPBFieldPair *b = second;
+            return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
+          });
+  for (size_t i = 0; i < count; ++i) {
+    values[i] = pairs[i].value;
+  };
+  return [NSArray arrayWithObjects:values count:count];
+}
+
+#pragma mark - Internal Methods
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
+  if (!fields_) return;
+  size_t count = CFDictionaryGetCount(fields_);
+  ssize_t keys[count];
+  GPBField *values[count];
+  CFDictionaryGetKeysAndValues(fields_, (const void **)keys,
+                               (const void **)values);
+  if (count > 1) {
+    struct GPBFieldPair {
+      ssize_t key;
+      GPBField *value;
+    } pairs[count];
+
+    for (size_t i = 0; i < count; ++i) {
+      pairs[i].key = keys[i];
+      pairs[i].value = values[i];
+    };
+    qsort_b(pairs, count, sizeof(struct GPBFieldPair),
+            ^(const void *first, const void *second) {
+              const struct GPBFieldPair *a = first;
+              const struct GPBFieldPair *b = second;
+              return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
+            });
+    for (size_t i = 0; i < count; ++i) {
+      GPBField *value = pairs[i].value;
+      [value writeToOutput:output];
+    }
+  } else {
+    [values[0] writeToOutput:output];
+  }
+}
+
+- (NSString *)description {
+  NSMutableString *description = [NSMutableString
+      stringWithFormat:@"<%@ %p>: TextFormat: {\n", [self class], self];
+  NSString *textFormat = GPBTextFormatForUnknownFieldSet(self, @"  ");
+  [description appendString:textFormat];
+  [description appendString:@"}"];
+  return description;
+}
+
+static void GPBUnknownFieldSetSerializedSize(const void *key, const void *value,
+                                             void *context) {
+#pragma unused(key)
+  GPBField *field = value;
+  size_t *result = context;
+  *result += [field serializedSize];
+}
+
+- (size_t)serializedSize {
+  size_t result = 0;
+  if (fields_) {
+    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSize,
+                              &result);
+  }
+  return result;
+}
+
+static void GPBUnknownFieldSetWriteAsMessageSetTo(const void *key,
+                                                  const void *value,
+                                                  void *context) {
+#pragma unused(key)
+  GPBField *field = value;
+  GPBCodedOutputStream *output = context;
+  [field writeAsMessageSetExtensionToOutput:output];
+}
+
+- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output {
+  if (fields_) {
+    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetWriteAsMessageSetTo,
+                              output);
+  }
+}
+
+static void GPBUnknownFieldSetSerializedSizeAsMessageSet(const void *key,
+                                                         const void *value,
+                                                         void *context) {
+#pragma unused(key)
+  GPBField *field = value;
+  size_t *result = context;
+  *result += [field serializedSizeAsMessageSetExtension];
+}
+
+- (size_t)serializedSizeAsMessageSet {
+  size_t result = 0;
+  if (fields_) {
+    CFDictionaryApplyFunction(
+        fields_, GPBUnknownFieldSetSerializedSizeAsMessageSet, &result);
+  }
+  return result;
+}
+
+- (NSData *)data {
+  NSMutableData *data = [NSMutableData dataWithLength:self.serializedSize];
+  GPBCodedOutputStream *output =
+      [[GPBCodedOutputStream alloc] initWithData:data];
+  [self writeToCodedOutputStream:output];
+  [output release];
+  return data;
+}
+
++ (BOOL)isFieldTag:(int32_t)tag {
+  return GPBWireFormatGetTagWireType(tag) != GPBWireFormatEndGroup;
+}
+
+- (void)addField:(GPBField *)field {
+  int32_t number = [field number];
+  checkNumber(number);
+  if (!fields_) {
+    CFDictionaryKeyCallBacks keyCallBacks = {
+        // See description above for reason for using custom dictionary.
+        0, GPBUnknownFieldSetKeyRetain, GPBUnknownFieldSetKeyRelease,
+        GPBUnknownFieldSetCopyKeyDescription, GPBUnknownFieldSetKeyEqual,
+        GPBUnknownFieldSetKeyHash,
+    };
+    fields_ = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks,
+                                        &kCFTypeDictionaryValueCallBacks);
+  }
+  ssize_t key = number;
+  CFDictionarySetValue(fields_, (const void *)key, field);
+}
+
+- (GPBField *)mutableFieldForNumber:(int32_t)number create:(BOOL)create {
+  ssize_t key = number;
+  GPBField *existing =
+      fields_ ? CFDictionaryGetValue(fields_, (const void *)key) : nil;
+  if (!existing && create) {
+    existing = [[GPBField alloc] initWithNumber:number];
+    // This retains existing.
+    [self addField:existing];
+    [existing release];
+  }
+  return existing;
+}
+
+static void GPBUnknownFieldSetMergeUnknownFields(const void *key,
+                                                 const void *value,
+                                                 void *context) {
+#pragma unused(key)
+  GPBField *field = value;
+  GPBUnknownFieldSet *self = context;
+
+  int32_t number = [field number];
+  checkNumber(number);
+  GPBField *oldField = [self mutableFieldForNumber:number create:NO];
+  if (oldField) {
+    [oldField mergeFromField:field];
+  } else {
+    // Merge only comes from GPBMessage's mergeFrom:, so it means we are on
+    // mutable message and are an mutable instance, so make sure we need
+    // mutable fields.
+    GPBField *fieldCopy = [field copy];
+    [self addField:fieldCopy];
+    [fieldCopy release];
+  }
+}
+
+- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other {
+  if (other && other->fields_) {
+    CFDictionaryApplyFunction(other->fields_,
+                              GPBUnknownFieldSetMergeUnknownFields, self);
+  }
+}
+
+- (void)mergeFromData:(NSData *)data {
+  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
+  [self mergeFromCodedInputStream:input];
+  [input checkLastTagWas:0];
+  [input release];
+}
+
+- (void)mergeVarintField:(int32_t)number value:(int32_t)value {
+  checkNumber(number);
+  [[self mutableFieldForNumber:number create:YES] addVarint:value];
+}
+
+- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input {
+  int32_t number = GPBWireFormatGetTagFieldNumber(tag);
+  GPBCodedInputStreamState *state = &input->state_;
+  switch (GPBWireFormatGetTagWireType(tag)) {
+    case GPBWireFormatVarint: {
+      GPBField *field = [self mutableFieldForNumber:number create:YES];
+      [field addVarint:GPBCodedInputStreamReadInt64(state)];
+      return YES;
+    }
+    case GPBWireFormatFixed64: {
+      GPBField *field = [self mutableFieldForNumber:number create:YES];
+      [field addFixed64:GPBCodedInputStreamReadFixed64(state)];
+      return YES;
+    }
+    case GPBWireFormatLengthDelimited: {
+      NSData *data = GPBCodedInputStreamReadRetainedData(state);
+      GPBField *field = [self mutableFieldForNumber:number create:YES];
+      [field addLengthDelimited:data];
+      [data release];
+      return YES;
+    }
+    case GPBWireFormatStartGroup: {
+      GPBUnknownFieldSet *unknownFieldSet = [[GPBUnknownFieldSet alloc] init];
+      [input readUnknownGroup:number message:unknownFieldSet];
+      GPBField *field = [self mutableFieldForNumber:number create:YES];
+      [field addGroup:unknownFieldSet];
+      [unknownFieldSet release];
+      return YES;
+    }
+    case GPBWireFormatEndGroup:
+      return NO;
+    case GPBWireFormatFixed32: {
+      GPBField *field = [self mutableFieldForNumber:number create:YES];
+      [field addFixed32:GPBCodedInputStreamReadFixed32(state)];
+      return YES;
+    }
+  }
+}
+
+- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData {
+  [[self mutableFieldForNumber:number create:YES]
+      addLengthDelimited:messageData];
+}
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data {
+  GPBField *field = [self mutableFieldForNumber:fieldNum create:YES];
+  [field addLengthDelimited:data];
+}
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input {
+  while (YES) {
+    int32_t tag = GPBCodedInputStreamReadTag(&input->state_);
+    if (tag == 0 || ![self mergeFieldFrom:tag input:input]) {
+      break;
+    }
+  }
+}
+
+- (void)getTags:(int32_t *)tags {
+  if (!fields_) return;
+  size_t count = CFDictionaryGetCount(fields_);
+  ssize_t keys[count];
+  CFDictionaryGetKeysAndValues(fields_, (const void **)keys, NULL);
+  for (size_t i = 0; i < count; ++i) {
+    tags[i] = (int32_t)keys[i];
+  }
+}
+
+@end

+ 61 - 0
objectivec/GPBUnknownFieldSet_PackagePrivate.h

@@ -0,0 +1,61 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBUnknownFieldSet.h"
+
+@class GPBCodedOutputStream;
+@class GPBCodedInputStream;
+
+@interface GPBUnknownFieldSet ()
+
++ (BOOL)isFieldTag:(int32_t)tag;
+
+- (NSData *)data;
+
+- (size_t)serializedSize;
+- (size_t)serializedSizeAsMessageSet;
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
+- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output;
+
+- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other;
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input;
+- (void)mergeFromData:(NSData *)data;
+
+- (void)mergeVarintField:(int32_t)number value:(int32_t)value;
+- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input;
+- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData;
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data;
+
+@end

+ 181 - 0
objectivec/GPBUtilities.h

@@ -0,0 +1,181 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBMessage.h"
+#import "GPBTypes.h"
+
+CF_EXTERN_C_BEGIN
+
+BOOL GPBMessageHasFieldNumberSet(GPBMessage *self, uint32_t fieldNumber);
+BOOL GPBMessageHasFieldSet(GPBMessage *self, GPBFieldDescriptor *field);
+
+void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field);
+
+// Returns an empty NSData to assign to byte fields when you wish
+// to assign them to empty. Prevents allocating a lot of little [NSData data]
+// objects.
+NSData *GPBEmptyNSData(void) __attribute__((pure));
+
+//%PDDM-EXPAND GPB_IVAR_ACCESSORS()
+// This block of code is generated, do not edit it directly.
+
+// Getters and Setters for ivars named |name| from instance self.
+
+NSData* GPBGetDataIvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field);
+void GPBSetDataIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             NSData* value);
+NSString* GPBGetStringIvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field);
+void GPBSetStringIvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               NSString* value);
+GPBMessage* GPBGetMessageIvarWithField(GPBMessage *self,
+                                       GPBFieldDescriptor *field);
+void GPBSetMessageIvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                GPBMessage* value);
+GPBMessage* GPBGetGroupIvarWithField(GPBMessage *self,
+                                     GPBFieldDescriptor *field);
+void GPBSetGroupIvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              GPBMessage* value);
+BOOL GPBGetBoolIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field);
+void GPBSetBoolIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             BOOL value);
+int32_t GPBGetInt32IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field);
+void GPBSetInt32IvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              int32_t value);
+int32_t GPBGetSFixed32IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field);
+void GPBSetSFixed32IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field,
+                                 int32_t value);
+int32_t GPBGetSInt32IvarWithField(GPBMessage *self,
+                                  GPBFieldDescriptor *field);
+void GPBSetSInt32IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               int32_t value);
+int32_t GPBGetEnumIvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field);
+void GPBSetEnumIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             int32_t value);
+uint32_t GPBGetUInt32IvarWithField(GPBMessage *self,
+                                   GPBFieldDescriptor *field);
+void GPBSetUInt32IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               uint32_t value);
+uint32_t GPBGetFixed32IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field);
+void GPBSetFixed32IvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                uint32_t value);
+int64_t GPBGetInt64IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field);
+void GPBSetInt64IvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              int64_t value);
+int64_t GPBGetSInt64IvarWithField(GPBMessage *self,
+                                  GPBFieldDescriptor *field);
+void GPBSetSInt64IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               int64_t value);
+int64_t GPBGetSFixed64IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field);
+void GPBSetSFixed64IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field,
+                                 int64_t value);
+uint64_t GPBGetUInt64IvarWithField(GPBMessage *self,
+                                   GPBFieldDescriptor *field);
+void GPBSetUInt64IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               uint64_t value);
+uint64_t GPBGetFixed64IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field);
+void GPBSetFixed64IvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                uint64_t value);
+float GPBGetFloatIvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field);
+void GPBSetFloatIvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              float value);
+double GPBGetDoubleIvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field);
+void GPBSetDoubleIvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               double value);
+//%PDDM-EXPAND-END GPB_IVAR_ACCESSORS()
+
+// Generates a sting that should be a valid "Text Format" for the C++ version
+// of Protocol Buffers. lineIndent can be nil if no additional line indent is
+// needed. The comments provide the names according to the ObjC library, they
+// most likely won't exactly match the original .proto file.
+NSString *GPBTextFormatForMessage(GPBMessage *message, NSString *lineIndent);
+NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet *unknownSet,
+                                          NSString *lineIndent);
+
+CF_EXTERN_C_END
+
+//%PDDM-DEFINE GPB_IVAR_ACCESSORS()
+//%// Getters and Setters for ivars named |name| from instance self.
+//%
+//%GPB_IVAR_ACCESSORS_DECL(Data, NSData*)
+//%GPB_IVAR_ACCESSORS_DECL(String, NSString*)
+//%GPB_IVAR_ACCESSORS_DECL(Message, GPBMessage*)
+//%GPB_IVAR_ACCESSORS_DECL(Group, GPBMessage*)
+//%GPB_IVAR_ACCESSORS_DECL(Bool, BOOL)
+//%GPB_IVAR_ACCESSORS_DECL(Int32, int32_t)
+//%GPB_IVAR_ACCESSORS_DECL(SFixed32, int32_t)
+//%GPB_IVAR_ACCESSORS_DECL(SInt32, int32_t)
+//%GPB_IVAR_ACCESSORS_DECL(Enum, int32_t)
+//%GPB_IVAR_ACCESSORS_DECL(UInt32, uint32_t)
+//%GPB_IVAR_ACCESSORS_DECL(Fixed32, uint32_t)
+//%GPB_IVAR_ACCESSORS_DECL(Int64, int64_t)
+//%GPB_IVAR_ACCESSORS_DECL(SInt64, int64_t)
+//%GPB_IVAR_ACCESSORS_DECL(SFixed64, int64_t)
+//%GPB_IVAR_ACCESSORS_DECL(UInt64, uint64_t)
+//%GPB_IVAR_ACCESSORS_DECL(Fixed64, uint64_t)
+//%GPB_IVAR_ACCESSORS_DECL(Float, float)
+//%GPB_IVAR_ACCESSORS_DECL(Double, double)
+//%PDDM-DEFINE GPB_IVAR_ACCESSORS_DECL(NAME, TYPE)
+//%TYPE GPBGet##NAME##IvarWithField(GPBMessage *self,
+//% TYPE$S     NAME$S               GPBFieldDescriptor *field);
+//%void GPBSet##NAME##IvarWithField(GPBMessage *self,
+//%            NAME$S             GPBFieldDescriptor *field,
+//%            NAME$S             TYPE value);

+ 1645 - 0
objectivec/GPBUtilities.m

@@ -0,0 +1,1645 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBUtilities_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import "GPBArray_PackagePrivate.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBExtensionField.h"
+#import "GPBField.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUnknownFieldSet.h"
+
+static void AppendTextFormatForMessage(GPBMessage *message,
+                                       NSMutableString *toStr,
+                                       NSString *lineIndent);
+
+NSData *GPBEmptyNSData(void) {
+  static dispatch_once_t onceToken;
+  static NSData *defaultNSData = nil;
+  dispatch_once(&onceToken, ^{
+    defaultNSData = [[NSData alloc] init];
+  });
+  return defaultNSData;
+}
+
+BOOL GPBMessageHasFieldNumberSet(GPBMessage *self, uint32_t fieldNumber) {
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:fieldNumber];
+  return GPBMessageHasFieldSet(self, field);
+}
+
+BOOL GPBMessageHasFieldSet(GPBMessage *self, GPBFieldDescriptor *field) {
+  if (self == nil || field == nil) return NO;
+
+  // Repeated/Map don't use the bit, they check the count.
+  if (GPBFieldIsMapOrArray(field)) {
+    // Array/map type doesn't matter, since GPB*Array/NSArray and
+    // GPB*Dictionary/NSDictionary all support -count;
+    NSArray *arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+    return (arrayOrMap.count > 0);
+  } else {
+    return GPBGetHasIvarField(self, field);
+  }
+}
+
+void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field) {
+  // If not set, nothing to do.
+  if (!GPBGetHasIvarField(self, field)) {
+    return;
+  }
+
+  if (GPBFieldStoresObject(field)) {
+    // Object types are handled slightly differently, they need to be released.
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    id *typePtr = (id *)&storage[field->description_->offset];
+    [*typePtr release];
+    *typePtr = nil;
+  } else {
+    // POD types just need to clear the has bit as the Get* method will
+    // fetch the default when needed.
+  }
+  GPBSetHasIvarField(self, field, NO);
+}
+
+BOOL GPBGetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber) {
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+  if (idx < 0) {
+    NSCAssert(fieldNumber != 0, @"Invalid field number.");
+    BOOL hasIvar = (self->messageStorage_->_has_storage_[-idx] == fieldNumber);
+    return hasIvar;
+  } else {
+    NSCAssert(idx != GPBNoHasBit, @"Invalid has bit.");
+    uint32_t byteIndex = idx / 32;
+    uint32_t bitMask = (1 << (idx % 32));
+    BOOL hasIvar =
+        (self->messageStorage_->_has_storage_[byteIndex] & bitMask) ? YES : NO;
+    return hasIvar;
+  }
+}
+
+uint32_t GPBGetHasOneof(GPBMessage *self, int32_t idx) {
+  NSCAssert(idx < 0, @"invalid index for oneof.");
+  uint32_t result = self->messageStorage_->_has_storage_[-idx];
+  return result;
+}
+
+void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber,
+                   BOOL value) {
+  if (idx < 0) {
+    NSCAssert(fieldNumber != 0, @"Invalid field number.");
+    uint32_t *has_storage = self->messageStorage_->_has_storage_;
+    has_storage[-idx] = (value ? fieldNumber : 0);
+  } else {
+    NSCAssert(idx != GPBNoHasBit, @"Invalid has bit.");
+    uint32_t *has_storage = self->messageStorage_->_has_storage_;
+    uint32_t byte = idx / 32;
+    uint32_t bitMask = (1 << (idx % 32));
+    if (value) {
+      has_storage[byte] |= bitMask;
+    } else {
+      has_storage[byte] &= ~bitMask;
+    }
+  }
+}
+
+void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof,
+                        uint32_t fieldNumberNotToClear) {
+  int32_t hasIndex = oneof->oneofDescription_->index;
+  uint32_t fieldNumberSet = GPBGetHasOneof(self, hasIndex);
+  if ((fieldNumberSet == fieldNumberNotToClear) || (fieldNumberSet == 0)) {
+    // Do nothing/nothing set in the oneof.
+    return;
+  }
+
+  // Like GPBClearMessageField(), free the memory if an objecttype is set,
+  // pod types don't need to do anything.
+  GPBFieldDescriptor *fieldSet = [oneof fieldWithNumber:fieldNumberSet];
+  NSCAssert(fieldSet, @"oneof set to something not in the oneof?");
+  if (fieldSet && GPBFieldStoresObject(fieldSet)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    id *typePtr = (id *)&storage[fieldSet->description_->offset];
+    [*typePtr release];
+    *typePtr = nil;
+  }
+
+  // Set to nothing stored in the oneof.
+  // (field number doesn't matter since setting to nothing).
+  GPBSetHasIvar(self, hasIndex, 1, NO);
+}
+
+#pragma mark - IVar accessors
+
+//%PDDM-DEFINE IVAR_POD_ACCESSORS_DEFN(NAME, TYPE)
+//%TYPE GPBGet##NAME##IvarWithField(GPBMessage *self,
+//% TYPE$S     NAME$S               GPBFieldDescriptor *field) {
+//%  if (GPBGetHasIvarField(self, field)) {
+//%    uint8_t *storage = (uint8_t *)self->messageStorage_;
+//%    TYPE *typePtr = (TYPE *)&storage[field->description_->offset];
+//%    return *typePtr;
+//%  } else {
+//%    return field.defaultValue.value##NAME;
+//%  }
+//%}
+//%
+//%// Only exists for public api, no core code should use this.
+//%void GPBSet##NAME##IvarWithField(GPBMessage *self,
+//%            NAME$S             GPBFieldDescriptor *field,
+//%            NAME$S             TYPE value) {
+//%  if (self == nil || field == nil) return;
+//%  GPBFileSyntax syntax = [self descriptor].file.syntax;
+//%  GPBSet##NAME##IvarWithFieldInternal(self, field, value, syntax);
+//%}
+//%
+//%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self,
+//%            NAME$S                     GPBFieldDescriptor *field,
+//%            NAME$S                     TYPE value,
+//%            NAME$S                     GPBFileSyntax syntax) {
+//%  GPBOneofDescriptor *oneof = field->containingOneof_;
+//%  if (oneof) {
+//%    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+//%  }
+//%  NSCAssert(self->messageStorage_ != NULL, @"How?");
+//%#if defined(__clang_analyzer__)
+//%  if (self->messageStorage_ == NULL) return;
+//%#endif
+//%  uint8_t *storage = (uint8_t *)self->messageStorage_;
+//%  TYPE *typePtr = (TYPE *)&storage[field->description_->offset];
+//%  *typePtr = value;
+//%  // proto2: any value counts as having been set; proto3, it
+//%  // has to be a non zero value.
+//%  BOOL hasValue =
+//%    (syntax == GPBFileSyntaxProto2) || (value != (TYPE)0);
+//%  GPBSetHasIvarField(self, field, hasValue);
+//%  GPBBecomeVisibleToAutocreator(self);
+//%}
+//%
+//%PDDM-DEFINE IVAR_ALIAS_DEFN(NAME, ALIAS_NAME, TYPE, ALIAS_TYPE)
+//%// Only exists for public api, no core code should use this.
+//%TYPE GPBGet##NAME##IvarWithField(GPBMessage *self,
+//% TYPE$S     NAME$S               GPBFieldDescriptor *field) {
+//%  return (TYPE)GPBGet##ALIAS_NAME##IvarWithField(self, field);
+//%}
+//%
+//%// Only exists for public api, no core code should use this.
+//%void GPBSet##NAME##IvarWithField(GPBMessage *self,
+//%            NAME$S             GPBFieldDescriptor *field,
+//%            NAME$S             TYPE value) {
+//%  GPBSet##ALIAS_NAME##IvarWithField(self, field, (ALIAS_TYPE)value);
+//%}
+//%
+
+// Object types are handled slightly differently, they need to be released
+// and retained.
+
+void GPBSetAutocreatedRetainedObjectIvarWithField(
+    GPBMessage *self, GPBFieldDescriptor *field,
+    id __attribute__((ns_consumed)) value) {
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+  NSCAssert(*typePtr == NULL, @"Can't set autocreated object more than once.");
+  *typePtr = value;
+}
+
+void GPBClearAutocreatedMessageIvarWithField(GPBMessage *self,
+                                             GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    return;
+  }
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+  GPBMessage *oldValue = *typePtr;
+  *typePtr = NULL;
+  GPBClearMessageAutocreator(oldValue);
+  [oldValue release];
+}
+
+// This exists only for briging some aliased types, nothing else should use it.
+GPB_INLINE void GPBSetObjectIvarWithField(GPBMessage *self,
+                                          GPBFieldDescriptor *field, id value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value retain],
+                                            syntax);
+}
+
+void GPBSetObjectIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field, id value,
+                                       GPBFileSyntax syntax) {
+  GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value retain],
+                                            syntax);
+}
+
+void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
+                                               GPBFieldDescriptor *field,
+                                               id value, GPBFileSyntax syntax) {
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  GPBType fieldType = GPBGetFieldType(field);
+  BOOL isMapOrArray = GPBFieldIsMapOrArray(field);
+  BOOL fieldIsMessage = GPBTypeIsMessage(fieldType);
+#ifdef DEBUG
+  if (value == nil && !isMapOrArray && !fieldIsMessage &&
+      field.hasDefaultValue) {
+    // Setting a message to nil is an obvious way to "clear" the value
+    // as there is no way to set a non-empty default value for messages.
+    //
+    // For Strings and Bytes that have default values set it is not clear what
+    // should be done when their value is set to nil. Is the intention just to
+    // clear the set value and reset to default, or is the intention to set the
+    // value to the empty string/data? Arguments can be made for both cases.
+    // 'nil' has been abused as a replacement for an empty string/data in ObjC.
+    // We decided to be consistent with all "object" types and clear the has
+    // field, and fall back on the default value. The warning below will only
+    // appear in debug, but the could should be changed so the intention is
+    // clear.
+    NSString *hasSel = NSStringFromSelector(field->hasSel_);
+    NSString *propName = field.name;
+    NSString *className = self.descriptor.name;
+    NSLog(@"warning: '%@.%@ = nil;' is not clearly defined for fields with "
+          @"default values. Please use '%@.%@ = %@' if you want to set it to "
+          @"empty, or call '%@.%@ = NO' to reset it to it's default value of "
+          @"'%@'. Defaulting to resetting default value.",
+          className, propName, className, propName,
+          (fieldType == GPBTypeString) ? @"@\"\"" : @"GPBEmptyNSData()",
+          className, hasSel, field.defaultValue.valueString);
+    // Note: valueString, depending on the type, it could easily be
+    // valueData/valueMessage.
+  }
+#endif  // DEBUG
+  if (!isMapOrArray) {
+    // Non repeated/map can be in an oneof, clear any existing value from the
+    // oneof.
+    GPBOneofDescriptor *oneof = field->containingOneof_;
+    if (oneof) {
+      GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+    }
+    // Clear "has" if they are being set to nil.
+    BOOL setHasValue = (value != nil);
+    // Under proto3, Data & String fields get cleared by resetting them to their
+    // default (empty) values, so if they are set to something of length zero,
+    // they are being cleared.
+    if ((syntax == GPBFileSyntaxProto3) && !fieldIsMessage &&
+        ([value length] == 0)) {
+      setHasValue = NO;
+      value = nil;
+    }
+    GPBSetHasIvarField(self, field, setHasValue);
+  }
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+
+  id oldValue = *typePtr;
+
+  *typePtr = value;
+
+  if (oldValue) {
+    if (isMapOrArray) {
+      if (field.fieldType == GPBFieldTypeRepeated) {
+        // If the old message value was autocreated by us, then clear it.
+        if (GPBTypeIsObject(fieldType)) {
+          GPBAutocreatedArray *autoArray = oldValue;
+          if (autoArray->_autocreator == self) {
+            autoArray->_autocreator = nil;
+          }
+        } else {
+          // Type doesn't matter, it is a GPB*Array.
+          GPBInt32Array *gpbArray = oldValue;
+          if (gpbArray->_autocreator == self) {
+            gpbArray->_autocreator = nil;
+          }
+        }
+      }
+    } else if (fieldIsMessage) {
+      // If the old message value was autocreated by us, then clear it.
+      GPBMessage *oldMessageValue = oldValue;
+      if (GPBWasMessageAutocreatedBy(oldMessageValue, self)) {
+        GPBClearMessageAutocreator(oldMessageValue);
+      }
+    }
+    [oldValue release];
+  }
+
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+id GPBGetObjectIvarWithFieldNoAutocreate(GPBMessage *self,
+                                         GPBFieldDescriptor *field) {
+  if (self->messageStorage_ == nil) {
+    return nil;
+  }
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+  return *typePtr;
+}
+
+id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+  NSCAssert(!GPBFieldIsMapOrArray(field), @"Shouldn't get here");
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    id *typePtr = (id *)&storage[field->description_->offset];
+    return *typePtr;
+  }
+  // Not set...
+
+  // Non messages (string/data), get their default.
+  if (!GPBFieldTypeIsMessage(field)) {
+    return field.defaultValue.valueMessage;
+  }
+
+  OSSpinLockLock(&self->readOnlyMutex_);
+  GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!result) {
+    // For non repeated messages, create the object, set it and return it.
+    // This object will not initially be visible via GPBGetHasIvar, so
+    // we save its creator so it can become visible if it's mutated later.
+    result = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
+    GPBSetAutocreatedRetainedObjectIvarWithField(self, field, result);
+  }
+  OSSpinLockUnlock(&self->readOnlyMutex_);
+  return result;
+}
+
+// Only exists for public api, no core code should use this.
+int32_t GPBGetEnumIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  return GPBGetEnumIvarWithFieldInternal(self, field, syntax);
+}
+
+int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax) {
+  int32_t result = GPBGetInt32IvarWithField(self, field);
+  // If this is presevering unknown enums, make sure the value is
+  // valid before returning it.
+  if (GPBHasPreservingUnknownEnumSemantics(syntax) &&
+      ![field isValidEnumValue:result]) {
+    result = kGPBUnrecognizedEnumeratorValue;
+  }
+  return result;
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetEnumIvarWithField(GPBMessage *self, GPBFieldDescriptor *field,
+                             int32_t value) {
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetEnumIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field, int32_t value,
+                                     GPBFileSyntax syntax) {
+  // Don't allow in unknown values.  Proto3 can use the Raw method.
+  if (![field isValidEnumValue:value]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@: Attempt to set an unknown enum value (%d)",
+                       [self class], field.name, value];
+  }
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+BOOL GPBGetBoolIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    BOOL *typePtr = (BOOL *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueBool;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetBoolIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             BOOL value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetBoolIvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetBoolIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field,
+                                     BOOL value,
+                                     GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  BOOL *typePtr = (BOOL *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (BOOL)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+int32_t GPBGetInt32IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    int32_t *typePtr = (int32_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueInt32;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetInt32IvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              int32_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetInt32IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int32_t value,
+                                      GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  int32_t *typePtr = (int32_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (int32_t)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+uint32_t GPBGetUInt32IvarWithField(GPBMessage *self,
+                                   GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    uint32_t *typePtr = (uint32_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueUInt32;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetUInt32IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               uint32_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetUInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint32_t value,
+                                       GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  uint32_t *typePtr = (uint32_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (uint32_t)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+int64_t GPBGetInt64IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    int64_t *typePtr = (int64_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueInt64;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetInt64IvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              int64_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt64IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetInt64IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int64_t value,
+                                      GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  int64_t *typePtr = (int64_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (int64_t)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+uint64_t GPBGetUInt64IvarWithField(GPBMessage *self,
+                                   GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    uint64_t *typePtr = (uint64_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueUInt64;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetUInt64IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               uint64_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetUInt64IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint64_t value,
+                                       GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  uint64_t *typePtr = (uint64_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (uint64_t)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Float, float)
+// This block of code is generated, do not edit it directly.
+
+float GPBGetFloatIvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    float *typePtr = (float *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueFloat;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetFloatIvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              float value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetFloatIvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetFloatIvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      float value,
+                                      GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  float *typePtr = (float *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (float)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Double, double)
+// This block of code is generated, do not edit it directly.
+
+double GPBGetDoubleIvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    double *typePtr = (double *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueDouble;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetDoubleIvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               double value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetDoubleIvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       double value,
+                                       GPBFileSyntax syntax) {
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field));
+  }
+  NSCAssert(self->messageStorage_ != NULL, @"How?");
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  double *typePtr = (double *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value.
+  BOOL hasValue =
+    (syntax == GPBFileSyntaxProto2) || (value != (double)0);
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND-END (7 expansions)
+
+// Aliases are function calls that are virtually the same.
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(SInt32, Int32, int32_t, int32_t)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+int32_t GPBGetSInt32IvarWithField(GPBMessage *self,
+                                  GPBFieldDescriptor *field) {
+  return (int32_t)GPBGetInt32IvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetSInt32IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               int32_t value) {
+  GPBSetInt32IvarWithField(self, field, (int32_t)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(SFixed32, Int32, int32_t, int32_t)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+int32_t GPBGetSFixed32IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field) {
+  return (int32_t)GPBGetInt32IvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetSFixed32IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field,
+                                 int32_t value) {
+  GPBSetInt32IvarWithField(self, field, (int32_t)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(Fixed32, UInt32, uint32_t, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+uint32_t GPBGetFixed32IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field) {
+  return (uint32_t)GPBGetUInt32IvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetFixed32IvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                uint32_t value) {
+  GPBSetUInt32IvarWithField(self, field, (uint32_t)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(SInt64, Int64, int64_t, int64_t)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+int64_t GPBGetSInt64IvarWithField(GPBMessage *self,
+                                  GPBFieldDescriptor *field) {
+  return (int64_t)GPBGetInt64IvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetSInt64IvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               int64_t value) {
+  GPBSetInt64IvarWithField(self, field, (int64_t)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(SFixed64, Int64, int64_t, int64_t)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+int64_t GPBGetSFixed64IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field) {
+  return (int64_t)GPBGetInt64IvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetSFixed64IvarWithField(GPBMessage *self,
+                                 GPBFieldDescriptor *field,
+                                 int64_t value) {
+  GPBSetInt64IvarWithField(self, field, (int64_t)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(Fixed64, UInt64, uint64_t, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+uint64_t GPBGetFixed64IvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field) {
+  return (uint64_t)GPBGetUInt64IvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetFixed64IvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                uint64_t value) {
+  GPBSetUInt64IvarWithField(self, field, (uint64_t)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(String, Object, NSString*, id)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+NSString* GPBGetStringIvarWithField(GPBMessage *self,
+                                    GPBFieldDescriptor *field) {
+  return (NSString*)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetStringIvarWithField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               NSString* value) {
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(Data, Object, NSData*, id)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+NSData* GPBGetDataIvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field) {
+  return (NSData*)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetDataIvarWithField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             NSData* value) {
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(Message, Object, GPBMessage*, id)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+GPBMessage* GPBGetMessageIvarWithField(GPBMessage *self,
+                                       GPBFieldDescriptor *field) {
+  return (GPBMessage*)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageIvarWithField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                GPBMessage* value) {
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN(Group, Object, GPBMessage*, id)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+GPBMessage* GPBGetGroupIvarWithField(GPBMessage *self,
+                                     GPBFieldDescriptor *field) {
+  return (GPBMessage*)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetGroupIvarWithField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              GPBMessage* value) {
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND-END (10 expansions)
+
+#pragma mark - Misc Dynamic Runtime Utils
+
+void GPBApplyFunctionsToMessageFields(GPBApplyFunctions *functions,
+                                      GPBMessage *msg, void *context) {
+  GPBDescriptor *descriptor = [[msg class] descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    BOOL wasGood;
+    if (GPBFieldIsMapOrArray(field)) {
+      wasGood = (*functions)[GPBApplyFunctionObject](field, context);
+    } else {
+      wasGood = GPBApplyFunctionsBasedOnField(field, functions, context);
+    }
+    if (!wasGood) {
+      break;
+    }
+  }
+}
+
+BOOL GPBApplyFunctionsBasedOnField(GPBFieldDescriptor *field,
+                                   GPBApplyFunctions *functions,
+                                   void *context) {
+  static const GPBApplyFunctionOrder typeMap[GPBTypeCount] = {
+    GPBApplyFunctionBool,
+    GPBApplyFunctionUInt32,
+    GPBApplyFunctionInt32,
+    GPBApplyFunctionFloat,
+    GPBApplyFunctionUInt64,
+    GPBApplyFunctionInt64,
+    GPBApplyFunctionDouble,
+    GPBApplyFunctionInt32,
+    GPBApplyFunctionInt64,
+    GPBApplyFunctionInt32,
+    GPBApplyFunctionInt64,
+    GPBApplyFunctionUInt32,
+    GPBApplyFunctionUInt64,
+    GPBApplyFunctionObject,
+    GPBApplyFunctionObject,
+    GPBApplyFunctionObject,
+    GPBApplyFunctionObject,
+    GPBApplyFunctionInt32
+  };
+  return (*functions)[typeMap[GPBGetFieldType(field)]](field, context);
+}
+
+void GPBApplyStrictFunctionsToMessageFields(GPBApplyStrictFunctions *functions,
+                                            GPBMessage *msg, void *context) {
+  GPBDescriptor *descriptor = [[msg class] descriptor];
+  for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) {
+    GPBApplyFunction function = (*functions)[GPBGetFieldType(fieldDescriptor)];
+    BOOL wasGood = function(fieldDescriptor, context);
+    if (!wasGood) {
+      break;
+    }
+  }
+}
+
+const char *GPBMessageEncodingForSelector(SEL selector, BOOL instanceSel) {
+  Protocol *protocol =
+      objc_getProtocol(GPBStringifySymbol(GPBMessageSignatureProtocol));
+  struct objc_method_description description =
+      protocol_getMethodDescription(protocol, selector, NO, instanceSel);
+  return description.types;
+}
+
+#pragma mark - Text Format Support
+
+static void AppendStringEscaped(NSString *toPrint, NSMutableString *destStr) {
+  [destStr appendString:@"\""];
+  NSUInteger len = [toPrint length];
+  for (NSUInteger i = 0; i < len; ++i) {
+    unichar aChar = [toPrint characterAtIndex:i];
+    switch (aChar) {
+      case '\n': [destStr appendString:@"\\n"];  break;
+      case '\r': [destStr appendString:@"\\r"];  break;
+      case '\t': [destStr appendString:@"\\t"];  break;
+      case '\"': [destStr appendString:@"\\\""]; break;
+      case '\'': [destStr appendString:@"\\\'"]; break;
+      case '\\': [destStr appendString:@"\\\\"]; break;
+      default:
+        [destStr appendFormat:@"%C", aChar];
+        break;
+    }
+  }
+  [destStr appendString:@"\""];
+}
+
+static void AppendBufferAsString(NSData *buffer, NSMutableString *destStr) {
+  const char *src = (const char *)[buffer bytes];
+  size_t srcLen = [buffer length];
+  [destStr appendString:@"\""];
+  for (const char *srcEnd = src + srcLen; src < srcEnd; src++) {
+    switch (*src) {
+      case '\n': [destStr appendString:@"\\n"];  break;
+      case '\r': [destStr appendString:@"\\r"];  break;
+      case '\t': [destStr appendString:@"\\t"];  break;
+      case '\"': [destStr appendString:@"\\\""]; break;
+      case '\'': [destStr appendString:@"\\\'"]; break;
+      case '\\': [destStr appendString:@"\\\\"]; break;
+      default:
+        if (isprint(*src)) {
+          [destStr appendFormat:@"%c", *src];
+        } else {
+          // NOTE: doing hex means you have to worry about the letter after
+          // the hex being another hex char and forcing that to be escaped, so
+          // use octal to keep it simple.
+          [destStr appendFormat:@"\\%03o", (uint8_t)(*src)];
+        }
+        break;
+    }
+  }
+  [destStr appendString:@"\""];
+}
+
+static void AppendTextFormatForMapMessageField(
+    id map, GPBFieldDescriptor *field, NSMutableString *toStr,
+    NSString *lineIndent, NSString *fieldName, NSString *lineEnding) {
+  GPBType keyType = field.mapKeyType;
+  GPBType valueType = GPBGetFieldType(field);
+  BOOL isMessageValue = GPBTypeIsMessage(valueType);
+
+  NSString *msgStartFirst =
+      [NSString stringWithFormat:@"%@%@ {%@\n", lineIndent, fieldName, lineEnding];
+  NSString *msgStart =
+      [NSString stringWithFormat:@"%@%@ {\n", lineIndent, fieldName];
+  NSString *msgEnd = [NSString stringWithFormat:@"%@}\n", lineIndent];
+
+  NSString *keyLine = [NSString stringWithFormat:@"%@  key: ", lineIndent];
+  NSString *valueLine = [NSString stringWithFormat:@"%@  value%s ", lineIndent,
+                                                   (isMessageValue ? "" : ":")];
+
+  __block BOOL isFirst = YES;
+
+  if ((keyType == GPBTypeString) && GPBTypeIsObject(valueType)) {
+    // map is an NSDictionary.
+    NSDictionary *dict = map;
+    [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
+      #pragma unused(stop)
+      [toStr appendString:(isFirst ? msgStartFirst : msgStart)];
+      isFirst = NO;
+
+      [toStr appendString:keyLine];
+      AppendStringEscaped(key, toStr);
+      [toStr appendString:@"\n"];
+
+      [toStr appendString:valueLine];
+      switch (valueType) {
+        case GPBTypeString:
+          AppendStringEscaped(value, toStr);
+          break;
+
+        case GPBTypeData:
+          AppendBufferAsString(value, toStr);
+          break;
+
+        case GPBTypeMessage:
+          [toStr appendString:@"{\n"];
+          NSString *subIndent = [lineIndent stringByAppendingString:@"    "];
+          AppendTextFormatForMessage(value, toStr, subIndent);
+          [toStr appendFormat:@"%@  }", lineIndent];
+          break;
+
+        default:
+          NSCAssert(NO, @"Can't happen");
+          break;
+      }
+      [toStr appendString:@"\n"];
+
+      [toStr appendString:msgEnd];
+    }];
+  } else {
+    // map is one of the GPB*Dictionary classes, type doesn't matter.
+    GPBInt32Int32Dictionary *dict = map;
+    [dict enumerateForTextFormat:^(id keyObj, id valueObj) {
+      [toStr appendString:(isFirst ? msgStartFirst : msgStart)];
+      isFirst = NO;
+
+      // Key always is a NSString.
+      if (keyType == GPBTypeString) {
+        [toStr appendString:keyLine];
+        AppendStringEscaped(keyObj, toStr);
+        [toStr appendString:@"\n"];
+      } else {
+        [toStr appendFormat:@"%@%@\n", keyLine, keyObj];
+      }
+
+      [toStr appendString:valueLine];
+      switch (valueType) {
+        case GPBTypeString:
+          AppendStringEscaped(valueObj, toStr);
+          break;
+
+        case GPBTypeData:
+          AppendBufferAsString(valueObj, toStr);
+          break;
+
+        case GPBTypeMessage:
+          [toStr appendString:@"{\n"];
+          NSString *subIndent = [lineIndent stringByAppendingString:@"    "];
+          AppendTextFormatForMessage(valueObj, toStr, subIndent);
+          [toStr appendFormat:@"%@  }", lineIndent];
+          break;
+
+        case GPBTypeEnum: {
+          int32_t enumValue = [valueObj intValue];
+          NSString *valueStr = nil;
+          GPBEnumDescriptor *descriptor = field.enumDescriptor;
+          if (descriptor) {
+            valueStr = [descriptor textFormatNameForValue:enumValue];
+          }
+          if (valueStr) {
+            [toStr appendString:valueStr];
+          } else {
+            [toStr appendFormat:@"%d", enumValue];
+          }
+          break;
+        }
+
+        default:
+          NSCAssert(valueType != GPBTypeGroup, @"Can't happen");
+          // Everything else is a NSString.
+          [toStr appendString:valueObj];
+          break;
+      }
+      [toStr appendString:@"\n"];
+
+      [toStr appendString:msgEnd];
+    }];
+  }
+}
+
+static void AppendTextFormatForMessageField(GPBMessage *message,
+                                            GPBFieldDescriptor *field,
+                                            NSMutableString *toStr,
+                                            NSString *lineIndent) {
+  id array;
+  NSUInteger arrayCount;
+  GPBFieldType fieldType = field.fieldType;
+  switch (fieldType) {
+    case GPBFieldTypeSingle:
+      array = nil;
+      arrayCount = (GPBGetHasIvarField(message, field) ? 1 : 0);
+      break;
+
+    case GPBFieldTypeRepeated:
+      array = GPBGetObjectIvarWithFieldNoAutocreate(message, field);
+      arrayCount = [(NSArray *)array count];
+      break;
+
+    case GPBFieldTypeMap: {
+      // Could be a GPB*Dictionary or NSMutableDictionary, type doesn't matter,
+      // just want count.
+      array = GPBGetObjectIvarWithFieldNoAutocreate(message, field);
+      arrayCount = [(NSArray *)array count];
+      break;
+    }
+  }
+
+  if (arrayCount == 0) {
+    // Nothing to print, out of here.
+    return;
+  }
+
+  NSString *lineEnding = @"";
+
+  // If the name can't be reversed or support for extra info was turned off,
+  // this can return nil.
+  NSString *fieldName = [field textFormatName];
+  if ([fieldName length] == 0) {
+    fieldName = [NSString stringWithFormat:@"%u", GPBFieldNumber(field)];
+    // If there is only one entry, put the objc name as a comment, other wise
+    // add it before the the repeated values.
+    if (arrayCount > 1) {
+      [toStr appendFormat:@"%@# %@\n", lineIndent, field.name];
+    } else {
+      lineEnding = [NSString stringWithFormat:@"  # %@", field.name];
+    }
+  }
+
+  if (fieldType == GPBFieldTypeMap) {
+    AppendTextFormatForMapMessageField(array, field, toStr, lineIndent,
+                                       fieldName, lineEnding);
+    return;
+  }
+
+  const BOOL isRepeated = (array != nil);
+
+  GPBType fieldDataType = GPBGetFieldType(field);
+  BOOL isMessageField = GPBTypeIsMessage(fieldDataType);
+  for (NSUInteger j = 0; j < arrayCount; ++j) {
+    // Start the line.
+    [toStr appendFormat:@"%@%@%s ", lineIndent, fieldName,
+                        (isMessageField ? "" : ":")];
+
+    // The value.
+    switch (fieldDataType) {
+#define FIELD_CASE(GPBTYPE, CTYPE, ARRAY_TYPE, ...)                          \
+  case GPBType##GPBTYPE: {                                                   \
+    CTYPE v = (isRepeated ? [(GPB##ARRAY_TYPE##Array *)array valueAtIndex:j] \
+                          : GPBGet##GPBTYPE##IvarWithField(message, field)); \
+    [toStr appendFormat:__VA_ARGS__, v];                                     \
+    break;                                                                   \
+  }
+
+      FIELD_CASE(Int32, int32_t, Int32, @"%d")
+      FIELD_CASE(SInt32, int32_t, Int32, @"%d")
+      FIELD_CASE(SFixed32, int32_t, Int32, @"%d")
+      FIELD_CASE(UInt32, uint32_t, UInt32, @"%u")
+      FIELD_CASE(Fixed32, uint32_t, UInt32, @"%u")
+      FIELD_CASE(Int64, int64_t, Int64, @"%lld")
+      FIELD_CASE(SInt64, int64_t, Int64, @"%lld")
+      FIELD_CASE(SFixed64, int64_t, Int64, @"%lld")
+      FIELD_CASE(UInt64, uint64_t, UInt64, @"%llu")
+      FIELD_CASE(Fixed64, uint64_t, UInt64, @"%llu")
+      FIELD_CASE(Float, float, Float, @"%.*g", FLT_DIG)
+      FIELD_CASE(Double, double, Double, @"%.*lg", DBL_DIG)
+
+#undef FIELD_CASE
+
+      case GPBTypeEnum: {
+        int32_t v = (isRepeated ? [(GPBEnumArray *)array rawValueAtIndex:j]
+                                : GPBGetInt32IvarWithField(message, field));
+        NSString *valueStr = nil;
+        GPBEnumDescriptor *descriptor = field.enumDescriptor;
+        if (descriptor) {
+          valueStr = [descriptor textFormatNameForValue:v];
+        }
+        if (valueStr) {
+          [toStr appendString:valueStr];
+        } else {
+          [toStr appendFormat:@"%d", v];
+        }
+        break;
+      }
+
+      case GPBTypeBool: {
+        BOOL v = (isRepeated ? [(GPBBoolArray *)array valueAtIndex:j]
+                             : GPBGetBoolIvarWithField(message, field));
+        [toStr appendString:(v ? @"true" : @"false")];
+        break;
+      }
+
+      case GPBTypeString: {
+        NSString *v = (isRepeated ? [(NSArray *)array objectAtIndex:j]
+                                  : GPBGetStringIvarWithField(message, field));
+        AppendStringEscaped(v, toStr);
+        break;
+      }
+
+      case GPBTypeData: {
+        NSData *v = (isRepeated ? [(NSArray *)array objectAtIndex:j]
+                                : GPBGetDataIvarWithField(message, field));
+        AppendBufferAsString(v, toStr);
+        break;
+      }
+
+      case GPBTypeGroup:
+      case GPBTypeMessage: {
+        GPBMessage *v =
+            (isRepeated ? [(NSArray *)array objectAtIndex:j]
+                        : GPBGetObjectIvarWithField(message, field));
+        [toStr appendFormat:@"{%@\n", lineEnding];
+        NSString *subIndent = [lineIndent stringByAppendingString:@"  "];
+        AppendTextFormatForMessage(v, toStr, subIndent);
+        [toStr appendFormat:@"%@}", lineIndent];
+        lineEnding = @"";
+        break;
+      }
+
+    }  // switch(fieldDataType)
+
+    // End the line.
+    [toStr appendFormat:@"%@\n", lineEnding];
+
+  }  // for(arrayCount)
+}
+
+static void AppendTextFormatForMessageExtensionRange(GPBMessage *message,
+                                                     NSArray *activeExtensions,
+                                                     GPBExtensionRange range,
+                                                     NSMutableString *toStr,
+                                                     NSString *lineIndent) {
+  uint32_t start = range.start;
+  uint32_t end = range.end;
+  for (GPBExtensionField *extension in activeExtensions) {
+    uint32_t fieldNumber = extension.fieldNumber;
+    if (fieldNumber < start) {
+      // Not there yet.
+      continue;
+    }
+    if (fieldNumber > end) {
+      // Done.
+      break;
+    }
+
+    id rawExtValue = [message getExtension:extension];
+    GPBExtensionDescriptor *extDescriptor = [extension descriptor];
+    BOOL isRepeated = extDescriptor.isRepeated;
+
+    NSUInteger numValues = 1;
+    NSString *lineEnding = @"";
+    if (isRepeated) {
+      numValues = [(NSArray *)rawExtValue count];
+    }
+
+    NSString *singletonName = extension.descriptor.singletonName;
+    if (numValues == 1) {
+      lineEnding = [NSString stringWithFormat:@"  # [%@]", singletonName];
+    } else {
+      [toStr appendFormat:@"%@# [%@]\n", lineIndent, singletonName];
+    }
+
+    GPBType extType = extDescriptor.type;
+    for (NSUInteger j = 0; j < numValues; ++j) {
+      id curValue = (isRepeated ? [rawExtValue objectAtIndex:j] : rawExtValue);
+
+      // Start the line.
+      [toStr appendFormat:@"%@%u%s ", lineIndent, fieldNumber,
+                          (GPBTypeIsMessage(extType) ? "" : ":")];
+
+      // The value.
+      switch (extType) {
+#define FIELD_CASE(GPBTYPE, CTYPE, NUMSELECTOR, ...) \
+  case GPBType##GPBTYPE: {                           \
+    CTYPE v = [(NSNumber *)curValue NUMSELECTOR];    \
+    [toStr appendFormat:__VA_ARGS__, v];             \
+    break;                                           \
+  }
+
+        FIELD_CASE(Int32, int32_t, intValue, @"%d")
+        FIELD_CASE(SInt32, int32_t, intValue, @"%d")
+        FIELD_CASE(SFixed32, int32_t, unsignedIntValue, @"%d")
+        FIELD_CASE(UInt32, uint32_t, unsignedIntValue, @"%u")
+        FIELD_CASE(Fixed32, uint32_t, unsignedIntValue, @"%u")
+        FIELD_CASE(Int64, int64_t, longLongValue, @"%lld")
+        FIELD_CASE(SInt64, int64_t, longLongValue, @"%lld")
+        FIELD_CASE(SFixed64, int64_t, longLongValue, @"%lld")
+        FIELD_CASE(UInt64, uint64_t, unsignedLongLongValue, @"%llu")
+        FIELD_CASE(Fixed64, uint64_t, unsignedLongLongValue, @"%llu")
+        FIELD_CASE(Float, float, floatValue, @"%.*g", FLT_DIG)
+        FIELD_CASE(Double, double, doubleValue, @"%.*lg", DBL_DIG)
+        // TODO: Add a comment with the enum name from enum descriptors
+        // (might not be real value, so leave it as a comment, ObjC compiler
+        // name mangles differently).  Doesn't look like we actually generate
+        // an enum descriptor reference like we do for normal fields, so this
+        // will take a compiler change.
+        FIELD_CASE(Enum, int32_t, intValue, @"%d")
+
+#undef FIELD_CASE
+
+        case GPBTypeBool:
+          [toStr appendString:([(NSNumber *)curValue boolValue] ? @"true"
+                                                                : @"false")];
+          break;
+
+        case GPBTypeString:
+          AppendStringEscaped(curValue, toStr);
+          break;
+
+        case GPBTypeData:
+          AppendBufferAsString((NSData *)curValue, toStr);
+          break;
+
+        case GPBTypeGroup:
+        case GPBTypeMessage: {
+          [toStr appendFormat:@"{%@\n", lineEnding];
+          NSString *subIndent = [lineIndent stringByAppendingString:@"  "];
+          AppendTextFormatForMessage(curValue, toStr, subIndent);
+          [toStr appendFormat:@"%@}", lineIndent];
+          lineEnding = @"";
+          break;
+        }
+
+      }  // switch(extType)
+
+    }  //  for(numValues)
+
+    // End the line.
+    [toStr appendFormat:@"%@\n", lineEnding];
+
+  }  // for..in(activeExtensions)
+}
+
+static void AppendTextFormatForMessage(GPBMessage *message,
+                                       NSMutableString *toStr,
+                                       NSString *lineIndent) {
+  GPBDescriptor *descriptor = [message descriptor];
+  NSArray *fieldsArray = descriptor->fields_;
+  NSUInteger fieldCount = fieldsArray.count;
+  const GPBExtensionRange *extensionRanges = descriptor.extensionRanges;
+  NSUInteger extensionRangesCount = descriptor.extensionRangesCount;
+  NSArray *activeExtensions = [message sortedExtensionsInUse];
+  for (NSUInteger i = 0, j = 0; i < fieldCount || j < extensionRangesCount;) {
+    if (i == fieldCount) {
+      AppendTextFormatForMessageExtensionRange(
+          message, activeExtensions, extensionRanges[j++], toStr, lineIndent);
+    } else if (j == extensionRangesCount ||
+               GPBFieldNumber(fieldsArray[i]) < extensionRanges[j].start) {
+      AppendTextFormatForMessageField(message, fieldsArray[i++], toStr,
+                                      lineIndent);
+    } else {
+      AppendTextFormatForMessageExtensionRange(
+          message, activeExtensions, extensionRanges[j++], toStr, lineIndent);
+    }
+  }
+
+  NSString *unknownFieldsStr =
+      GPBTextFormatForUnknownFieldSet(message.unknownFields, lineIndent);
+  if ([unknownFieldsStr length] > 0) {
+    [toStr appendFormat:@"%@# --- Unknown fields ---\n", lineIndent];
+    [toStr appendString:unknownFieldsStr];
+  }
+}
+
+NSString *GPBTextFormatForMessage(GPBMessage *message, NSString *lineIndent) {
+  if (message == nil) return nil;
+  if (lineIndent == nil) lineIndent = @"";
+
+  NSMutableString *buildString = [NSMutableString string];
+  AppendTextFormatForMessage(message, buildString, lineIndent);
+  return buildString;
+}
+
+NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet *unknownSet,
+                                          NSString *lineIndent) {
+  if (unknownSet == nil) return nil;
+  if (lineIndent == nil) lineIndent = @"";
+
+  NSMutableString *result = [NSMutableString string];
+  for (GPBField *field in [unknownSet sortedFields]) {
+    int32_t fieldNumber = [field number];
+
+#define PRINT_LOOP(PROPNAME, CTYPE, FORMAT)                                   \
+  [field.PROPNAME                                                             \
+      enumerateValuesWithBlock:^(CTYPE value, NSUInteger idx, BOOL * stop) {  \
+    _Pragma("unused(idx, stop)");                                             \
+    [result                                                                   \
+        appendFormat:@"%@%d: " #FORMAT "\n", lineIndent, fieldNumber, value]; \
+      }];
+
+    PRINT_LOOP(varintList, uint64_t, %llu);
+    PRINT_LOOP(fixed32List, uint32_t, 0x%X);
+    PRINT_LOOP(fixed64List, uint64_t, 0x%llX);
+
+#undef PRINT_LOOP
+
+    // NOTE: C++ version of TextFormat tries to parse this as a message
+    // and print that if it succeeds.
+    for (NSData *data in field.lengthDelimitedList) {
+      [result appendFormat:@"%@%d: ", lineIndent, fieldNumber];
+      AppendBufferAsString(data, result);
+      [result appendString:@"\n"];
+    }
+
+    for (GPBUnknownFieldSet *subUnknownSet in field.groupList) {
+      [result appendFormat:@"%@%d: {\n", lineIndent, fieldNumber];
+      NSString *subIndent = [lineIndent stringByAppendingString:@"  "];
+      NSString *subUnknwonSetStr =
+          GPBTextFormatForUnknownFieldSet(subUnknownSet, subIndent);
+      [result appendString:subUnknwonSetStr];
+      [result appendFormat:@"%@}\n", lineIndent];
+    }
+  }
+  return result;
+}
+
+// Helpers to decode a varint. Not using GPBCodedInputStream version because
+// that needs a state object, and we don't want to create an input stream out
+// of the data.
+static inline int8_t ReadRawByteFromData(const uint8_t **data) {
+  int8_t result = *((int8_t *)(*data));
+  ++(*data);
+  return result;
+}
+
+static inline int32_t ReadRawVarint32FromData(const uint8_t **data) {
+  int8_t tmp = ReadRawByteFromData(data);
+  if (tmp >= 0) {
+    return tmp;
+  }
+  int32_t result = tmp & 0x7f;
+  if ((tmp = ReadRawByteFromData(data)) >= 0) {
+    result |= tmp << 7;
+  } else {
+    result |= (tmp & 0x7f) << 7;
+    if ((tmp = ReadRawByteFromData(data)) >= 0) {
+      result |= tmp << 14;
+    } else {
+      result |= (tmp & 0x7f) << 14;
+      if ((tmp = ReadRawByteFromData(data)) >= 0) {
+        result |= tmp << 21;
+      } else {
+        result |= (tmp & 0x7f) << 21;
+        result |= (tmp = ReadRawByteFromData(data)) << 28;
+        if (tmp < 0) {
+          // Discard upper 32 bits.
+          for (int i = 0; i < 5; i++) {
+            if (ReadRawByteFromData(data) >= 0) {
+              return result;
+            }
+          }
+          [NSException raise:NSParseErrorException
+                      format:@"Unable to read varint32"];
+        }
+      }
+    }
+  }
+  return result;
+}
+
+NSString *GPBDecodeTextFormatName(const uint8_t *decodeData, int32_t key,
+                                  NSString *inputStr) {
+  // decodData form:
+  //  varint32: num entries
+  //  for each entry:
+  //    varint32: key
+  //    bytes*: decode data
+  //
+  // decode data one of two forms:
+  //  1: a \0 followed by the string followed by an \0
+  //  2: bytecodes to transform an input into the right thing, ending with \0
+  //
+  // the bytes codes are of the form:
+  //  0xabbccccc
+  //  0x0 (all zeros), end.
+  //  a - if set, add an underscore
+  //  bb - 00 ccccc bytes as is
+  //  bb - 10 ccccc upper first, as is on rest, ccccc byte total
+  //  bb - 01 ccccc lower first, as is on rest, ccccc byte total
+  //  bb - 11 ccccc all upper, ccccc byte total
+
+  if (!decodeData || !inputStr) {
+    return nil;
+  }
+
+  // Find key
+  const uint8_t *scan = decodeData;
+  int32_t numEntries = ReadRawVarint32FromData(&scan);
+  BOOL foundKey = NO;
+  while (!foundKey && (numEntries > 0)) {
+    --numEntries;
+    int32_t dataKey = ReadRawVarint32FromData(&scan);
+    if (dataKey == key) {
+      foundKey = YES;
+    } else {
+      // If it is a inlined string, it will start with \0; if it is bytecode it
+      // will start with a code. So advance one (skipping the inline string
+      // marker), and then loop until reaching the end marker (\0).
+      ++scan;
+      while (*scan != 0) ++scan;
+      // Now move past the end marker.
+      ++scan;
+    }
+  }
+
+  if (!foundKey) {
+    return nil;
+  }
+
+  // Decode
+
+  if (*scan == 0) {
+    // Inline string. Move over the marker, and NSString can take it as
+    // UTF8.
+    ++scan;
+    NSString *result = [NSString stringWithUTF8String:(const char *)scan];
+    return result;
+  }
+
+  NSMutableString *result =
+      [NSMutableString stringWithCapacity:[inputStr length]];
+
+  const uint8_t kAddUnderscore  = 0b10000000;
+  const uint8_t kOpMask         = 0b01100000;
+  // const uint8_t kOpAsIs        = 0b00000000;
+  const uint8_t kOpFirstUpper     = 0b01000000;
+  const uint8_t kOpFirstLower     = 0b00100000;
+  const uint8_t kOpAllUpper       = 0b01100000;
+  const uint8_t kSegmentLenMask = 0b00011111;
+
+  NSInteger i = 0;
+  for (; *scan != 0; ++scan) {
+    if (*scan & kAddUnderscore) {
+      [result appendString:@"_"];
+    }
+    int segmentLen = *scan & kSegmentLenMask;
+    uint8_t decodeOp = *scan & kOpMask;
+
+    // Do op specific handling of the first character.
+    if (decodeOp == kOpFirstUpper) {
+      unichar c = [inputStr characterAtIndex:i];
+      [result appendFormat:@"%c", toupper((char)c)];
+      ++i;
+      --segmentLen;
+    } else if (decodeOp == kOpFirstLower) {
+      unichar c = [inputStr characterAtIndex:i];
+      [result appendFormat:@"%c", tolower((char)c)];
+      ++i;
+      --segmentLen;
+    }
+    // else op == kOpAsIs || op == kOpAllUpper
+
+    // Now pull over the rest of the length for this segment.
+    for (int x = 0; x < segmentLen; ++x) {
+      unichar c = [inputStr characterAtIndex:(i + x)];
+      if (decodeOp == kOpAllUpper) {
+        [result appendFormat:@"%c", toupper((char)c)];
+      } else {
+        [result appendFormat:@"%C", c];
+      }
+    }
+    i += segmentLen;
+  }
+
+  return result;
+}
+
+#pragma mark - GPBMessageSignatureProtocol
+
+// A series of selectors that are used solely to get @encoding values
+// for them by the dynamic protobuf runtime code. An object using the protocol
+// needs to be declared for the protocol to be valid at runtime.
+@interface GPBMessageSignatureProtocol : NSObject<GPBMessageSignatureProtocol>
+@end
+@implementation GPBMessageSignatureProtocol
+@end

+ 426 - 0
objectivec/GPBUtilities_PackagePrivate.h

@@ -0,0 +1,426 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBUtilities.h"
+
+#import "GPBDescriptor_PackagePrivate.h"
+
+// Macros for stringifying library symbols. These are used in the generated
+// PB descriptor classes wherever a library symbol name is represented as a
+// string. See README.google for more information.
+#define GPBStringify(S) #S
+#define GPBStringifySymbol(S) GPBStringify(S)
+
+#define GPBNSStringify(S) @#S
+#define GPBNSStringifySymbol(S) GPBNSStringify(S)
+
+// Constant to internally mark when there is no has bit.
+#define GPBNoHasBit INT32_MAX
+
+CF_EXTERN_C_BEGIN
+
+// Conversion functions for de/serializing floating point types.
+
+GPB_INLINE int64_t GPBConvertDoubleToInt64(double v) {
+  union { double f; int64_t i; } u;
+  u.f = v;
+  return u.i;
+}
+
+GPB_INLINE int32_t GPBConvertFloatToInt32(float v) {
+  union { float f; int32_t i; } u;
+  u.f = v;
+  return u.i;
+}
+
+GPB_INLINE double GPBConvertInt64ToDouble(int64_t v) {
+  union { double f; int64_t i; } u;
+  u.i = v;
+  return u.f;
+}
+
+GPB_INLINE float GPBConvertInt32ToFloat(int32_t v) {
+  union { float f; int32_t i; } u;
+  u.i = v;
+  return u.f;
+}
+
+GPB_INLINE int32_t GPBLogicalRightShift32(int32_t value, int32_t spaces) {
+  return (int32_t)((uint32_t)(value) >> spaces);
+}
+
+GPB_INLINE int64_t GPBLogicalRightShift64(int64_t value, int32_t spaces) {
+  return (int64_t)((uint64_t)(value) >> spaces);
+}
+
+// Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE int32_t GPBDecodeZigZag32(uint32_t n) {
+  return GPBLogicalRightShift32(n, 1) ^ -(n & 1);
+}
+
+// Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE int64_t GPBDecodeZigZag64(uint64_t n) {
+  return GPBLogicalRightShift64(n, 1) ^ -(n & 1);
+}
+
+// Encode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE uint32_t GPBEncodeZigZag32(int32_t n) {
+  // Note:  the right-shift must be arithmetic
+  return (n << 1) ^ (n >> 31);
+}
+
+// Encode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE uint64_t GPBEncodeZigZag64(int64_t n) {
+  // Note:  the right-shift must be arithmetic
+  return (n << 1) ^ (n >> 63);
+}
+
+GPB_INLINE BOOL GPBTypeIsObject(GPBType type) {
+  switch (type) {
+    case GPBTypeData:
+    case GPBTypeString:
+    case GPBTypeMessage:
+    case GPBTypeGroup:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+GPB_INLINE BOOL GPBTypeIsMessage(GPBType type) {
+  switch (type) {
+    case GPBTypeMessage:
+    case GPBTypeGroup:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+GPB_INLINE BOOL GPBTypeIsEnum(GPBType type) { return type == GPBTypeEnum; }
+
+GPB_INLINE BOOL GPBFieldTypeIsMessage(GPBFieldDescriptor *field) {
+  return GPBTypeIsMessage(field->description_->type);
+}
+
+GPB_INLINE BOOL GPBFieldTypeIsObject(GPBFieldDescriptor *field) {
+  return GPBTypeIsObject(field->description_->type);
+}
+
+GPB_INLINE BOOL GPBExtensionIsMessage(GPBExtensionDescriptor *ext) {
+  return GPBTypeIsMessage(ext->description_->type);
+}
+
+// The field is an array/map or it has an object value.
+GPB_INLINE BOOL GPBFieldStoresObject(GPBFieldDescriptor *field) {
+  GPBMessageFieldDescription *desc = field->description_;
+  if ((desc->flags & (GPBFieldRepeated | GPBFieldMapKeyMask)) != 0) {
+    return YES;
+  }
+  return GPBTypeIsObject(desc->type);
+}
+
+BOOL GPBGetHasIvar(GPBMessage *self, int32_t index, uint32_t fieldNumber);
+void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber,
+                   BOOL value);
+uint32_t GPBGetHasOneof(GPBMessage *self, int32_t index);
+
+GPB_INLINE BOOL
+GPBGetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field) {
+  GPBMessageFieldDescription *fieldDesc = field->description_;
+  return GPBGetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number);
+}
+GPB_INLINE void GPBSetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field,
+                                   BOOL value) {
+  GPBMessageFieldDescription *fieldDesc = field->description_;
+  GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, value);
+}
+
+void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof,
+                        uint32_t fieldNumberNotToClear);
+
+//%PDDM-DEFINE GPB_IVAR_SET_DECL(NAME, TYPE)
+//%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self,
+//%            NAME$S                     GPBFieldDescriptor *field,
+//%            NAME$S                     TYPE value,
+//%            NAME$S                     GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetBoolIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field,
+                                     BOOL value,
+                                     GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetInt32IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int32_t value,
+                                      GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint32_t value,
+                                       GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetInt64IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int64_t value,
+                                      GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint64_t value,
+                                       GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Float, float)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetFloatIvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      float value,
+                                      GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Double, double)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       double value,
+                                       GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Enum, int32_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetEnumIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field,
+                                     int32_t value,
+                                     GPBFileSyntax syntax);
+//%PDDM-EXPAND-END (8 expansions)
+
+int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax);
+
+id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
+
+void GPBSetObjectIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field, id value,
+                                       GPBFileSyntax syntax);
+void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
+                                               GPBFieldDescriptor *field,
+                                               id __attribute__((ns_consumed))
+                                               value,
+                                               GPBFileSyntax syntax);
+
+// GPBGetObjectIvarWithField will automatically create the field (message) if
+// it doesn't exist. GPBGetObjectIvarWithFieldNoAutocreate will return nil.
+id GPBGetObjectIvarWithFieldNoAutocreate(GPBMessage *self,
+                                         GPBFieldDescriptor *field);
+
+void GPBSetAutocreatedRetainedObjectIvarWithField(
+    GPBMessage *self, GPBFieldDescriptor *field,
+    id __attribute__((ns_consumed)) value);
+
+// Clears and releases the autocreated message ivar, if it's autocreated. If
+// it's not set as autocreated, this method does nothing.
+void GPBClearAutocreatedMessageIvarWithField(GPBMessage *self,
+                                             GPBFieldDescriptor *field);
+
+// Utilities for applying various functions based on Objective C types.
+
+// A basic functor that is passed a field and a context. Returns YES
+// if the calling function should continue processing, and NO if the calling
+// function should stop processing.
+typedef BOOL (*GPBApplyFunction)(GPBFieldDescriptor *field, void *context);
+
+// Functions called for various types. See ApplyFunctionsToMessageFields.
+typedef enum {
+  GPBApplyFunctionObject,
+  GPBApplyFunctionBool,
+  GPBApplyFunctionInt32,
+  GPBApplyFunctionUInt32,
+  GPBApplyFunctionInt64,
+  GPBApplyFunctionUInt64,
+  GPBApplyFunctionFloat,
+  GPBApplyFunctionDouble,
+} GPBApplyFunctionOrder;
+
+enum {
+  // A count of the number of types in GPBApplyFunctionOrder. Separated out
+  // from the GPBApplyFunctionOrder enum to avoid warnings regarding not
+  // handling GPBApplyFunctionCount in switch statements.
+  GPBApplyFunctionCount = GPBApplyFunctionDouble + 1
+};
+
+typedef GPBApplyFunction GPBApplyFunctions[GPBApplyFunctionCount];
+
+// Functions called for various types.
+// See ApplyStrictFunctionsToMessageFields.
+// They are in the same order as the GPBTypes enum.
+typedef GPBApplyFunction GPBApplyStrictFunctions[GPBTypeCount];
+
+// A macro for easily initializing a GPBApplyFunctions struct
+// GPBApplyFunctions foo = GPBAPPLY_FUNCTIONS_INIT(Foo);
+#define GPBAPPLY_FUNCTIONS_INIT(PREFIX) \
+  { \
+    PREFIX##Object,  \
+    PREFIX##Bool,    \
+    PREFIX##Int32,   \
+    PREFIX##UInt32,  \
+    PREFIX##Int64,   \
+    PREFIX##UInt64,  \
+    PREFIX##Float,   \
+    PREFIX##Double,  \
+  }
+
+// A macro for easily initializing a GPBApplyStrictFunctions struct
+// GPBApplyStrictFunctions foo = GPBAPPLY_STRICT_FUNCTIONS_INIT(Foo);
+// These need to stay in the same order as
+// the GPBType enum.
+#define GPBAPPLY_STRICT_FUNCTIONS_INIT(PREFIX) \
+  { \
+    PREFIX##Bool,     \
+    PREFIX##Fixed32,  \
+    PREFIX##SFixed32, \
+    PREFIX##Float,    \
+    PREFIX##Fixed64,  \
+    PREFIX##SFixed64, \
+    PREFIX##Double,   \
+    PREFIX##Int32,    \
+    PREFIX##Int64,    \
+    PREFIX##SInt32,   \
+    PREFIX##SInt64,   \
+    PREFIX##UInt32,   \
+    PREFIX##UInt64,   \
+    PREFIX##Data,     \
+    PREFIX##String,   \
+    PREFIX##Message,  \
+    PREFIX##Group,    \
+    PREFIX##Enum,     \
+}
+
+// Iterates over the fields of a proto |msg| and applies the functions in
+// |functions| to them with |context|. If one of the functions in |functions|
+// returns NO, it will return immediately and not process the rest of the
+// ivars. The types in the fields are mapped so:
+// Int32, Enum, SInt32 and SFixed32 will be mapped to the int32Function,
+// UInt32 and Fixed32 will be mapped to the uint32Function,
+// Bytes, String, Message and Group will be mapped to the objectFunction,
+// etc..
+// If you require more specific mappings look at
+// GPBApplyStrictFunctionsToMessageFields.
+void GPBApplyFunctionsToMessageFields(GPBApplyFunctions *functions,
+                                      GPBMessage *msg, void *context);
+
+// Iterates over the fields of a proto |msg| and applies the functions in
+// |functions| to them with |context|. If one of the functions in |functions|
+// returns NO, it will return immediately and not process the rest of the
+// ivars. The types in the fields are mapped directly:
+// Int32 -> functions[GPBTypeInt32],
+// SFixed32 -> functions[GPBTypeSFixed32],
+// etc...
+// If you can use looser mappings look at GPBApplyFunctionsToMessageFields.
+void GPBApplyStrictFunctionsToMessageFields(GPBApplyStrictFunctions *functions,
+                                            GPBMessage *msg, void *context);
+
+// Applies the appropriate function in |functions| based on |field|.
+// Returns the value from function(name, context).
+// Throws an exception if the type is unrecognized.
+BOOL GPBApplyFunctionsBasedOnField(GPBFieldDescriptor *field,
+                                   GPBApplyFunctions *functions, void *context);
+
+// Returns an Objective C encoding for |selector|. |instanceSel| should be
+// YES if it's an instance selector (as opposed to a class selector).
+// |selector| must be a selector from MessageSignatureProtocol.
+const char *GPBMessageEncodingForSelector(SEL selector, BOOL instanceSel);
+
+// Helper for text format name encoding.
+// decodeData is the data describing the sepecial decodes.
+// key and inputString are the input that needs decoding.
+NSString *GPBDecodeTextFormatName(const uint8_t *decodeData, int32_t key,
+                                  NSString *inputString);
+
+// A series of selectors that are used solely to get @encoding values
+// for them by the dynamic protobuf runtime code. See
+// GPBMessageEncodingForSelector for details.
+@protocol GPBMessageSignatureProtocol
+@optional
+
+#define GPB_MESSAGE_SIGNATURE_ENTRY(TYPE, NAME) \
+  -(TYPE)get##NAME;                             \
+  -(void)set##NAME : (TYPE)value;               \
+  -(TYPE)get##NAME##AtIndex : (NSUInteger)index;
+
+GPB_MESSAGE_SIGNATURE_ENTRY(BOOL, Bool)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint32_t, Fixed32)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, SFixed32)
+GPB_MESSAGE_SIGNATURE_ENTRY(float, Float)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint64_t, Fixed64)
+GPB_MESSAGE_SIGNATURE_ENTRY(int64_t, SFixed64)
+GPB_MESSAGE_SIGNATURE_ENTRY(double, Double)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, Int32)
+GPB_MESSAGE_SIGNATURE_ENTRY(int64_t, Int64)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, SInt32)
+GPB_MESSAGE_SIGNATURE_ENTRY(int64_t, SInt64)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint32_t, UInt32)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint64_t, UInt64)
+GPB_MESSAGE_SIGNATURE_ENTRY(NSData *, Data)
+GPB_MESSAGE_SIGNATURE_ENTRY(NSString *, String)
+GPB_MESSAGE_SIGNATURE_ENTRY(GPBMessage *, Message)
+GPB_MESSAGE_SIGNATURE_ENTRY(GPBMessage *, Group)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, Enum)
+
+#undef GPB_MESSAGE_SIGNATURE_ENTRY
+
+- (id)getArray;
+- (void)setArray:(NSArray *)array;
++ (id)getClassValue;
+@end
+
+CF_EXTERN_C_END

+ 48 - 0
objectivec/GPBWellKnownTypes.h

@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "google/protobuf/Timestamp.pbobjc.h"
+#import "google/protobuf/Duration.pbobjc.h"
+
+// Extension to GPBTimestamp to work with standard Foundation time/date types.
+@interface GPBTimestamp (GBPWellKnownTypes)
+@property(nonatomic, readwrite, strong) NSDate *date;
+@property(nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
+- (instancetype)initWithDate:(NSDate *)date;
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970;
+@end
+
+// Extension to GPBDuration to work with standard Foundation time type.
+@interface GPBDuration (GBPWellKnownTypes)
+@property(nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970;
+@end

+ 117 - 0
objectivec/GPBWellKnownTypes.m

@@ -0,0 +1,117 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Importing sources here to force the linker to include our category methods in
+// the static library. If these were compiled separately, the category methods
+// below would be stripped by the linker.
+
+#import "google/protobuf/Timestamp.pbobjc.m"
+#import "google/protobuf/Duration.pbobjc.m"
+#import "GPBWellKnownTypes.h"
+
+static NSTimeInterval TimeIntervalSince1970FromSecondsAndNanos(int64_t seconds,
+                                                               int32_t nanos) {
+  return seconds + (NSTimeInterval)nanos / 1e9;
+}
+
+static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time,
+                                                        int64_t *outSeconds) {
+  NSTimeInterval seconds;
+  NSTimeInterval nanos = modf(time, &seconds);
+  nanos *= 1e9;
+  *outSeconds = (int64_t)seconds;
+  return (int32_t)nanos;
+}
+
+@implementation GPBTimestamp (GBPWellKnownTypes)
+
+- (instancetype)initWithDate:(NSDate *)date {
+  return [self initWithTimeIntervalSince1970:date.timeIntervalSince1970];
+}
+
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  if ((self = [super init])) {
+    int64_t seconds;
+    int32_t nanos = SecondsAndNanosFromTimeIntervalSince1970(
+        timeIntervalSince1970, &seconds);
+    self.seconds = seconds;
+    self.nanos = nanos;
+  }
+  return self;
+}
+
+- (NSDate *)date {
+  return [NSDate dateWithTimeIntervalSince1970:self.timeIntervalSince1970];
+}
+
+- (void)setDate:(NSDate *)date {
+  self.timeIntervalSince1970 = date.timeIntervalSince1970;
+}
+
+- (NSTimeInterval)timeIntervalSince1970 {
+  return TimeIntervalSince1970FromSecondsAndNanos(self.seconds, self.nanos);
+}
+
+- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  int64_t seconds;
+  int32_t nanos =
+      SecondsAndNanosFromTimeIntervalSince1970(timeIntervalSince1970, &seconds);
+  self.seconds = seconds;
+  self.nanos = nanos;
+}
+
+@end
+
+@implementation GPBDuration (GBPWellKnownTypes)
+
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  if ((self = [super init])) {
+    int64_t seconds;
+    int32_t nanos = SecondsAndNanosFromTimeIntervalSince1970(
+        timeIntervalSince1970, &seconds);
+    self.seconds = seconds;
+    self.nanos = nanos;
+  }
+  return self;
+}
+
+- (NSTimeInterval)timeIntervalSince1970 {
+  return TimeIntervalSince1970FromSecondsAndNanos(self.seconds, self.nanos);
+}
+
+- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  int64_t seconds;
+  int32_t nanos =
+      SecondsAndNanosFromTimeIntervalSince1970(timeIntervalSince1970, &seconds);
+  self.seconds = seconds;
+  self.nanos = nanos;
+}
+
+@end

+ 68 - 0
objectivec/GPBWireFormat.h

@@ -0,0 +1,68 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBTypes.h"
+
+CF_EXTERN_C_BEGIN
+
+typedef enum {
+  GPBWireFormatVarint = 0,
+  GPBWireFormatFixed64 = 1,
+  GPBWireFormatLengthDelimited = 2,
+  GPBWireFormatStartGroup = 3,
+  GPBWireFormatEndGroup = 4,
+  GPBWireFormatFixed32 = 5,
+} GPBWireFormat;
+
+enum {
+  GPBWireFormatMessageSetItem = 1,
+  GPBWireFormatMessageSetTypeId = 2,
+  GPBWireFormatMessageSetMessage = 3
+};
+
+uint32_t GPBWireFormatMakeTag(uint32_t fieldNumber, GPBWireFormat wireType)
+    __attribute__((const));
+GPBWireFormat GPBWireFormatGetTagWireType(uint32_t tag) __attribute__((const));
+uint32_t GPBWireFormatGetTagFieldNumber(uint32_t tag) __attribute__((const));
+
+GPBWireFormat GPBWireFormatForType(GPBType type, BOOL isPacked)
+    __attribute__((const));
+
+#define GPBWireFormatMessageSetItemTag \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetItem, GPBWireFormatStartGroup))
+#define GPBWireFormatMessageSetItemEndTag \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetItem, GPBWireFormatEndGroup))
+#define GPBWireFormatMessageSetTypeIdTag \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetTypeId, GPBWireFormatVarint))
+#define GPBWireFormatMessageSetMessageTag               \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetMessage, \
+                        GPBWireFormatLengthDelimited))
+
+CF_EXTERN_C_END

+ 78 - 0
objectivec/GPBWireFormat.m

@@ -0,0 +1,78 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBWireFormat.h"
+
+#import "GPBUtilities_PackagePrivate.h"
+
+enum {
+  GPBWireFormatTagTypeBits = 3,
+  GPBWireFormatTagTypeMask = 7 /* = (1 << GPBWireFormatTagTypeBits) - 1 */,
+};
+
+uint32_t GPBWireFormatMakeTag(uint32_t fieldNumber, GPBWireFormat wireType) {
+  return (fieldNumber << GPBWireFormatTagTypeBits) | wireType;
+}
+
+GPBWireFormat GPBWireFormatGetTagWireType(uint32_t tag) {
+  return (GPBWireFormat)(tag & GPBWireFormatTagTypeMask);
+}
+
+uint32_t GPBWireFormatGetTagFieldNumber(uint32_t tag) {
+  return GPBLogicalRightShift32(tag, GPBWireFormatTagTypeBits);
+}
+
+GPBWireFormat GPBWireFormatForType(GPBType type, BOOL isPacked) {
+  if (isPacked) {
+    return GPBWireFormatLengthDelimited;
+  }
+
+  static const GPBWireFormat format[GPBTypeCount] = {
+      GPBWireFormatVarint,           // GPBTypeBool
+      GPBWireFormatFixed32,          // GPBTypeFixed32
+      GPBWireFormatFixed32,          // GPBTypeSFixed32
+      GPBWireFormatFixed32,          // GPBTypeFloat
+      GPBWireFormatFixed64,          // GPBTypeFixed64
+      GPBWireFormatFixed64,          // GPBTypeSFixed64
+      GPBWireFormatFixed64,          // GPBTypeDouble
+      GPBWireFormatVarint,           // GPBTypeInt32
+      GPBWireFormatVarint,           // GPBTypeInt64
+      GPBWireFormatVarint,           // GPBTypeSInt32
+      GPBWireFormatVarint,           // GPBTypeSInt64
+      GPBWireFormatVarint,           // GPBTypeUInt32
+      GPBWireFormatVarint,           // GPBTypeUInt64
+      GPBWireFormatLengthDelimited,  // GPBTypeBytes
+      GPBWireFormatLengthDelimited,  // GPBTypeString
+      GPBWireFormatLengthDelimited,  // GPBTypeMessage
+      GPBWireFormatStartGroup,       // GPBTypeGroup
+      GPBWireFormatVarint            // GPBTypeEnum
+  };
+  return format[type];
+}

Plik diff jest za duży
+ 597 - 0
objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj


+ 7 - 0
objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:ProtocolBuffers_OSX.xcodeproj">
+   </FileRef>
+</Workspace>

+ 8 - 0
objectivec/ProtocolBuffers_OSX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
+	<false/>
+</dict>
+</plist>

+ 125 - 0
objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0620"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+               BuildableName = "libProtocolBuffers.a"
+               BlueprintName = "ProtocolBuffers"
+               ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Release">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8BBEA4A5147C727100C4ADB7"
+               BuildableName = "UnitTests.xctest"
+               BlueprintName = "UnitTests"
+               ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "ArrayTests">
+               </Test>
+               <Test
+                  Identifier = "CodedInputStreamTests">
+               </Test>
+               <Test
+                  Identifier = "CodedOutputStreamTests">
+               </Test>
+               <Test
+                  Identifier = "ConcurrencyTests">
+               </Test>
+               <Test
+                  Identifier = "FilteredMessageTests">
+               </Test>
+               <Test
+                  Identifier = "GPBStringTests">
+               </Test>
+               <Test
+                  Identifier = "GPBTestCase">
+               </Test>
+               <Test
+                  Identifier = "GeneratedMessageTests">
+               </Test>
+               <Test
+                  Identifier = "MessageTests">
+               </Test>
+               <Test
+                  Identifier = "UnknownFieldSetTest">
+               </Test>
+               <Test
+                  Identifier = "UtilitiesTests">
+               </Test>
+               <Test
+                  Identifier = "WireFormatTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Release">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 115 - 0
objectivec/ProtocolBuffers_OSX.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme

@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0620"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "NO">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+               BuildableName = "libProtocolBuffers.a"
+               BlueprintName = "ProtocolBuffers"
+               ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F4487C381A9F8E0200531423"
+               BuildableName = "libTestSingleSourceBuild.a"
+               BlueprintName = "TestSingleSourceBuild"
+               ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8BBEA4A5147C727100C4ADB7"
+               BuildableName = "UnitTests.xctest"
+               BlueprintName = "UnitTests"
+               ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "PerfTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_OSX.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

Plik diff jest za duży
+ 685 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj


+ 7 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:ProtocolBuffers_iOS.xcodeproj">
+   </FileRef>
+</Workspace>

+ 8 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
+	<false/>
+</dict>
+</plist>

+ 62 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcbaselines/8BBEA4A5147C727100C4ADB7.xcbaseline/FFE465CA-0E74-40E8-9F09-500B66B7DCB2.plist

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>classNames</key>
+	<dict>
+		<key>PerfTests</key>
+		<dict>
+			<key>testExtensionsPerformance</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.9</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Feb 5, 2015, 9:42:41 AM</string>
+				</dict>
+			</dict>
+			<key>testHas</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.09</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Feb 5, 2015, 9:42:35 AM</string>
+				</dict>
+			</dict>
+			<key>testMessagePerformance</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.57</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Feb 5, 2015, 9:42:47 AM</string>
+				</dict>
+			</dict>
+			<key>testPackedExtensionsPerformance</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.75</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Feb 5, 2015, 9:42:51 AM</string>
+				</dict>
+			</dict>
+			<key>testPackedTypesPerformance</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.26</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Feb 5, 2015, 9:42:55 AM</string>
+				</dict>
+			</dict>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 21 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcbaselines/8BBEA4A5147C727100C4ADB7.xcbaseline/Info.plist

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>runDestinationsByUUID</key>
+	<dict>
+		<key>FFE465CA-0E74-40E8-9F09-500B66B7DCB2</key>
+		<dict>
+			<key>targetArchitecture</key>
+			<string>arm64</string>
+			<key>targetDevice</key>
+			<dict>
+				<key>modelCode</key>
+				<string>iPhone7,1</string>
+				<key>platformIdentifier</key>
+				<string>com.apple.platform.iphoneos</string>
+			</dict>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 134 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0610"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+               BuildableName = "libProtocolBuffers.a"
+               BlueprintName = "ProtocolBuffers"
+               ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Release">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8BBEA4A5147C727100C4ADB7"
+               BuildableName = "UnitTests.xctest"
+               BlueprintName = "UnitTests"
+               ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "ArrayTests">
+               </Test>
+               <Test
+                  Identifier = "CodedInputStreamTests">
+               </Test>
+               <Test
+                  Identifier = "CodedOutputStreamTests">
+               </Test>
+               <Test
+                  Identifier = "ConcurrencyTests">
+               </Test>
+               <Test
+                  Identifier = "FilteredMessageTests">
+               </Test>
+               <Test
+                  Identifier = "GPBStringTests">
+               </Test>
+               <Test
+                  Identifier = "GPBTestCase">
+               </Test>
+               <Test
+                  Identifier = "GeneratedMessageTests">
+               </Test>
+               <Test
+                  Identifier = "MessageTests">
+               </Test>
+               <Test
+                  Identifier = "UnknownFieldSetTest">
+               </Test>
+               <Test
+                  Identifier = "UtilitiesTests">
+               </Test>
+               <Test
+                  Identifier = "WireFormatTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "8B9A5EA41831993600A9D33B"
+            BuildableName = "iOSTestHarness.app"
+            BlueprintName = "iOSTestHarness"
+            ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "8B9A5EA41831993600A9D33B"
+            BuildableName = "iOSTestHarness.app"
+            BlueprintName = "iOSTestHarness"
+            ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Release">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 116 - 0
objectivec/ProtocolBuffers_iOS.xcodeproj/xcshareddata/xcschemes/ProtocolBuffers.xcscheme

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0610"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "NO">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+               BuildableName = "libProtocolBuffers.a"
+               BlueprintName = "ProtocolBuffers"
+               ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "F4487C551A9F8F8100531423"
+               BuildableName = "libTestSingleSourceBuild.a"
+               BlueprintName = "TestSingleSourceBuild"
+               ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8BBEA4A5147C727100C4ADB7"
+               BuildableName = "UnitTests.xctest"
+               BlueprintName = "UnitTests"
+               ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+            </BuildableReference>
+            <SkippedTests>
+               <Test
+                  Identifier = "PerfTests">
+               </Test>
+            </SkippedTests>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "8B9A5EA41831993600A9D33B"
+            BuildableName = "iOSTestHarness.app"
+            BlueprintName = "iOSTestHarness"
+            ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "8B9A5EA41831993600A9D33B"
+            BuildableName = "iOSTestHarness.app"
+            BlueprintName = "iOSTestHarness"
+            ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "7461B52D0F94FAF800A0C422"
+            BuildableName = "libProtocolBuffers.a"
+            BlueprintName = "ProtocolBuffers"
+            ReferencedContainer = "container:ProtocolBuffers_iOS.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 40 - 0
objectivec/Tests/Filter1.txt

@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Test the filter system for the ObjC Protocol Buffer Compiler.
+
+// Class names are matched using file name globbing rules.
+// Whitespace is not allowed at the beginning of a line (except for a line
+// that is a single newline).
+
+Keep
+RemoveEnumMessage_KeepNestedInside
+RemoveJustKidding

+ 35 - 0
objectivec/Tests/Filter2.txt

@@ -0,0 +1,35 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Test the filter system for the ObjC Protocol Buffer Compiler to test
+// multiple filter support.
+
+Other

+ 57 - 0
objectivec/Tests/GPBARCUnittestProtos.m

@@ -0,0 +1,57 @@
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Makes sure all the generated headers compile with ARC on.
+
+#import "google/protobuf/Unittest.pbobjc.h"
+#import "google/protobuf/UnittestCustomOptions.pbobjc.h"
+#import "google/protobuf/UnittestCycle.pbobjc.h"
+#import "google/protobuf/UnittestDropUnknownFields.pbobjc.h"
+#import "google/protobuf/UnittestEmbedOptimizeFor.pbobjc.h"
+#import "google/protobuf/UnittestEmpty.pbobjc.h"
+#import "google/protobuf/UnittestEnormousDescriptor.pbobjc.h"
+#import "google/protobuf/UnittestFilter.pbobjc.h"
+#import "google/protobuf/UnittestImport.pbobjc.h"
+#import "google/protobuf/UnittestImportLite.pbobjc.h"
+#import "google/protobuf/UnittestImportPublic.pbobjc.h"
+#import "google/protobuf/UnittestImportPublicLite.pbobjc.h"
+#import "google/protobuf/UnittestLite.pbobjc.h"
+#import "google/protobuf/UnittestMset.pbobjc.h"
+#import "google/protobuf/UnittestNameMangling.pbobjc.h"
+#import "google/protobuf/UnittestNoGenericServices.pbobjc.h"
+#import "google/protobuf/UnittestObjc.pbobjc.h"
+#import "google/protobuf/UnittestOptimizeFor.pbobjc.h"
+#import "google/protobuf/UnittestPreserveUnknownEnum.pbobjc.h"
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+#import "google/protobuf/UnittestRuntimeProto3.pbobjc.h"

+ 3365 - 0
objectivec/Tests/GPBArrayTests.m

@@ -0,0 +1,3365 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import <XCTest/XCTest.h>
+
+#import "GPBArray.h"
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// To let the testing macros work, add some extra methods to simplify things.
+@interface GPBEnumArray (TestingTweak)
++ (instancetype)arrayWithValue:(int32_t)value;
+- (instancetype)initWithValues:(const int32_t [])values
+                         count:(NSUInteger)count;
+@end
+
+static BOOL TestingEnum_IsValidValue(int32_t value) {
+  switch (value) {
+    case 71:
+    case 72:
+    case 73:
+    case 74:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+static BOOL TestingEnum_IsValidValue2(int32_t value) {
+  switch (value) {
+    case 71:
+    case 72:
+    case 73:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+@implementation GPBEnumArray (TestingTweak)
++ (instancetype)arrayWithValue:(int32_t)value {
+  return [[[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                         rawValues:&value
+                                             count:1] autorelease];
+}
+- (instancetype)initWithValues:(const int32_t [])values
+                         count:(NSUInteger)count {
+  return [self initWithValidationFunction:TestingEnum_IsValidValue
+                                rawValues:values
+                                    count:count];
+}
+@end
+
+#pragma mark - PDDM Macros
+
+//%PDDM-DEFINE ARRAY_TESTS(NAME, TYPE, VAL1, VAL2, VAL3, VAL4)
+//%ARRAY_TESTS2(NAME, TYPE, VAL1, VAL2, VAL3, VAL4, )
+//%PDDM-DEFINE ARRAY_TESTS2(NAME, TYPE, VAL1, VAL2, VAL3, VAL4, HELPER)
+//%#pragma mark - NAME
+//%
+//%@interface GPB##NAME##ArrayTests : XCTestCase
+//%@end
+//%
+//%@implementation GPB##NAME##ArrayTests
+//%
+//%- (void)testEmpty {
+//%  GPB##NAME##Array *array = [[GPB##NAME##Array alloc] init];
+//%  XCTAssertNotNil(array);
+//%  XCTAssertEqual(array.count, 0U);
+//%  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+//%  [array enumerateValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    #pragma unused(value, idx, stop)
+//%    XCTFail(@"Shouldn't get here!");
+//%  }];
+//%  [array enumerateValuesWithOptions:NSEnumerationReverse
+//%                         usingBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    #pragma unused(value, idx, stop)
+//%    XCTFail(@"Shouldn't get here!");
+//%  }];
+//%  [array release];
+//%}
+//%
+//%- (void)testOne {
+//%  GPB##NAME##Array *array = [GPB##NAME##Array arrayWithValue:VAL1];
+//%  XCTAssertNotNil(array);
+//%  XCTAssertEqual(array.count, 1U);
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+//%  [array enumerateValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    XCTAssertEqual(idx, 0U);
+//%    XCTAssertEqual(value, VAL1);
+//%    XCTAssertNotEqual(stop, NULL);
+//%  }];
+//%  [array enumerateValuesWithOptions:NSEnumerationReverse
+//%                         usingBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    XCTAssertEqual(idx, 0U);
+//%    XCTAssertEqual(value, VAL1);
+//%    XCTAssertNotEqual(stop, NULL);
+//%  }];
+//%}
+//%
+//%- (void)testBasics {
+//%  static const TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%  XCTAssertEqual(array.count, 4U);
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL2);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL3);
+//%  XCTAssertEqual([array valueAtIndex:3], VAL4);
+//%  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+//%  __block NSUInteger idx2 = 0;
+//%  [array enumerateValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    XCTAssertEqual(idx, idx2);
+//%    XCTAssertEqual(value, kValues[idx]);
+//%    XCTAssertNotEqual(stop, NULL);
+//%    ++idx2;
+//%  }];
+//%  idx2 = 0;
+//%  [array enumerateValuesWithOptions:NSEnumerationReverse
+//%                         usingBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    XCTAssertEqual(idx, (3 - idx2));
+//%    XCTAssertEqual(value, kValues[idx]);
+//%    XCTAssertNotEqual(stop, NULL);
+//%    ++idx2;
+//%  }];
+//%  // Stopping the enumeration.
+//%  idx2 = 0;
+//%  [array enumerateValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    XCTAssertEqual(idx, idx2);
+//%    XCTAssertEqual(value, kValues[idx]);
+//%    XCTAssertNotEqual(stop, NULL);
+//%    if (idx2 == 1) *stop = YES;
+//%    XCTAssertNotEqual(idx, 2U);
+//%    XCTAssertNotEqual(idx, 3U);
+//%    ++idx2;
+//%  }];
+//%  idx2 = 0;
+//%  [array enumerateValuesWithOptions:NSEnumerationReverse
+//%                         usingBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%    XCTAssertEqual(idx, (3 - idx2));
+//%    XCTAssertEqual(value, kValues[idx]);
+//%    XCTAssertNotEqual(stop, NULL);
+//%    if (idx2 == 1) *stop = YES;
+//%    XCTAssertNotEqual(idx, 1U);
+//%    XCTAssertNotEqual(idx, 0U);
+//%    ++idx2;
+//%  }];
+//%  [array release];
+//%}
+//%
+//%- (void)testEquality {
+//%  const TYPE kValues1[] = { VAL1, VAL2, VAL3 };
+//%  const TYPE kValues2[] = { VAL1, VAL4, VAL3 };
+//%  const TYPE kValues3[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##NAME##Array *array1 =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues1
+//%            NAME$S                     count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(array1);
+//%  GPB##NAME##Array *array1prime =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues1
+//%            NAME$S                     count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(array1prime);
+//%  GPB##NAME##Array *array2 =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues2
+//%            NAME$S                     count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(array2);
+//%  GPB##NAME##Array *array3 =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues3
+//%            NAME$S                     count:GPBARRAYSIZE(kValues3)];
+//%  XCTAssertNotNil(array3);
+//%
+//%  // 1/1Prime should be different objects, but equal.
+//%  XCTAssertNotEqual(array1, array1prime);
+//%  XCTAssertEqualObjects(array1, array1prime);
+//%  // Equal, so they must have same hash.
+//%  XCTAssertEqual([array1 hash], [array1prime hash]);
+//%
+//%  // 1/2/3 shouldn't be equal.
+//%  XCTAssertNotEqualObjects(array1, array2);
+//%  XCTAssertNotEqualObjects(array1, array3);
+//%  XCTAssertNotEqualObjects(array2, array3);
+//%
+//%  [array1 release];
+//%  [array1prime release];
+//%  [array2 release];
+//%  [array3 release];
+//%}
+//%
+//%- (void)testCopy {
+//%  const TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%
+//%  GPB##NAME##Array *array2 = [array copy];
+//%  XCTAssertNotNil(array2);
+//%
+//%  // Should be new object but equal.
+//%  XCTAssertNotEqual(array, array2);
+//%  XCTAssertEqualObjects(array, array2);
+//%}
+//%
+//%- (void)testArrayFromArray {
+//%  const TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%
+//%  GPB##NAME##Array *array2 = [GPB##NAME##Array arrayWithValueArray:array];
+//%  XCTAssertNotNil(array2);
+//%
+//%  // Should be new pointer, but equal objects.
+//%  XCTAssertNotEqual(array, array2);
+//%  XCTAssertEqualObjects(array, array2);
+//%}
+//%
+//%- (void)testAdds {
+//%  GPB##NAME##Array *array = [GPB##NAME##Array array];
+//%  XCTAssertNotNil(array);
+//%
+//%  XCTAssertEqual(array.count, 0U);
+//%  [array addValue:VAL1];
+//%  XCTAssertEqual(array.count, 1U);
+//%
+//%  const TYPE kValues1[] = { VAL2, VAL3 };
+//%  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertEqual(array.count, 3U);
+//%
+//%  const TYPE kValues2[] = { VAL4, VAL1 };
+//%  GPB##NAME##Array *array2 =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues2
+//%            NAME$S                     count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(array2);
+//%  [array add##HELPER##ValuesFromArray:array2];
+//%  XCTAssertEqual(array.count, 5U);
+//%
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL2);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL3);
+//%  XCTAssertEqual([array valueAtIndex:3], VAL4);
+//%  XCTAssertEqual([array valueAtIndex:4], VAL1);
+//%}
+//%
+//%- (void)testInsert {
+//%  const TYPE kValues[] = { VAL1, VAL2, VAL3 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%  XCTAssertEqual(array.count, 3U);
+//%
+//%  // First
+//%  [array insertValue:VAL4 atIndex:0];
+//%  XCTAssertEqual(array.count, 4U);
+//%
+//%  // Middle
+//%  [array insertValue:VAL4 atIndex:2];
+//%  XCTAssertEqual(array.count, 5U);
+//%
+//%  // End
+//%  [array insertValue:VAL4 atIndex:5];
+//%  XCTAssertEqual(array.count, 6U);
+//%
+//%  // Too far.
+//%  XCTAssertThrowsSpecificNamed([array insertValue:VAL4 atIndex:7],
+//%                               NSException, NSRangeException);
+//%
+//%  XCTAssertEqual([array valueAtIndex:0], VAL4);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL4);
+//%  XCTAssertEqual([array valueAtIndex:3], VAL2);
+//%  XCTAssertEqual([array valueAtIndex:4], VAL3);
+//%  XCTAssertEqual([array valueAtIndex:5], VAL4);
+//%}
+//%
+//%- (void)testRemove {
+//%  const TYPE kValues[] = { VAL4, VAL1, VAL2, VAL4, VAL3, VAL4 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%  XCTAssertEqual(array.count, 6U);
+//%
+//%  // First
+//%  [array removeValueAtIndex:0];
+//%  XCTAssertEqual(array.count, 5U);
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%
+//%  // Middle
+//%  [array removeValueAtIndex:2];
+//%  XCTAssertEqual(array.count, 4U);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL3);
+//%
+//%  // End
+//%  [array removeValueAtIndex:3];
+//%  XCTAssertEqual(array.count, 3U);
+//%
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL2);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL3);
+//%
+//%  // Too far.
+//%  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+//%                               NSException, NSRangeException);
+//%
+//%  [array removeAll];
+//%  XCTAssertEqual(array.count, 0U);
+//%  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+//%                               NSException, NSRangeException);
+//%}
+//%
+//%- (void)testInplaceMutation {
+//%  const TYPE kValues[] = { VAL1, VAL1, VAL3, VAL3 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%
+//%  [array replaceValueAtIndex:1 withValue:VAL2];
+//%  [array replaceValueAtIndex:3 withValue:VAL4];
+//%  XCTAssertEqual(array.count, 4U);
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL2);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL3);
+//%  XCTAssertEqual([array valueAtIndex:3], VAL4);
+//%
+//%  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:VAL4],
+//%                               NSException, NSRangeException);
+//%
+//%  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+//%  XCTAssertEqual(array.count, 4U);
+//%  XCTAssertEqual([array valueAtIndex:0], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL4);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL3);
+//%  XCTAssertEqual([array valueAtIndex:3], VAL2);
+//%
+//%  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+//%  XCTAssertEqual(array.count, 4U);
+//%  XCTAssertEqual([array valueAtIndex:0], VAL3);
+//%  XCTAssertEqual([array valueAtIndex:1], VAL4);
+//%  XCTAssertEqual([array valueAtIndex:2], VAL1);
+//%  XCTAssertEqual([array valueAtIndex:3], VAL2);
+//%
+//%  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+//%                               NSException, NSRangeException);
+//%  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+//%                               NSException, NSRangeException);
+//%}
+//%
+//%- (void)testInternalResizing {
+//%  const TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##NAME##Array *array =
+//%      [[GPB##NAME##Array alloc] initWithValues:kValues
+//%            NAME$S                     count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(array);
+//%
+//%  // Add/remove to trigger the intneral buffer to grow/shrink.
+//%  for (int i = 0; i < 100; ++i) {
+//%    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+//%  }
+//%  XCTAssertEqual(array.count, 404U);
+//%  for (int i = 0; i < 100; ++i) {
+//%    [array removeValueAtIndex:(i * 2)];
+//%  }
+//%  XCTAssertEqual(array.count, 304U);
+//%  for (int i = 0; i < 100; ++i) {
+//%    [array insertValue:VAL4 atIndex:(i * 3)];
+//%  }
+//%  XCTAssertEqual(array.count, 404U);
+//%  [array removeAll];
+//%  XCTAssertEqual(array.count, 0U);
+//%}
+//%
+//%@end
+//%
+//%PDDM-EXPAND ARRAY_TESTS(Int32, int32_t, 1, 2, 3, 4)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+@interface GPBInt32ArrayTests : XCTestCase
+@end
+
+@implementation GPBInt32ArrayTests
+
+- (void)testEmpty {
+  GPBInt32Array *array = [[GPBInt32Array alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBInt32Array *array = [GPBInt32Array arrayWithValue:1];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 1);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 1);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 1);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const int32_t kValues[] = { 1, 2, 3, 4 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 1);
+  XCTAssertEqual([array valueAtIndex:1], 2);
+  XCTAssertEqual([array valueAtIndex:2], 3);
+  XCTAssertEqual([array valueAtIndex:3], 4);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const int32_t kValues1[] = { 1, 2, 3 };
+  const int32_t kValues2[] = { 1, 4, 3 };
+  const int32_t kValues3[] = { 1, 2, 3, 4 };
+  GPBInt32Array *array1 =
+      [[GPBInt32Array alloc] initWithValues:kValues1
+                                      count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBInt32Array *array1prime =
+      [[GPBInt32Array alloc] initWithValues:kValues1
+                                      count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBInt32Array *array2 =
+      [[GPBInt32Array alloc] initWithValues:kValues2
+                                      count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBInt32Array *array3 =
+      [[GPBInt32Array alloc] initWithValues:kValues3
+                                      count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const int32_t kValues[] = { 1, 2, 3, 4 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBInt32Array *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const int32_t kValues[] = { 1, 2, 3, 4 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBInt32Array *array2 = [GPBInt32Array arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBInt32Array *array = [GPBInt32Array array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:1];
+  XCTAssertEqual(array.count, 1U);
+
+  const int32_t kValues1[] = { 2, 3 };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const int32_t kValues2[] = { 4, 1 };
+  GPBInt32Array *array2 =
+      [[GPBInt32Array alloc] initWithValues:kValues2
+                                      count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 1);
+  XCTAssertEqual([array valueAtIndex:1], 2);
+  XCTAssertEqual([array valueAtIndex:2], 3);
+  XCTAssertEqual([array valueAtIndex:3], 4);
+  XCTAssertEqual([array valueAtIndex:4], 1);
+}
+
+- (void)testInsert {
+  const int32_t kValues[] = { 1, 2, 3 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:4 atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:4 atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:4 atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:4 atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 4);
+  XCTAssertEqual([array valueAtIndex:1], 1);
+  XCTAssertEqual([array valueAtIndex:2], 4);
+  XCTAssertEqual([array valueAtIndex:3], 2);
+  XCTAssertEqual([array valueAtIndex:4], 3);
+  XCTAssertEqual([array valueAtIndex:5], 4);
+}
+
+- (void)testRemove {
+  const int32_t kValues[] = { 4, 1, 2, 4, 3, 4 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 1);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 3);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 1);
+  XCTAssertEqual([array valueAtIndex:1], 2);
+  XCTAssertEqual([array valueAtIndex:2], 3);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const int32_t kValues[] = { 1, 1, 3, 3 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:2];
+  [array replaceValueAtIndex:3 withValue:4];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 1);
+  XCTAssertEqual([array valueAtIndex:1], 2);
+  XCTAssertEqual([array valueAtIndex:2], 3);
+  XCTAssertEqual([array valueAtIndex:3], 4);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:4],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 1);
+  XCTAssertEqual([array valueAtIndex:1], 4);
+  XCTAssertEqual([array valueAtIndex:2], 3);
+  XCTAssertEqual([array valueAtIndex:3], 2);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 3);
+  XCTAssertEqual([array valueAtIndex:1], 4);
+  XCTAssertEqual([array valueAtIndex:2], 1);
+  XCTAssertEqual([array valueAtIndex:3], 2);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const int32_t kValues[] = { 1, 2, 3, 4 };
+  GPBInt32Array *array =
+      [[GPBInt32Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:4 atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS(UInt32, uint32_t, 11U, 12U, 13U, 14U)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32
+
+@interface GPBUInt32ArrayTests : XCTestCase
+@end
+
+@implementation GPBUInt32ArrayTests
+
+- (void)testEmpty {
+  GPBUInt32Array *array = [[GPBUInt32Array alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBUInt32Array *array = [GPBUInt32Array arrayWithValue:11U];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 11U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 11U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const uint32_t kValues[] = { 11U, 12U, 13U, 14U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+  XCTAssertEqual([array valueAtIndex:1], 12U);
+  XCTAssertEqual([array valueAtIndex:2], 13U);
+  XCTAssertEqual([array valueAtIndex:3], 14U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const uint32_t kValues1[] = { 11U, 12U, 13U };
+  const uint32_t kValues2[] = { 11U, 14U, 13U };
+  const uint32_t kValues3[] = { 11U, 12U, 13U, 14U };
+  GPBUInt32Array *array1 =
+      [[GPBUInt32Array alloc] initWithValues:kValues1
+                                       count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBUInt32Array *array1prime =
+      [[GPBUInt32Array alloc] initWithValues:kValues1
+                                       count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBUInt32Array *array2 =
+      [[GPBUInt32Array alloc] initWithValues:kValues2
+                                       count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBUInt32Array *array3 =
+      [[GPBUInt32Array alloc] initWithValues:kValues3
+                                       count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const uint32_t kValues[] = { 11U, 12U, 13U, 14U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBUInt32Array *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const uint32_t kValues[] = { 11U, 12U, 13U, 14U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBUInt32Array *array2 = [GPBUInt32Array arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBUInt32Array *array = [GPBUInt32Array array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:11U];
+  XCTAssertEqual(array.count, 1U);
+
+  const uint32_t kValues1[] = { 12U, 13U };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const uint32_t kValues2[] = { 14U, 11U };
+  GPBUInt32Array *array2 =
+      [[GPBUInt32Array alloc] initWithValues:kValues2
+                                       count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+  XCTAssertEqual([array valueAtIndex:1], 12U);
+  XCTAssertEqual([array valueAtIndex:2], 13U);
+  XCTAssertEqual([array valueAtIndex:3], 14U);
+  XCTAssertEqual([array valueAtIndex:4], 11U);
+}
+
+- (void)testInsert {
+  const uint32_t kValues[] = { 11U, 12U, 13U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:14U atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:14U atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:14U atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:14U atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 14U);
+  XCTAssertEqual([array valueAtIndex:1], 11U);
+  XCTAssertEqual([array valueAtIndex:2], 14U);
+  XCTAssertEqual([array valueAtIndex:3], 12U);
+  XCTAssertEqual([array valueAtIndex:4], 13U);
+  XCTAssertEqual([array valueAtIndex:5], 14U);
+}
+
+- (void)testRemove {
+  const uint32_t kValues[] = { 14U, 11U, 12U, 14U, 13U, 14U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 13U);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+  XCTAssertEqual([array valueAtIndex:1], 12U);
+  XCTAssertEqual([array valueAtIndex:2], 13U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kValues[] = { 11U, 11U, 13U, 13U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:12U];
+  [array replaceValueAtIndex:3 withValue:14U];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+  XCTAssertEqual([array valueAtIndex:1], 12U);
+  XCTAssertEqual([array valueAtIndex:2], 13U);
+  XCTAssertEqual([array valueAtIndex:3], 14U);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:14U],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 11U);
+  XCTAssertEqual([array valueAtIndex:1], 14U);
+  XCTAssertEqual([array valueAtIndex:2], 13U);
+  XCTAssertEqual([array valueAtIndex:3], 12U);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 13U);
+  XCTAssertEqual([array valueAtIndex:1], 14U);
+  XCTAssertEqual([array valueAtIndex:2], 11U);
+  XCTAssertEqual([array valueAtIndex:3], 12U);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const uint32_t kValues[] = { 11U, 12U, 13U, 14U };
+  GPBUInt32Array *array =
+      [[GPBUInt32Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:14U atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS(Int64, int64_t, 31LL, 32LL, 33LL, 34LL)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int64
+
+@interface GPBInt64ArrayTests : XCTestCase
+@end
+
+@implementation GPBInt64ArrayTests
+
+- (void)testEmpty {
+  GPBInt64Array *array = [[GPBInt64Array alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBInt64Array *array = [GPBInt64Array arrayWithValue:31LL];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 31LL);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 31LL);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const int64_t kValues[] = { 31LL, 32LL, 33LL, 34LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+  XCTAssertEqual([array valueAtIndex:1], 32LL);
+  XCTAssertEqual([array valueAtIndex:2], 33LL);
+  XCTAssertEqual([array valueAtIndex:3], 34LL);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const int64_t kValues1[] = { 31LL, 32LL, 33LL };
+  const int64_t kValues2[] = { 31LL, 34LL, 33LL };
+  const int64_t kValues3[] = { 31LL, 32LL, 33LL, 34LL };
+  GPBInt64Array *array1 =
+      [[GPBInt64Array alloc] initWithValues:kValues1
+                                      count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBInt64Array *array1prime =
+      [[GPBInt64Array alloc] initWithValues:kValues1
+                                      count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBInt64Array *array2 =
+      [[GPBInt64Array alloc] initWithValues:kValues2
+                                      count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBInt64Array *array3 =
+      [[GPBInt64Array alloc] initWithValues:kValues3
+                                      count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const int64_t kValues[] = { 31LL, 32LL, 33LL, 34LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBInt64Array *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const int64_t kValues[] = { 31LL, 32LL, 33LL, 34LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBInt64Array *array2 = [GPBInt64Array arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBInt64Array *array = [GPBInt64Array array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:31LL];
+  XCTAssertEqual(array.count, 1U);
+
+  const int64_t kValues1[] = { 32LL, 33LL };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const int64_t kValues2[] = { 34LL, 31LL };
+  GPBInt64Array *array2 =
+      [[GPBInt64Array alloc] initWithValues:kValues2
+                                      count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+  XCTAssertEqual([array valueAtIndex:1], 32LL);
+  XCTAssertEqual([array valueAtIndex:2], 33LL);
+  XCTAssertEqual([array valueAtIndex:3], 34LL);
+  XCTAssertEqual([array valueAtIndex:4], 31LL);
+}
+
+- (void)testInsert {
+  const int64_t kValues[] = { 31LL, 32LL, 33LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:34LL atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:34LL atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:34LL atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:34LL atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 34LL);
+  XCTAssertEqual([array valueAtIndex:1], 31LL);
+  XCTAssertEqual([array valueAtIndex:2], 34LL);
+  XCTAssertEqual([array valueAtIndex:3], 32LL);
+  XCTAssertEqual([array valueAtIndex:4], 33LL);
+  XCTAssertEqual([array valueAtIndex:5], 34LL);
+}
+
+- (void)testRemove {
+  const int64_t kValues[] = { 34LL, 31LL, 32LL, 34LL, 33LL, 34LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 33LL);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+  XCTAssertEqual([array valueAtIndex:1], 32LL);
+  XCTAssertEqual([array valueAtIndex:2], 33LL);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const int64_t kValues[] = { 31LL, 31LL, 33LL, 33LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:32LL];
+  [array replaceValueAtIndex:3 withValue:34LL];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+  XCTAssertEqual([array valueAtIndex:1], 32LL);
+  XCTAssertEqual([array valueAtIndex:2], 33LL);
+  XCTAssertEqual([array valueAtIndex:3], 34LL);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:34LL],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 31LL);
+  XCTAssertEqual([array valueAtIndex:1], 34LL);
+  XCTAssertEqual([array valueAtIndex:2], 33LL);
+  XCTAssertEqual([array valueAtIndex:3], 32LL);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 33LL);
+  XCTAssertEqual([array valueAtIndex:1], 34LL);
+  XCTAssertEqual([array valueAtIndex:2], 31LL);
+  XCTAssertEqual([array valueAtIndex:3], 32LL);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const int64_t kValues[] = { 31LL, 32LL, 33LL, 34LL };
+  GPBInt64Array *array =
+      [[GPBInt64Array alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:34LL atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS(UInt64, uint64_t, 41ULL, 42ULL, 43ULL, 44ULL)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt64
+
+@interface GPBUInt64ArrayTests : XCTestCase
+@end
+
+@implementation GPBUInt64ArrayTests
+
+- (void)testEmpty {
+  GPBUInt64Array *array = [[GPBUInt64Array alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBUInt64Array *array = [GPBUInt64Array arrayWithValue:41ULL];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 41ULL);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 41ULL);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const uint64_t kValues[] = { 41ULL, 42ULL, 43ULL, 44ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+  XCTAssertEqual([array valueAtIndex:1], 42ULL);
+  XCTAssertEqual([array valueAtIndex:2], 43ULL);
+  XCTAssertEqual([array valueAtIndex:3], 44ULL);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const uint64_t kValues1[] = { 41ULL, 42ULL, 43ULL };
+  const uint64_t kValues2[] = { 41ULL, 44ULL, 43ULL };
+  const uint64_t kValues3[] = { 41ULL, 42ULL, 43ULL, 44ULL };
+  GPBUInt64Array *array1 =
+      [[GPBUInt64Array alloc] initWithValues:kValues1
+                                       count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBUInt64Array *array1prime =
+      [[GPBUInt64Array alloc] initWithValues:kValues1
+                                       count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBUInt64Array *array2 =
+      [[GPBUInt64Array alloc] initWithValues:kValues2
+                                       count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBUInt64Array *array3 =
+      [[GPBUInt64Array alloc] initWithValues:kValues3
+                                       count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const uint64_t kValues[] = { 41ULL, 42ULL, 43ULL, 44ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBUInt64Array *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const uint64_t kValues[] = { 41ULL, 42ULL, 43ULL, 44ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBUInt64Array *array2 = [GPBUInt64Array arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBUInt64Array *array = [GPBUInt64Array array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:41ULL];
+  XCTAssertEqual(array.count, 1U);
+
+  const uint64_t kValues1[] = { 42ULL, 43ULL };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const uint64_t kValues2[] = { 44ULL, 41ULL };
+  GPBUInt64Array *array2 =
+      [[GPBUInt64Array alloc] initWithValues:kValues2
+                                       count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+  XCTAssertEqual([array valueAtIndex:1], 42ULL);
+  XCTAssertEqual([array valueAtIndex:2], 43ULL);
+  XCTAssertEqual([array valueAtIndex:3], 44ULL);
+  XCTAssertEqual([array valueAtIndex:4], 41ULL);
+}
+
+- (void)testInsert {
+  const uint64_t kValues[] = { 41ULL, 42ULL, 43ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:44ULL atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:44ULL atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:44ULL atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:44ULL atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 44ULL);
+  XCTAssertEqual([array valueAtIndex:1], 41ULL);
+  XCTAssertEqual([array valueAtIndex:2], 44ULL);
+  XCTAssertEqual([array valueAtIndex:3], 42ULL);
+  XCTAssertEqual([array valueAtIndex:4], 43ULL);
+  XCTAssertEqual([array valueAtIndex:5], 44ULL);
+}
+
+- (void)testRemove {
+  const uint64_t kValues[] = { 44ULL, 41ULL, 42ULL, 44ULL, 43ULL, 44ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 43ULL);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+  XCTAssertEqual([array valueAtIndex:1], 42ULL);
+  XCTAssertEqual([array valueAtIndex:2], 43ULL);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kValues[] = { 41ULL, 41ULL, 43ULL, 43ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:42ULL];
+  [array replaceValueAtIndex:3 withValue:44ULL];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+  XCTAssertEqual([array valueAtIndex:1], 42ULL);
+  XCTAssertEqual([array valueAtIndex:2], 43ULL);
+  XCTAssertEqual([array valueAtIndex:3], 44ULL);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:44ULL],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 41ULL);
+  XCTAssertEqual([array valueAtIndex:1], 44ULL);
+  XCTAssertEqual([array valueAtIndex:2], 43ULL);
+  XCTAssertEqual([array valueAtIndex:3], 42ULL);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 43ULL);
+  XCTAssertEqual([array valueAtIndex:1], 44ULL);
+  XCTAssertEqual([array valueAtIndex:2], 41ULL);
+  XCTAssertEqual([array valueAtIndex:3], 42ULL);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const uint64_t kValues[] = { 41ULL, 42ULL, 43ULL, 44ULL };
+  GPBUInt64Array *array =
+      [[GPBUInt64Array alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:44ULL atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS(Float, float, 51.f, 52.f, 53.f, 54.f)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Float
+
+@interface GPBFloatArrayTests : XCTestCase
+@end
+
+@implementation GPBFloatArrayTests
+
+- (void)testEmpty {
+  GPBFloatArray *array = [[GPBFloatArray alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBFloatArray *array = [GPBFloatArray arrayWithValue:51.f];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 51.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 51.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const float kValues[] = { 51.f, 52.f, 53.f, 54.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+  XCTAssertEqual([array valueAtIndex:1], 52.f);
+  XCTAssertEqual([array valueAtIndex:2], 53.f);
+  XCTAssertEqual([array valueAtIndex:3], 54.f);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(float value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const float kValues1[] = { 51.f, 52.f, 53.f };
+  const float kValues2[] = { 51.f, 54.f, 53.f };
+  const float kValues3[] = { 51.f, 52.f, 53.f, 54.f };
+  GPBFloatArray *array1 =
+      [[GPBFloatArray alloc] initWithValues:kValues1
+                                      count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBFloatArray *array1prime =
+      [[GPBFloatArray alloc] initWithValues:kValues1
+                                      count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBFloatArray *array2 =
+      [[GPBFloatArray alloc] initWithValues:kValues2
+                                      count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBFloatArray *array3 =
+      [[GPBFloatArray alloc] initWithValues:kValues3
+                                      count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const float kValues[] = { 51.f, 52.f, 53.f, 54.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBFloatArray *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const float kValues[] = { 51.f, 52.f, 53.f, 54.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBFloatArray *array2 = [GPBFloatArray arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBFloatArray *array = [GPBFloatArray array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:51.f];
+  XCTAssertEqual(array.count, 1U);
+
+  const float kValues1[] = { 52.f, 53.f };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const float kValues2[] = { 54.f, 51.f };
+  GPBFloatArray *array2 =
+      [[GPBFloatArray alloc] initWithValues:kValues2
+                                      count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+  XCTAssertEqual([array valueAtIndex:1], 52.f);
+  XCTAssertEqual([array valueAtIndex:2], 53.f);
+  XCTAssertEqual([array valueAtIndex:3], 54.f);
+  XCTAssertEqual([array valueAtIndex:4], 51.f);
+}
+
+- (void)testInsert {
+  const float kValues[] = { 51.f, 52.f, 53.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:54.f atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:54.f atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:54.f atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:54.f atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 54.f);
+  XCTAssertEqual([array valueAtIndex:1], 51.f);
+  XCTAssertEqual([array valueAtIndex:2], 54.f);
+  XCTAssertEqual([array valueAtIndex:3], 52.f);
+  XCTAssertEqual([array valueAtIndex:4], 53.f);
+  XCTAssertEqual([array valueAtIndex:5], 54.f);
+}
+
+- (void)testRemove {
+  const float kValues[] = { 54.f, 51.f, 52.f, 54.f, 53.f, 54.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 53.f);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+  XCTAssertEqual([array valueAtIndex:1], 52.f);
+  XCTAssertEqual([array valueAtIndex:2], 53.f);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const float kValues[] = { 51.f, 51.f, 53.f, 53.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:52.f];
+  [array replaceValueAtIndex:3 withValue:54.f];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+  XCTAssertEqual([array valueAtIndex:1], 52.f);
+  XCTAssertEqual([array valueAtIndex:2], 53.f);
+  XCTAssertEqual([array valueAtIndex:3], 54.f);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:54.f],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 51.f);
+  XCTAssertEqual([array valueAtIndex:1], 54.f);
+  XCTAssertEqual([array valueAtIndex:2], 53.f);
+  XCTAssertEqual([array valueAtIndex:3], 52.f);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 53.f);
+  XCTAssertEqual([array valueAtIndex:1], 54.f);
+  XCTAssertEqual([array valueAtIndex:2], 51.f);
+  XCTAssertEqual([array valueAtIndex:3], 52.f);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const float kValues[] = { 51.f, 52.f, 53.f, 54.f };
+  GPBFloatArray *array =
+      [[GPBFloatArray alloc] initWithValues:kValues
+                                      count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:54.f atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS(Double, double, 61., 62., 63., 64.)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Double
+
+@interface GPBDoubleArrayTests : XCTestCase
+@end
+
+@implementation GPBDoubleArrayTests
+
+- (void)testEmpty {
+  GPBDoubleArray *array = [[GPBDoubleArray alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBDoubleArray *array = [GPBDoubleArray arrayWithValue:61.];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 61.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 61.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const double kValues[] = { 61., 62., 63., 64. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+  XCTAssertEqual([array valueAtIndex:1], 62.);
+  XCTAssertEqual([array valueAtIndex:2], 63.);
+  XCTAssertEqual([array valueAtIndex:3], 64.);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(double value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const double kValues1[] = { 61., 62., 63. };
+  const double kValues2[] = { 61., 64., 63. };
+  const double kValues3[] = { 61., 62., 63., 64. };
+  GPBDoubleArray *array1 =
+      [[GPBDoubleArray alloc] initWithValues:kValues1
+                                       count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBDoubleArray *array1prime =
+      [[GPBDoubleArray alloc] initWithValues:kValues1
+                                       count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBDoubleArray *array2 =
+      [[GPBDoubleArray alloc] initWithValues:kValues2
+                                       count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBDoubleArray *array3 =
+      [[GPBDoubleArray alloc] initWithValues:kValues3
+                                       count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const double kValues[] = { 61., 62., 63., 64. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBDoubleArray *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const double kValues[] = { 61., 62., 63., 64. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBDoubleArray *array2 = [GPBDoubleArray arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBDoubleArray *array = [GPBDoubleArray array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:61.];
+  XCTAssertEqual(array.count, 1U);
+
+  const double kValues1[] = { 62., 63. };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const double kValues2[] = { 64., 61. };
+  GPBDoubleArray *array2 =
+      [[GPBDoubleArray alloc] initWithValues:kValues2
+                                       count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+  XCTAssertEqual([array valueAtIndex:1], 62.);
+  XCTAssertEqual([array valueAtIndex:2], 63.);
+  XCTAssertEqual([array valueAtIndex:3], 64.);
+  XCTAssertEqual([array valueAtIndex:4], 61.);
+}
+
+- (void)testInsert {
+  const double kValues[] = { 61., 62., 63. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:64. atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:64. atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:64. atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:64. atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 64.);
+  XCTAssertEqual([array valueAtIndex:1], 61.);
+  XCTAssertEqual([array valueAtIndex:2], 64.);
+  XCTAssertEqual([array valueAtIndex:3], 62.);
+  XCTAssertEqual([array valueAtIndex:4], 63.);
+  XCTAssertEqual([array valueAtIndex:5], 64.);
+}
+
+- (void)testRemove {
+  const double kValues[] = { 64., 61., 62., 64., 63., 64. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 63.);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+  XCTAssertEqual([array valueAtIndex:1], 62.);
+  XCTAssertEqual([array valueAtIndex:2], 63.);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const double kValues[] = { 61., 61., 63., 63. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:62.];
+  [array replaceValueAtIndex:3 withValue:64.];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+  XCTAssertEqual([array valueAtIndex:1], 62.);
+  XCTAssertEqual([array valueAtIndex:2], 63.);
+  XCTAssertEqual([array valueAtIndex:3], 64.);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:64.],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 61.);
+  XCTAssertEqual([array valueAtIndex:1], 64.);
+  XCTAssertEqual([array valueAtIndex:2], 63.);
+  XCTAssertEqual([array valueAtIndex:3], 62.);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 63.);
+  XCTAssertEqual([array valueAtIndex:1], 64.);
+  XCTAssertEqual([array valueAtIndex:2], 61.);
+  XCTAssertEqual([array valueAtIndex:3], 62.);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const double kValues[] = { 61., 62., 63., 64. };
+  GPBDoubleArray *array =
+      [[GPBDoubleArray alloc] initWithValues:kValues
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:64. atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS(Bool, BOOL, TRUE, TRUE, FALSE, FALSE)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool
+
+@interface GPBBoolArrayTests : XCTestCase
+@end
+
+@implementation GPBBoolArrayTests
+
+- (void)testEmpty {
+  GPBBoolArray *array = [[GPBBoolArray alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBBoolArray *array = [GPBBoolArray arrayWithValue:TRUE];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, TRUE);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, TRUE);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const BOOL kValues[] = { TRUE, TRUE, FALSE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+  XCTAssertEqual([array valueAtIndex:1], TRUE);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+  XCTAssertEqual([array valueAtIndex:3], FALSE);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const BOOL kValues1[] = { TRUE, TRUE, FALSE };
+  const BOOL kValues2[] = { TRUE, FALSE, FALSE };
+  const BOOL kValues3[] = { TRUE, TRUE, FALSE, FALSE };
+  GPBBoolArray *array1 =
+      [[GPBBoolArray alloc] initWithValues:kValues1
+                                     count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBBoolArray *array1prime =
+      [[GPBBoolArray alloc] initWithValues:kValues1
+                                     count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBBoolArray *array2 =
+      [[GPBBoolArray alloc] initWithValues:kValues2
+                                     count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBBoolArray *array3 =
+      [[GPBBoolArray alloc] initWithValues:kValues3
+                                     count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const BOOL kValues[] = { TRUE, TRUE, FALSE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBBoolArray *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const BOOL kValues[] = { TRUE, TRUE, FALSE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBBoolArray *array2 = [GPBBoolArray arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBBoolArray *array = [GPBBoolArray array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:TRUE];
+  XCTAssertEqual(array.count, 1U);
+
+  const BOOL kValues1[] = { TRUE, FALSE };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const BOOL kValues2[] = { FALSE, TRUE };
+  GPBBoolArray *array2 =
+      [[GPBBoolArray alloc] initWithValues:kValues2
+                                     count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+  XCTAssertEqual([array valueAtIndex:1], TRUE);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+  XCTAssertEqual([array valueAtIndex:3], FALSE);
+  XCTAssertEqual([array valueAtIndex:4], TRUE);
+}
+
+- (void)testInsert {
+  const BOOL kValues[] = { TRUE, TRUE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:FALSE atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:FALSE atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:FALSE atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:FALSE atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], FALSE);
+  XCTAssertEqual([array valueAtIndex:1], TRUE);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+  XCTAssertEqual([array valueAtIndex:3], TRUE);
+  XCTAssertEqual([array valueAtIndex:4], FALSE);
+  XCTAssertEqual([array valueAtIndex:5], FALSE);
+}
+
+- (void)testRemove {
+  const BOOL kValues[] = { FALSE, TRUE, TRUE, FALSE, FALSE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+  XCTAssertEqual([array valueAtIndex:1], TRUE);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const BOOL kValues[] = { TRUE, TRUE, FALSE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:TRUE];
+  [array replaceValueAtIndex:3 withValue:FALSE];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+  XCTAssertEqual([array valueAtIndex:1], TRUE);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+  XCTAssertEqual([array valueAtIndex:3], FALSE);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:FALSE],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], TRUE);
+  XCTAssertEqual([array valueAtIndex:1], FALSE);
+  XCTAssertEqual([array valueAtIndex:2], FALSE);
+  XCTAssertEqual([array valueAtIndex:3], TRUE);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], FALSE);
+  XCTAssertEqual([array valueAtIndex:1], FALSE);
+  XCTAssertEqual([array valueAtIndex:2], TRUE);
+  XCTAssertEqual([array valueAtIndex:3], TRUE);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const BOOL kValues[] = { TRUE, TRUE, FALSE, FALSE };
+  GPBBoolArray *array =
+      [[GPBBoolArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:FALSE atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_TESTS2(Enum, int32_t, 71, 72, 73, 74, Raw)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Enum
+
+@interface GPBEnumArrayTests : XCTestCase
+@end
+
+@implementation GPBEnumArrayTests
+
+- (void)testEmpty {
+  GPBEnumArray *array = [[GPBEnumArray alloc] init];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:0], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    #pragma unused(value, idx, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [array release];
+}
+
+- (void)testOne {
+  GPBEnumArray *array = [GPBEnumArray arrayWithValue:71];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 1U);
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:1], NSException, NSRangeException);
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 71);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, 0U);
+    XCTAssertEqual(value, 71);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  static const int32_t kValues[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertEqual([array valueAtIndex:1], 72);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+  XCTAssertEqual([array valueAtIndex:3], 74);
+  XCTAssertThrowsSpecificNamed([array valueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const int32_t kValues1[] = { 71, 72, 73 };
+  const int32_t kValues2[] = { 71, 74, 73 };
+  const int32_t kValues3[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array1 =
+      [[GPBEnumArray alloc] initWithValues:kValues1
+                                     count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBEnumArray *array1prime =
+      [[GPBEnumArray alloc] initWithValues:kValues1
+                                     count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBEnumArray *array2 =
+      [[GPBEnumArray alloc] initWithValues:kValues2
+                                     count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBEnumArray *array3 =
+      [[GPBEnumArray alloc] initWithValues:kValues3
+                                     count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const int32_t kValues[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBEnumArray *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testArrayFromArray {
+  const int32_t kValues[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBEnumArray *array2 = [GPBEnumArray arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+}
+
+- (void)testAdds {
+  GPBEnumArray *array = [GPBEnumArray array];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addValue:71];
+  XCTAssertEqual(array.count, 1U);
+
+  const int32_t kValues1[] = { 72, 73 };
+  [array addValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const int32_t kValues2[] = { 74, 71 };
+  GPBEnumArray *array2 =
+      [[GPBEnumArray alloc] initWithValues:kValues2
+                                     count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addRawValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertEqual([array valueAtIndex:1], 72);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+  XCTAssertEqual([array valueAtIndex:3], 74);
+  XCTAssertEqual([array valueAtIndex:4], 71);
+}
+
+- (void)testInsert {
+  const int32_t kValues[] = { 71, 72, 73 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertValue:74 atIndex:0];
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertValue:74 atIndex:2];
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertValue:74 atIndex:5];
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertValue:74 atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array valueAtIndex:0], 74);
+  XCTAssertEqual([array valueAtIndex:1], 71);
+  XCTAssertEqual([array valueAtIndex:2], 74);
+  XCTAssertEqual([array valueAtIndex:3], 72);
+  XCTAssertEqual([array valueAtIndex:4], 73);
+  XCTAssertEqual([array valueAtIndex:5], 74);
+}
+
+- (void)testRemove {
+  const int32_t kValues[] = { 74, 71, 72, 74, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 6U);
+
+  // First
+  [array removeValueAtIndex:0];
+  XCTAssertEqual(array.count, 5U);
+  XCTAssertEqual([array valueAtIndex:0], 71);
+
+  // Middle
+  [array removeValueAtIndex:2];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+
+  // End
+  [array removeValueAtIndex:3];
+  XCTAssertEqual(array.count, 3U);
+
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertEqual([array valueAtIndex:1], 72);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:3],
+                               NSException, NSRangeException);
+
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+  XCTAssertThrowsSpecificNamed([array removeValueAtIndex:0],
+                               NSException, NSRangeException);
+}
+
+- (void)testInplaceMutation {
+  const int32_t kValues[] = { 71, 71, 73, 73 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withValue:72];
+  [array replaceValueAtIndex:3 withValue:74];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertEqual([array valueAtIndex:1], 72);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+  XCTAssertEqual([array valueAtIndex:3], 74);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withValue:74],
+                               NSException, NSRangeException);
+
+  [array exchangeValueAtIndex:1 withValueAtIndex:3];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertEqual([array valueAtIndex:1], 74);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+  XCTAssertEqual([array valueAtIndex:3], 72);
+
+  [array exchangeValueAtIndex:2 withValueAtIndex:0];
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 73);
+  XCTAssertEqual([array valueAtIndex:1], 74);
+  XCTAssertEqual([array valueAtIndex:2], 71);
+  XCTAssertEqual([array valueAtIndex:3], 72);
+
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:4 withValueAtIndex:1],
+                               NSException, NSRangeException);
+  XCTAssertThrowsSpecificNamed([array exchangeValueAtIndex:1 withValueAtIndex:4],
+                               NSException, NSRangeException);
+}
+
+- (void)testInternalResizing {
+  const int32_t kValues[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertValue:74 atIndex:(i * 3)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end
+
+//%PDDM-EXPAND-END (8 expansions)
+
+#pragma mark - Non macro-based Enum tests
+
+// These are hand written tests to cover the verification and raw methods.
+
+@interface GPBEnumArrayCustomTests : XCTestCase
+@end
+
+@implementation GPBEnumArrayCustomTests
+
+- (void)testRawBasics {
+  static const int32_t kValues[] = { 71, 272, 73, 374 };
+  static const int32_t kValuesFiltered[] = {
+      71, kGPBUnrecognizedEnumeratorValue, 73, kGPBUnrecognizedEnumeratorValue
+  };
+  XCTAssertEqual(GPBARRAYSIZE(kValues), GPBARRAYSIZE(kValuesFiltered));
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 4U);
+  GPBEnumValidationFunc func = TestingEnum_IsValidValue;
+  XCTAssertEqual(array.validationFunc, func);
+  XCTAssertEqual([array rawValueAtIndex:0], 71);
+  XCTAssertEqual([array rawValueAtIndex:1], 272);
+  XCTAssertEqual([array valueAtIndex:1], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertEqual([array rawValueAtIndex:2], 73);
+  XCTAssertEqual([array rawValueAtIndex:3], 374);
+  XCTAssertEqual([array valueAtIndex:3], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertThrowsSpecificNamed([array rawValueAtIndex:4], NSException, NSRangeException);
+  __block NSUInteger idx2 = 0;
+  [array enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValuesFiltered[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateRawValuesWithOptions:NSEnumerationReverse
+                            usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateValuesWithOptions:NSEnumerationReverse
+                         usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValuesFiltered[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    ++idx2;
+  }];
+  // Stopping the enumeration.
+  idx2 = 0;
+  [array enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, idx2);
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    XCTAssertNotEqual(idx, 3U);
+    ++idx2;
+  }];
+  idx2 = 0;
+  [array enumerateRawValuesWithOptions:NSEnumerationReverse
+                            usingBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+    XCTAssertEqual(idx, (3 - idx2));
+    XCTAssertEqual(value, kValues[idx]);
+    XCTAssertNotEqual(stop, NULL);
+    if (idx2 == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 1U);
+    XCTAssertNotEqual(idx, 0U);
+    ++idx2;
+  }];
+  [array release];
+}
+
+- (void)testEquality {
+  const int32_t kValues1[] = { 71, 72, 173 };  // With unknown value
+  const int32_t kValues2[] = { 71, 74, 173 };  // With unknown value
+  const int32_t kValues3[] = { 71, 72, 173, 74 };  // With unknown value
+  GPBEnumArray *array1 =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1);
+  GPBEnumArray *array1prime =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue2
+                                             rawValues:kValues1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(array1prime);
+  GPBEnumArray *array2 =
+      [[GPBEnumArray alloc] initWithValues:kValues2
+                                     count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  GPBEnumArray *array3 =
+      [[GPBEnumArray alloc] initWithValues:kValues3
+                                     count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(array3);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(array1, array1prime);
+  XCTAssertEqualObjects(array1, array1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([array1 hash], [array1prime hash]);
+  // But different validation functions.
+  XCTAssertNotEqual(array1.validationFunc, array1prime.validationFunc);
+
+  // 1/2/3 shouldn't be equal.
+  XCTAssertNotEqualObjects(array1, array2);
+  XCTAssertNotEqualObjects(array1, array3);
+  XCTAssertNotEqualObjects(array2, array3);
+
+  [array1 release];
+  [array1prime release];
+  [array2 release];
+  [array3 release];
+}
+
+- (void)testCopy {
+  const int32_t kValues[] = { 71, 72 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array addRawValue:1000]; // Unknown
+  XCTAssertEqual(array.count, 3U);
+  XCTAssertEqual([array rawValueAtIndex:0], 71);
+  XCTAssertEqual([array rawValueAtIndex:1], 72);
+  XCTAssertEqual([array rawValueAtIndex:2], 1000);
+  XCTAssertEqual([array valueAtIndex:2], kGPBUnrecognizedEnumeratorValue);
+
+  GPBEnumArray *array2 = [array copy];
+  XCTAssertNotNil(array2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+  XCTAssertEqual(array.validationFunc, array2.validationFunc);
+  XCTAssertTrue([array2 isKindOfClass:[GPBEnumArray class]]);
+  XCTAssertEqual(array2.count, 3U);
+  XCTAssertEqual([array2 rawValueAtIndex:0], 71);
+  XCTAssertEqual([array2 rawValueAtIndex:1], 72);
+  XCTAssertEqual([array2 rawValueAtIndex:2], 1000);
+  XCTAssertEqual([array2 valueAtIndex:2], kGPBUnrecognizedEnumeratorValue);
+}
+
+- (void)testArrayFromArray {
+  const int32_t kValues[] = { 71, 172, 173, 74 };  // Unknowns
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  GPBEnumArray *array2 = [GPBEnumArray arrayWithValueArray:array];
+  XCTAssertNotNil(array2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(array, array2);
+  XCTAssertEqualObjects(array, array2);
+  XCTAssertEqual(array.validationFunc, array2.validationFunc);
+}
+
+- (void)testUnknownAdds {
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(array);
+
+  XCTAssertThrowsSpecificNamed([array addValue:172],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(array.count, 0U);
+
+  const int32_t kValues1[] = { 172, 173 };  // Unknown
+  XCTAssertThrowsSpecificNamed([array addValues:kValues1 count:GPBARRAYSIZE(kValues1)],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(array.count, 0U);
+
+  [array release];
+}
+
+- (void)testRawAdds {
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(array);
+
+  XCTAssertEqual(array.count, 0U);
+  [array addRawValue:71];  // Valid
+  XCTAssertEqual(array.count, 1U);
+
+  const int32_t kValues1[] = { 172, 173 };  // Unknown
+  [array addRawValues:kValues1 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertEqual(array.count, 3U);
+
+  const int32_t kValues2[] = { 74, 71 };
+  GPBEnumArray *array2 =
+      [[GPBEnumArray alloc] initWithValues:kValues2
+                                     count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(array2);
+  [array addRawValuesFromArray:array2];
+  XCTAssertEqual(array.count, 5U);
+
+  XCTAssertEqual([array rawValueAtIndex:0], 71);
+  XCTAssertEqual([array rawValueAtIndex:1], 172);
+  XCTAssertEqual([array valueAtIndex:1], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertEqual([array rawValueAtIndex:2], 173);
+  XCTAssertEqual([array valueAtIndex:2], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertEqual([array rawValueAtIndex:3], 74);
+  XCTAssertEqual([array rawValueAtIndex:4], 71);
+
+  [array release];
+}
+
+- (void)testUnknownInserts {
+  const int32_t kValues[] = { 71, 72, 73 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  XCTAssertThrowsSpecificNamed([array insertValue:174 atIndex:0],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(array.count, 3U);
+
+  // Middle
+  XCTAssertThrowsSpecificNamed([array insertValue:274 atIndex:1],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(array.count, 3U);
+
+  // End
+  XCTAssertThrowsSpecificNamed([array insertValue:374 atIndex:3],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(array.count, 3U);
+}
+
+- (void)testRawInsert {
+  const int32_t kValues[] = { 71, 72, 73 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+  XCTAssertEqual(array.count, 3U);
+
+  // First
+  [array insertRawValue:174 atIndex:0];  // Unknown
+  XCTAssertEqual(array.count, 4U);
+
+  // Middle
+  [array insertRawValue:274 atIndex:2];  // Unknown
+  XCTAssertEqual(array.count, 5U);
+
+  // End
+  [array insertRawValue:374 atIndex:5];  // Unknown
+  XCTAssertEqual(array.count, 6U);
+
+  // Too far.
+  XCTAssertThrowsSpecificNamed([array insertRawValue:74 atIndex:7],
+                               NSException, NSRangeException);
+
+  XCTAssertEqual([array rawValueAtIndex:0], 174);
+  XCTAssertEqual([array valueAtIndex:0], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertEqual([array rawValueAtIndex:1], 71);
+  XCTAssertEqual([array rawValueAtIndex:2], 274);
+  XCTAssertEqual([array valueAtIndex:2], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertEqual([array rawValueAtIndex:3], 72);
+  XCTAssertEqual([array rawValueAtIndex:4], 73);
+  XCTAssertEqual([array rawValueAtIndex:5], 374);
+  XCTAssertEqual([array valueAtIndex:5], kGPBUnrecognizedEnumeratorValue);
+
+  [array release];
+}
+
+- (void)testUnknownInplaceMutation {
+  const int32_t kValues[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:1 withValue:172],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:3 withValue:274],
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array valueAtIndex:0], 71);
+  XCTAssertEqual([array valueAtIndex:1], 72);
+  XCTAssertEqual([array valueAtIndex:2], 73);
+  XCTAssertEqual([array valueAtIndex:3], 74);
+}
+
+
+- (void)testRawInplaceMutation {
+  const int32_t kValues[] = { 71, 72, 73, 74 };
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                             rawValues:kValues
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  [array replaceValueAtIndex:1 withRawValue:172];  // Unknown
+  [array replaceValueAtIndex:3 withRawValue:274];  // Unknown
+  XCTAssertEqual(array.count, 4U);
+  XCTAssertEqual([array rawValueAtIndex:0], 71);
+  XCTAssertEqual([array rawValueAtIndex:1], 172);
+  XCTAssertEqual([array valueAtIndex:1], kGPBUnrecognizedEnumeratorValue);
+  XCTAssertEqual([array rawValueAtIndex:2], 73);
+  XCTAssertEqual([array rawValueAtIndex:3], 274);
+  XCTAssertEqual([array valueAtIndex:3], kGPBUnrecognizedEnumeratorValue);
+
+  XCTAssertThrowsSpecificNamed([array replaceValueAtIndex:4 withRawValue:74],
+                               NSException, NSRangeException);
+}
+
+- (void)testRawInternalResizing {
+  const int32_t kValues[] = { 71, 172, 173, 74 };  // Unknown
+  GPBEnumArray *array =
+      [[GPBEnumArray alloc] initWithValues:kValues
+                                     count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(array);
+
+  // Add/remove to trigger the intneral buffer to grow/shrink.
+  for (int i = 0; i < 100; ++i) {
+    [array addRawValues:kValues count:GPBARRAYSIZE(kValues)];
+  }
+  XCTAssertEqual(array.count, 404U);
+  for (int i = 0; i < 100; ++i) {
+    [array removeValueAtIndex:(i * 2)];
+  }
+  XCTAssertEqual(array.count, 304U);
+  for (int i = 0; i < 100; ++i) {
+    [array insertRawValue:274 atIndex:(i * 3)];  // Unknown
+  }
+  XCTAssertEqual(array.count, 404U);
+  [array removeAll];
+  XCTAssertEqual(array.count, 0U);
+}
+
+@end

+ 290 - 0
objectivec/Tests/GPBCodedInputStreamTests.m

@@ -0,0 +1,290 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBTestUtilities.h"
+
+#import "GPBCodedInputStream.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+#import "google/protobuf/Unittest.pbobjc.h"
+
+@interface CodedInputStreamTests : GPBTestCase
+@end
+
+@implementation CodedInputStreamTests
+
+- (NSData*)bytes_with_sentinel:(int32_t)unused, ... {
+  va_list list;
+  va_start(list, unused);
+
+  NSMutableData* values = [NSMutableData dataWithCapacity:0];
+  int32_t i;
+
+  while ((i = va_arg(list, int32_t)) != 256) {
+    NSAssert(i >= 0 && i < 256, @"");
+    uint8_t u = (uint8_t)i;
+    [values appendBytes:&u length:1];
+  }
+
+  va_end(list);
+
+  return values;
+}
+
+#define bytes(...) [self bytes_with_sentinel:0, __VA_ARGS__, 256]
+
+- (void)testDecodeZigZag {
+  XCTAssertEqual(0, GPBDecodeZigZag32(0));
+  XCTAssertEqual(-1, GPBDecodeZigZag32(1));
+  XCTAssertEqual(1, GPBDecodeZigZag32(2));
+  XCTAssertEqual(-2, GPBDecodeZigZag32(3));
+  XCTAssertEqual((int32_t)0x3FFFFFFF, GPBDecodeZigZag32(0x7FFFFFFE));
+  XCTAssertEqual((int32_t)0xC0000000, GPBDecodeZigZag32(0x7FFFFFFF));
+  XCTAssertEqual((int32_t)0x7FFFFFFF, GPBDecodeZigZag32(0xFFFFFFFE));
+  XCTAssertEqual((int32_t)0x80000000, GPBDecodeZigZag32(0xFFFFFFFF));
+
+  XCTAssertEqual((int64_t)0, GPBDecodeZigZag64(0));
+  XCTAssertEqual((int64_t)-1, GPBDecodeZigZag64(1));
+  XCTAssertEqual((int64_t)1, GPBDecodeZigZag64(2));
+  XCTAssertEqual((int64_t)-2, GPBDecodeZigZag64(3));
+  XCTAssertEqual((int64_t)0x000000003FFFFFFFL,
+                 GPBDecodeZigZag64(0x000000007FFFFFFEL));
+  XCTAssertEqual((int64_t)0xFFFFFFFFC0000000L,
+                 GPBDecodeZigZag64(0x000000007FFFFFFFL));
+  XCTAssertEqual((int64_t)0x000000007FFFFFFFL,
+                 GPBDecodeZigZag64(0x00000000FFFFFFFEL));
+  XCTAssertEqual((int64_t)0xFFFFFFFF80000000L,
+                 GPBDecodeZigZag64(0x00000000FFFFFFFFL));
+  XCTAssertEqual((int64_t)0x7FFFFFFFFFFFFFFFL,
+                 GPBDecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+  XCTAssertEqual((int64_t)0x8000000000000000L,
+                 GPBDecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+}
+
+- (void)assertReadVarint:(NSData*)data value:(int64_t)value {
+  {
+    GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+    XCTAssertEqual((int32_t)value, [input readInt32]);
+  }
+  {
+    GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+    XCTAssertEqual(value, [input readInt64]);
+  }
+}
+
+- (void)assertReadLittleEndian32:(NSData*)data value:(int32_t)value {
+  GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+  XCTAssertEqual(value, [input readSFixed32]);
+}
+
+- (void)assertReadLittleEndian64:(NSData*)data value:(int64_t)value {
+  GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+  XCTAssertEqual(value, [input readSFixed64]);
+}
+
+- (void)assertReadVarintFailure:(NSData*)data {
+  {
+    GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+    XCTAssertThrows([input readInt32]);
+  }
+  {
+    GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+    XCTAssertThrows([input readInt64]);
+  }
+}
+
+- (void)testBytes {
+  NSData* data = bytes(0xa2, 0x74);
+  XCTAssertEqual(data.length, (NSUInteger)2);
+  XCTAssertEqual(((uint8_t*)data.bytes)[0], (uint8_t)0xa2);
+  XCTAssertEqual(((uint8_t*)data.bytes)[1], (uint8_t)0x74);
+}
+
+- (void)testReadVarint {
+  [self assertReadVarint:bytes(0x00) value:0];
+  [self assertReadVarint:bytes(0x01) value:1];
+  [self assertReadVarint:bytes(0x7f) value:127];
+  // 14882
+  [self assertReadVarint:bytes(0xa2, 0x74) value:(0x22 << 0) | (0x74 << 7)];
+  // 2961488830
+  [self assertReadVarint:bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b)
+                   value:(0x3e << 0) | (0x77 << 7) | (0x12 << 14) |
+                         (0x04 << 21) | (0x0bLL << 28)];
+
+  // 64-bit
+  // 7256456126
+  [self assertReadVarint:bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b)
+                   value:(0x3e << 0) | (0x77 << 7) | (0x12 << 14) |
+                         (0x04 << 21) | (0x1bLL << 28)];
+  // 41256202580718336
+  [self assertReadVarint:bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49)
+                   value:(0x00 << 0) | (0x66 << 7) | (0x6b << 14) |
+                         (0x1c << 21) | (0x43LL << 28) | (0x49LL << 35) |
+                         (0x24LL << 42) | (0x49LL << 49)];
+  // 11964378330978735131
+  [self
+      assertReadVarint:bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85,
+                             0xa6, 0x01)
+                 value:(0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
+                       (0x3bLL << 28) | (0x56LL << 35) | (0x00LL << 42) |
+                       (0x05LL << 49) | (0x26LL << 56) | (0x01LL << 63)];
+
+  // Failures
+  [self assertReadVarintFailure:bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+                                      0x80, 0x80, 0x80, 0x00)];
+  [self assertReadVarintFailure:bytes(0x80)];
+}
+
+- (void)testReadLittleEndian {
+  [self assertReadLittleEndian32:bytes(0x78, 0x56, 0x34, 0x12)
+                           value:0x12345678];
+  [self assertReadLittleEndian32:bytes(0xf0, 0xde, 0xbc, 0x9a)
+                           value:0x9abcdef0];
+
+  [self assertReadLittleEndian64:bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34,
+                                       0x12)
+                           value:0x123456789abcdef0LL];
+  [self assertReadLittleEndian64:bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc,
+                                       0x9a)
+                           value:0x9abcdef012345678LL];
+}
+
+- (void)testReadWholeMessage {
+  TestAllTypes* message = [self allSetRepeatedCount:kGPBDefaultRepeatCount];
+
+  NSData* rawBytes = message.data;
+  XCTAssertEqual(message.serializedSize, (size_t)rawBytes.length);
+
+  TestAllTypes* message2 =
+      [TestAllTypes parseFromData:rawBytes extensionRegistry:nil];
+  [self assertAllFieldsSet:message2 repeatedCount:kGPBDefaultRepeatCount];
+}
+
+- (void)testSkipWholeMessage {
+  TestAllTypes* message = [self allSetRepeatedCount:kGPBDefaultRepeatCount];
+  NSData* rawBytes = message.data;
+
+  // Create two parallel inputs.  Parse one as unknown fields while using
+  // skipField() to skip each field on the other.  Expect the same tags.
+  GPBCodedInputStream* input1 = [GPBCodedInputStream streamWithData:rawBytes];
+  GPBCodedInputStream* input2 = [GPBCodedInputStream streamWithData:rawBytes];
+  GPBUnknownFieldSet* unknownFields =
+      [[[GPBUnknownFieldSet alloc] init] autorelease];
+
+  while (YES) {
+    int32_t tag = [input1 readTag];
+    XCTAssertEqual(tag, [input2 readTag]);
+    if (tag == 0) {
+      break;
+    }
+    [unknownFields mergeFieldFrom:tag input:input1];
+    [input2 skipField:tag];
+  }
+}
+
+- (void)testReadHugeBlob {
+  // Allocate and initialize a 1MB blob.
+  NSMutableData* blob = [NSMutableData dataWithLength:1 << 20];
+  for (NSUInteger i = 0; i < blob.length; i++) {
+    ((uint8_t*)blob.mutableBytes)[i] = (uint8_t)i;
+  }
+
+  // Make a message containing it.
+  TestAllTypes* message = [TestAllTypes message];
+  [self setAllFields:message repeatedCount:kGPBDefaultRepeatCount];
+  [message setOptionalBytes:blob];
+
+  // Serialize and parse it.  Make sure to parse from an InputStream, not
+  // directly from a ByteString, so that CodedInputStream uses buffered
+  // reading.
+  GPBCodedInputStream* stream =
+      [GPBCodedInputStream streamWithData:message.data];
+  TestAllTypes* message2 =
+      [TestAllTypes parseFromCodedInputStream:stream extensionRegistry:nil];
+
+  XCTAssertEqualObjects(message.optionalBytes, message2.optionalBytes);
+
+  // Make sure all the other fields were parsed correctly.
+  TestAllTypes* message3 = [[message2 copy] autorelease];
+  TestAllTypes* types = [self allSetRepeatedCount:kGPBDefaultRepeatCount];
+  NSData* data = [types optionalBytes];
+  [message3 setOptionalBytes:data];
+
+  [self assertAllFieldsSet:message3 repeatedCount:kGPBDefaultRepeatCount];
+}
+
+- (void)testReadMaliciouslyLargeBlob {
+  NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+  GPBCodedOutputStream* output =
+      [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+
+  int32_t tag = GPBWireFormatMakeTag(1, GPBWireFormatLengthDelimited);
+  [output writeRawVarint32:tag];
+  [output writeRawVarint32:0x7FFFFFFF];
+  uint8_t bytes[32] = {0};
+  [output writeRawData:[NSData dataWithBytes:bytes length:32]];
+  [output flush];
+
+  NSData* data =
+      [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+  GPBCodedInputStream* input =
+      [GPBCodedInputStream streamWithData:[NSMutableData dataWithData:data]];
+  XCTAssertEqual(tag, [input readTag]);
+
+  XCTAssertThrows([input readData]);
+}
+
+// Verifies fix for b/10315336.
+- (void)testReadMalformedString {
+  NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+  GPBCodedOutputStream* output =
+      [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+
+  int32_t tag = GPBWireFormatMakeTag(TestAllTypes_FieldNumber_DefaultString,
+                                     GPBWireFormatLengthDelimited);
+  [output writeRawVarint32:tag];
+  [output writeRawVarint32:5];
+  // Create an invalid utf-8 byte array.
+  uint8_t bytes[5] = {0xc2, 0xf2};
+  [output writeRawData:[NSData dataWithBytes:bytes length:sizeof(bytes)]];
+  [output flush];
+
+  NSData* data =
+      [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+  GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
+  TestAllTypes* message =
+      [TestAllTypes parseFromCodedInputStream:input extensionRegistry:nil];
+  // Make sure we can read string properties twice without crashing.
+  XCTAssertEqual([message.defaultString length], (NSUInteger)0);
+  XCTAssertEqualObjects(@"", message.defaultString);
+}
+
+@end

+ 321 - 0
objectivec/Tests/GPBCodedOuputStreamTests.m

@@ -0,0 +1,321 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBTestUtilities.h"
+
+#import "GPBCodedOutputStream.h"
+#import "GPBCodedInputStream.h"
+#import "GPBUtilities_PackagePrivate.h"
+#import "google/protobuf/Unittest.pbobjc.h"
+
+@interface CodedOutputStreamTests : GPBTestCase
+@end
+
+@implementation CodedOutputStreamTests
+
+- (NSData*)bytes_with_sentinel:(int32_t)unused, ... {
+  va_list list;
+  va_start(list, unused);
+
+  NSMutableData* values = [NSMutableData dataWithCapacity:0];
+  int32_t i;
+
+  while ((i = va_arg(list, int32_t)) != 256) {
+    NSAssert(i >= 0 && i < 256, @"");
+    uint8_t u = (uint8_t)i;
+    [values appendBytes:&u length:1];
+  }
+
+  va_end(list);
+
+  return values;
+}
+
+#define bytes(...) [self bytes_with_sentinel:0, __VA_ARGS__, 256]
+
+- (void)assertWriteLittleEndian32:(NSData*)data value:(int32_t)value {
+  NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+  GPBCodedOutputStream* output =
+      [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+  [output writeRawLittleEndian32:(int32_t)value];
+  [output flush];
+
+  NSData* actual =
+      [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+  XCTAssertEqualObjects(data, actual);
+
+  // Try different block sizes.
+  for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+    rawOutput = [NSOutputStream outputStreamToMemory];
+    output = [GPBCodedOutputStream streamWithOutputStream:rawOutput
+                                               bufferSize:blockSize];
+    [output writeRawLittleEndian32:(int32_t)value];
+    [output flush];
+
+    actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    XCTAssertEqualObjects(data, actual);
+  }
+}
+
+- (void)assertWriteLittleEndian64:(NSData*)data value:(int64_t)value {
+  NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+  GPBCodedOutputStream* output =
+      [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+  [output writeRawLittleEndian64:value];
+  [output flush];
+
+  NSData* actual =
+      [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+  XCTAssertEqualObjects(data, actual);
+
+  // Try different block sizes.
+  for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+    rawOutput = [NSOutputStream outputStreamToMemory];
+    output = [GPBCodedOutputStream streamWithOutputStream:rawOutput
+                                               bufferSize:blockSize];
+    [output writeRawLittleEndian64:value];
+    [output flush];
+
+    actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    XCTAssertEqualObjects(data, actual);
+  }
+}
+
+- (void)assertWriteVarint:(NSData*)data value:(int64_t)value {
+  // Only do 32-bit write if the value fits in 32 bits.
+  if (GPBLogicalRightShift64(value, 32) == 0) {
+    NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+    GPBCodedOutputStream* output =
+        [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+    [output writeRawVarint32:(int32_t)value];
+    [output flush];
+
+    NSData* actual =
+        [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    XCTAssertEqualObjects(data, actual);
+
+    // Also try computing size.
+    XCTAssertEqual(GPBComputeRawVarint32Size((int32_t)value),
+                   (size_t)data.length);
+  }
+
+  {
+    NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+    GPBCodedOutputStream* output =
+        [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+    [output writeRawVarint64:value];
+    [output flush];
+
+    NSData* actual =
+        [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    XCTAssertEqualObjects(data, actual);
+
+    // Also try computing size.
+    XCTAssertEqual(GPBComputeRawVarint64Size(value), (size_t)data.length);
+  }
+
+  // Try different block sizes.
+  for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+    // Only do 32-bit write if the value fits in 32 bits.
+    if (GPBLogicalRightShift64(value, 32) == 0) {
+      NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+      GPBCodedOutputStream* output =
+          [GPBCodedOutputStream streamWithOutputStream:rawOutput
+                                            bufferSize:blockSize];
+
+      [output writeRawVarint32:(int32_t)value];
+      [output flush];
+
+      NSData* actual =
+          [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+      XCTAssertEqualObjects(data, actual);
+    }
+
+    {
+      NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+      GPBCodedOutputStream* output =
+          [GPBCodedOutputStream streamWithOutputStream:rawOutput
+                                            bufferSize:blockSize];
+
+      [output writeRawVarint64:value];
+      [output flush];
+
+      NSData* actual =
+          [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+      XCTAssertEqualObjects(data, actual);
+    }
+  }
+}
+
+- (void)testWriteVarint1 {
+  [self assertWriteVarint:bytes(0x00) value:0];
+}
+
+- (void)testWriteVarint2 {
+  [self assertWriteVarint:bytes(0x01) value:1];
+}
+
+- (void)testWriteVarint3 {
+  [self assertWriteVarint:bytes(0x7f) value:127];
+}
+
+- (void)testWriteVarint4 {
+  // 14882
+  [self assertWriteVarint:bytes(0xa2, 0x74) value:(0x22 << 0) | (0x74 << 7)];
+}
+
+- (void)testWriteVarint5 {
+  // 2961488830
+  [self assertWriteVarint:bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b)
+                    value:(0x3e << 0) | (0x77 << 7) | (0x12 << 14) |
+                          (0x04 << 21) | (0x0bLL << 28)];
+}
+
+- (void)testWriteVarint6 {
+  // 64-bit
+  // 7256456126
+  [self assertWriteVarint:bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b)
+                    value:(0x3e << 0) | (0x77 << 7) | (0x12 << 14) |
+                          (0x04 << 21) | (0x1bLL << 28)];
+}
+
+- (void)testWriteVarint7 {
+  // 41256202580718336
+  [self assertWriteVarint:bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49)
+                    value:(0x00 << 0) | (0x66 << 7) | (0x6b << 14) |
+                          (0x1c << 21) | (0x43LL << 28) | (0x49LL << 35) |
+                          (0x24LL << 42) | (0x49LL << 49)];
+}
+
+- (void)testWriteVarint8 {
+  // 11964378330978735131
+  [self assertWriteVarint:bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85,
+                                0xa6, 0x01)
+                    value:(0x1b << 0) | (0x28 << 7) | (0x79 << 14) |
+                          (0x42 << 21) | (0x3bLL << 28) | (0x56LL << 35) |
+                          (0x00LL << 42) | (0x05LL << 49) | (0x26LL << 56) |
+                          (0x01LL << 63)];
+}
+
+- (void)testWriteLittleEndian {
+  [self assertWriteLittleEndian32:bytes(0x78, 0x56, 0x34, 0x12)
+                            value:0x12345678];
+  [self assertWriteLittleEndian32:bytes(0xf0, 0xde, 0xbc, 0x9a)
+                            value:0x9abcdef0];
+
+  [self assertWriteLittleEndian64:bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
+                                        0x34, 0x12)
+                            value:0x123456789abcdef0LL];
+  [self assertWriteLittleEndian64:bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde,
+                                        0xbc, 0x9a)
+                            value:0x9abcdef012345678LL];
+}
+
+- (void)testEncodeZigZag {
+  XCTAssertEqual(0U, GPBEncodeZigZag32(0));
+  XCTAssertEqual(1U, GPBEncodeZigZag32(-1));
+  XCTAssertEqual(2U, GPBEncodeZigZag32(1));
+  XCTAssertEqual(3U, GPBEncodeZigZag32(-2));
+  XCTAssertEqual(0x7FFFFFFEU, GPBEncodeZigZag32(0x3FFFFFFF));
+  XCTAssertEqual(0x7FFFFFFFU, GPBEncodeZigZag32(0xC0000000));
+  XCTAssertEqual(0xFFFFFFFEU, GPBEncodeZigZag32(0x7FFFFFFF));
+  XCTAssertEqual(0xFFFFFFFFU, GPBEncodeZigZag32(0x80000000));
+
+  XCTAssertEqual(0ULL, GPBEncodeZigZag64(0));
+  XCTAssertEqual(1ULL, GPBEncodeZigZag64(-1));
+  XCTAssertEqual(2ULL, GPBEncodeZigZag64(1));
+  XCTAssertEqual(3ULL, GPBEncodeZigZag64(-2));
+  XCTAssertEqual(0x000000007FFFFFFEULL,
+                 GPBEncodeZigZag64(0x000000003FFFFFFFLL));
+  XCTAssertEqual(0x000000007FFFFFFFULL,
+                 GPBEncodeZigZag64(0xFFFFFFFFC0000000LL));
+  XCTAssertEqual(0x00000000FFFFFFFEULL,
+                 GPBEncodeZigZag64(0x000000007FFFFFFFLL));
+  XCTAssertEqual(0x00000000FFFFFFFFULL,
+                 GPBEncodeZigZag64(0xFFFFFFFF80000000LL));
+  XCTAssertEqual(0xFFFFFFFFFFFFFFFEULL,
+                 GPBEncodeZigZag64(0x7FFFFFFFFFFFFFFFLL));
+  XCTAssertEqual(0xFFFFFFFFFFFFFFFFULL,
+                 GPBEncodeZigZag64(0x8000000000000000LL));
+
+  // Some easier-to-verify round-trip tests.  The inputs (other than 0, 1, -1)
+  // were chosen semi-randomly via keyboard bashing.
+  XCTAssertEqual(0U, GPBEncodeZigZag32(GPBDecodeZigZag32(0)));
+  XCTAssertEqual(1U, GPBEncodeZigZag32(GPBDecodeZigZag32(1)));
+  XCTAssertEqual(-1U, GPBEncodeZigZag32(GPBDecodeZigZag32(-1)));
+  XCTAssertEqual(14927U, GPBEncodeZigZag32(GPBDecodeZigZag32(14927)));
+  XCTAssertEqual(-3612U, GPBEncodeZigZag32(GPBDecodeZigZag32(-3612)));
+
+  XCTAssertEqual(0ULL, GPBEncodeZigZag64(GPBDecodeZigZag64(0)));
+  XCTAssertEqual(1ULL, GPBEncodeZigZag64(GPBDecodeZigZag64(1)));
+  XCTAssertEqual(-1ULL, GPBEncodeZigZag64(GPBDecodeZigZag64(-1)));
+  XCTAssertEqual(14927ULL, GPBEncodeZigZag64(GPBDecodeZigZag64(14927)));
+  XCTAssertEqual(-3612ULL, GPBEncodeZigZag64(GPBDecodeZigZag64(-3612)));
+
+  XCTAssertEqual(856912304801416ULL,
+                 GPBEncodeZigZag64(GPBDecodeZigZag64(856912304801416LL)));
+  XCTAssertEqual(-75123905439571256ULL,
+                 GPBEncodeZigZag64(GPBDecodeZigZag64(-75123905439571256LL)));
+}
+
+- (void)testWriteWholeMessage {
+  // Not kGPBDefaultRepeatCount because we are comparing to a golden master file
+  // that was generated with 2.
+  TestAllTypes* message = [self allSetRepeatedCount:2];
+
+  NSData* rawBytes = message.data;
+  NSData* goldenData =
+      [self getDataFileNamed:@"golden_message" dataToWrite:rawBytes];
+  XCTAssertEqualObjects(rawBytes, goldenData);
+
+  // Try different block sizes.
+  for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
+    NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+    GPBCodedOutputStream* output =
+        [GPBCodedOutputStream streamWithOutputStream:rawOutput
+                                          bufferSize:blockSize];
+    [message writeToCodedOutputStream:output];
+    [output flush];
+
+    NSData* actual =
+        [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    XCTAssertEqualObjects(rawBytes, actual);
+  }
+
+  // Not kGPBDefaultRepeatCount because we are comparing to a golden master file
+  // that was generated with 2.
+  TestAllExtensions* extensions = [self allExtensionsSetRepeatedCount:2];
+  rawBytes = extensions.data;
+  goldenData = [self getDataFileNamed:@"golden_packed_fields_message"
+                          dataToWrite:rawBytes];
+  XCTAssertEqualObjects(rawBytes, goldenData);
+}
+
+@end

+ 157 - 0
objectivec/Tests/GPBConcurrencyTests.m

@@ -0,0 +1,157 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBTestUtilities.h"
+
+#import "google/protobuf/Unittest.pbobjc.h"
+
+static const int kNumThreads = 100;
+static const int kNumMessages = 100;
+
+@interface ConcurrencyTests : GPBTestCase
+@end
+
+@implementation ConcurrencyTests
+
+- (NSArray *)createThreadsWithSelector:(SEL)selector object:(id)object {
+  NSMutableArray *array = [NSMutableArray array];
+  for (NSUInteger i = 0; i < kNumThreads; i++) {
+    NSThread *thread =
+        [[NSThread alloc] initWithTarget:self selector:selector object:object];
+    [array addObject:thread];
+    [thread release];
+  }
+  return array;
+}
+
+- (NSArray *)createMessagesWithType:(Class)msgType {
+  NSMutableArray *array = [NSMutableArray array];
+  for (NSUInteger i = 0; i < kNumMessages; i++) {
+    [array addObject:[msgType message]];
+  }
+  return array;
+}
+
+- (void)startThreads:(NSArray *)threads {
+  for (NSThread *thread in threads) {
+    [thread start];
+  }
+}
+
+- (void)joinThreads:(NSArray *)threads {
+  for (NSThread *thread in threads) {
+    while (![thread isFinished])
+      ;
+  }
+}
+
+- (void)readForeignMessage:(NSArray *)messages {
+  for (NSUInteger i = 0; i < 10; i++) {
+    for (TestAllTypes *message in messages) {
+      XCTAssertEqual(message.optionalForeignMessage.c, 0);
+    }
+  }
+}
+
+- (void)testConcurrentReadOfUnsetMessageField {
+  NSArray *messages = [self createMessagesWithType:[TestAllTypes class]];
+  NSArray *threads =
+      [self createThreadsWithSelector:@selector(readForeignMessage:)
+                               object:messages];
+  [self startThreads:threads];
+  [self joinThreads:threads];
+  for (TestAllTypes *message in messages) {
+    XCTAssertFalse(message.hasOptionalForeignMessage);
+  }
+}
+
+- (void)readRepeatedInt32:(NSArray *)messages {
+  for (int i = 0; i < 10; i++) {
+    for (TestAllTypes *message in messages) {
+      XCTAssertEqual([message.repeatedInt32Array count], (NSUInteger)0);
+    }
+  }
+}
+
+- (void)testConcurrentReadOfUnsetRepeatedIntField {
+  NSArray *messages = [self createMessagesWithType:[TestAllTypes class]];
+  NSArray *threads =
+      [self createThreadsWithSelector:@selector(readRepeatedInt32:)
+                               object:messages];
+  [self startThreads:threads];
+  [self joinThreads:threads];
+  for (TestAllTypes *message in messages) {
+    XCTAssertEqual([message.repeatedInt32Array count], (NSUInteger)0);
+  }
+}
+
+- (void)readRepeatedString:(NSArray *)messages {
+  for (int i = 0; i < 10; i++) {
+    for (TestAllTypes *message in messages) {
+      XCTAssertEqual([message.repeatedStringArray count], (NSUInteger)0);
+    }
+  }
+}
+
+- (void)testConcurrentReadOfUnsetRepeatedStringField {
+  NSArray *messages = [self createMessagesWithType:[TestAllTypes class]];
+  NSArray *threads =
+      [self createThreadsWithSelector:@selector(readRepeatedString:)
+                               object:messages];
+  [self startThreads:threads];
+  [self joinThreads:threads];
+  for (TestAllTypes *message in messages) {
+    XCTAssertEqual([message.repeatedStringArray count], (NSUInteger)0);
+  }
+}
+
+- (void)readOptionalForeignMessageExtension:(NSArray *)messages {
+  for (int i = 0; i < 10; i++) {
+    for (TestAllExtensions *message in messages) {
+      ForeignMessage *foreign =
+          [message getExtension:[UnittestRoot optionalForeignMessageExtension]];
+      XCTAssertEqual(foreign.c, 0);
+    }
+  }
+}
+
+- (void)testConcurrentReadOfUnsetExtensionField {
+  NSArray *messages = [self createMessagesWithType:[TestAllExtensions class]];
+  SEL sel = @selector(readOptionalForeignMessageExtension:);
+  NSArray *threads = [self createThreadsWithSelector:sel object:messages];
+  [self startThreads:threads];
+  [self joinThreads:threads];
+  GPBExtensionField *extension = [UnittestRoot optionalForeignMessageExtension];
+  for (TestAllExtensions *message in messages) {
+    XCTAssertFalse([message hasExtension:extension]);
+  }
+}
+
+@end

+ 232 - 0
objectivec/Tests/GPBDescriptorTests.m

@@ -0,0 +1,232 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "GPBTestUtilities.h"
+
+#import <objc/runtime.h>
+
+#import "GPBDescriptor.h"
+#import "google/protobuf/Unittest.pbobjc.h"
+
+@interface DescriptorTests : GPBTestCase
+@end
+
+@implementation DescriptorTests
+
+- (void)testFieldDescriptor {
+  GPBDescriptor *descriptor = [TestAllTypes descriptor];
+
+  // Nested Enum
+  GPBFieldDescriptor *fieldDescriptorWithName =
+      [descriptor fieldWithName:@"optionalNestedEnum"];
+  XCTAssertNotNil(fieldDescriptorWithName);
+  GPBFieldDescriptor *fieldDescriptorWithNumber =
+      [descriptor fieldWithNumber:21];
+  XCTAssertNotNil(fieldDescriptorWithNumber);
+  XCTAssertEqual(fieldDescriptorWithName, fieldDescriptorWithNumber);
+  XCTAssertNotNil(fieldDescriptorWithNumber.enumDescriptor);
+  XCTAssertEqualObjects(fieldDescriptorWithNumber.enumDescriptor.name,
+                        @"TestAllTypes_NestedEnum");
+
+  // Foreign Enum
+  fieldDescriptorWithName = [descriptor fieldWithName:@"optionalForeignEnum"];
+  XCTAssertNotNil(fieldDescriptorWithName);
+  fieldDescriptorWithNumber = [descriptor fieldWithNumber:22];
+  XCTAssertNotNil(fieldDescriptorWithNumber);
+  XCTAssertEqual(fieldDescriptorWithName, fieldDescriptorWithNumber);
+  XCTAssertNotNil(fieldDescriptorWithNumber.enumDescriptor);
+  XCTAssertEqualObjects(fieldDescriptorWithNumber.enumDescriptor.name,
+                        @"ForeignEnum");
+
+  // Import Enum
+  fieldDescriptorWithName = [descriptor fieldWithName:@"optionalImportEnum"];
+  XCTAssertNotNil(fieldDescriptorWithName);
+  fieldDescriptorWithNumber = [descriptor fieldWithNumber:23];
+  XCTAssertNotNil(fieldDescriptorWithNumber);
+  XCTAssertEqual(fieldDescriptorWithName, fieldDescriptorWithNumber);
+  XCTAssertNotNil(fieldDescriptorWithNumber.enumDescriptor);
+  XCTAssertEqualObjects(fieldDescriptorWithNumber.enumDescriptor.name,
+                        @"ImportEnum");
+
+  // Nested Message
+  fieldDescriptorWithName = [descriptor fieldWithName:@"optionalNestedMessage"];
+  XCTAssertNotNil(fieldDescriptorWithName);
+  fieldDescriptorWithNumber = [descriptor fieldWithNumber:18];
+  XCTAssertNotNil(fieldDescriptorWithNumber);
+  XCTAssertEqual(fieldDescriptorWithName, fieldDescriptorWithNumber);
+  XCTAssertNil(fieldDescriptorWithNumber.enumDescriptor);
+
+  // Foreign Message
+  fieldDescriptorWithName =
+      [descriptor fieldWithName:@"optionalForeignMessage"];
+  XCTAssertNotNil(fieldDescriptorWithName);
+  fieldDescriptorWithNumber = [descriptor fieldWithNumber:19];
+  XCTAssertNotNil(fieldDescriptorWithNumber);
+  XCTAssertEqual(fieldDescriptorWithName, fieldDescriptorWithNumber);
+  XCTAssertNil(fieldDescriptorWithNumber.enumDescriptor);
+
+  // Import Message
+  fieldDescriptorWithName = [descriptor fieldWithName:@"optionalImportMessage"];
+  XCTAssertNotNil(fieldDescriptorWithName);
+  fieldDescriptorWithNumber = [descriptor fieldWithNumber:20];
+  XCTAssertNotNil(fieldDescriptorWithNumber);
+  XCTAssertEqual(fieldDescriptorWithName, fieldDescriptorWithNumber);
+  XCTAssertNil(fieldDescriptorWithNumber.enumDescriptor);
+}
+
+- (void)testEnumDescriptor {
+  GPBEnumDescriptor *descriptor = TestAllTypes_NestedEnum_EnumDescriptor();
+
+  NSString *enumName = [descriptor enumNameForValue:1];
+  XCTAssertNotNil(enumName);
+  int32_t value;
+  XCTAssertTrue(
+      [descriptor getValue:&value forEnumName:@"TestAllTypes_NestedEnum_Foo"]);
+  XCTAssertTrue(
+      [descriptor getValue:NULL forEnumName:@"TestAllTypes_NestedEnum_Foo"]);
+  XCTAssertEqual(value, TestAllTypes_NestedEnum_Foo);
+
+  enumName = [descriptor enumNameForValue:2];
+  XCTAssertNotNil(enumName);
+  XCTAssertTrue(
+      [descriptor getValue:&value forEnumName:@"TestAllTypes_NestedEnum_Bar"]);
+  XCTAssertEqual(value, TestAllTypes_NestedEnum_Bar);
+
+  enumName = [descriptor enumNameForValue:3];
+  XCTAssertNotNil(enumName);
+  XCTAssertTrue(
+      [descriptor getValue:&value forEnumName:@"TestAllTypes_NestedEnum_Baz"]);
+  XCTAssertEqual(value, TestAllTypes_NestedEnum_Baz);
+
+  // Bad values
+  enumName = [descriptor enumNameForValue:0];
+  XCTAssertNil(enumName);
+  XCTAssertFalse([descriptor getValue:&value forEnumName:@"Unknown"]);
+  XCTAssertFalse([descriptor getValue:NULL forEnumName:@"Unknown"]);
+  XCTAssertFalse([descriptor getValue:&value
+                          forEnumName:@"TestAllTypes_NestedEnum_Unknown"]);
+  XCTAssertFalse([descriptor getValue:NULL
+                          forEnumName:@"TestAllTypes_NestedEnum_Unknown"]);
+}
+
+- (void)testEnumValueValidator {
+  GPBDescriptor *descriptor = [TestAllTypes descriptor];
+  GPBFieldDescriptor *fieldDescriptor =
+      [descriptor fieldWithName:@"optionalNestedEnum"];
+
+  // Valid values
+  XCTAssertTrue([fieldDescriptor isValidEnumValue:1]);
+  XCTAssertTrue([fieldDescriptor isValidEnumValue:2]);
+  XCTAssertTrue([fieldDescriptor isValidEnumValue:3]);
+  XCTAssertTrue([fieldDescriptor isValidEnumValue:-1]);
+
+  // Invalid values
+  XCTAssertFalse([fieldDescriptor isValidEnumValue:4]);
+  XCTAssertFalse([fieldDescriptor isValidEnumValue:0]);
+  XCTAssertFalse([fieldDescriptor isValidEnumValue:-2]);
+}
+
+- (void)testEnumDescriptorLookup {
+  GPBDescriptor *descriptor = [TestAllTypes descriptor];
+  GPBEnumDescriptor *enumDescriptor =
+      [descriptor enumWithName:@"TestAllTypes_NestedEnum"];
+  XCTAssertNotNil(enumDescriptor);
+
+  // Descriptor cannot find foreign or imported enums.
+  enumDescriptor = [descriptor enumWithName:@"ForeignEnumEnum"];
+  XCTAssertNil(enumDescriptor);
+  enumDescriptor = [descriptor enumWithName:@"ImportEnumEnum"];
+  XCTAssertNil(enumDescriptor);
+}
+
+- (void)testOneofDescriptor {
+  GPBDescriptor *descriptor = [TestOneof2 descriptor];
+
+  // All fields should be listed.
+  XCTAssertEqual(descriptor.fields.count, 17U);
+
+  // There are two oneofs in there.
+  XCTAssertEqual(descriptor.oneofs.count, 2U);
+
+  GPBFieldDescriptor *fooStringField =
+      [descriptor fieldWithNumber:TestOneof2_FieldNumber_FooString];
+  XCTAssertNotNil(fooStringField);
+  GPBFieldDescriptor *barStringField =
+      [descriptor fieldWithNumber:TestOneof2_FieldNumber_BarString];
+  XCTAssertNotNil(barStringField);
+
+  // Check the oneofs to have what is expected.
+
+  GPBOneofDescriptor *oneofFoo = [descriptor oneofWithName:@"foo"];
+  XCTAssertNotNil(oneofFoo);
+  XCTAssertEqual(oneofFoo.fields.count, 9U);
+
+  // Pointer comparisons.
+  XCTAssertEqual([oneofFoo fieldWithNumber:TestOneof2_FieldNumber_FooString],
+                 fooStringField);
+  XCTAssertEqual([oneofFoo fieldWithName:@"fooString"], fooStringField);
+
+  GPBOneofDescriptor *oneofBar = [descriptor oneofWithName:@"bar"];
+  XCTAssertNotNil(oneofBar);
+  XCTAssertEqual(oneofBar.fields.count, 6U);
+
+  // Pointer comparisons.
+  XCTAssertEqual([oneofBar fieldWithNumber:TestOneof2_FieldNumber_BarString],
+                 barStringField);
+  XCTAssertEqual([oneofBar fieldWithName:@"barString"], barStringField);
+
+  // Unknown oneof not found.
+
+  XCTAssertNil([descriptor oneofWithName:@"mumble"]);
+  XCTAssertNil([descriptor oneofWithName:@"Foo"]);
+
+  // Unknown oneof item.
+
+  XCTAssertNil([oneofFoo fieldWithName:@"mumble"]);
+  XCTAssertNil([oneofFoo fieldWithNumber:666]);
+
+  // Field exists, but not in this oneof.
+
+  XCTAssertNil([oneofFoo fieldWithName:@"barString"]);
+  XCTAssertNil([oneofFoo fieldWithNumber:TestOneof2_FieldNumber_BarString]);
+  XCTAssertNil([oneofBar fieldWithName:@"fooString"]);
+  XCTAssertNil([oneofBar fieldWithNumber:TestOneof2_FieldNumber_FooString]);
+
+  // Check pointers back to the enclosing oneofs.
+  // (pointer comparisions)
+  XCTAssertEqual(fooStringField.containingOneof, oneofFoo);
+  XCTAssertEqual(barStringField.containingOneof, oneofBar);
+  GPBFieldDescriptor *bazString =
+      [descriptor fieldWithNumber:TestOneof2_FieldNumber_BazString];
+  XCTAssertNotNil(bazString);
+  XCTAssertNil(bazString.containingOneof);
+}
+
+@end

+ 2421 - 0
objectivec/Tests/GPBDictionaryTests+Bool.m

@@ -0,0 +1,2421 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// Pull in the macros (using an external file because expanding all tests
+// in a single file makes a file that is failing to work with within Xcode.
+//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(UInt32, uint32_t, 100U, 101U)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> UInt32
+
+@interface GPBBoolUInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolUInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBBoolUInt32Dictionary *dict = [[GPBBoolUInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolUInt32Dictionary *dict = [GPBBoolUInt32Dictionary dictionaryWithValue:100U forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, 100U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const uint32_t kValues[] = { 100U, 101U };
+  GPBBoolUInt32Dictionary *dict =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  uint32_t *seenValues = malloc(2 * sizeof(uint32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const uint32_t kValues1[] = { 100U, 101U };
+  const uint32_t kValues2[] = { 101U, 100U };
+  const uint32_t kValues3[] = { 101U };
+  GPBBoolUInt32Dictionary *dict1 =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolUInt32Dictionary *dict1prime =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolUInt32Dictionary *dict2 =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolUInt32Dictionary *dict3 =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolUInt32Dictionary *dict4 =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const uint32_t kValues[] = { 100U, 101U };
+  GPBBoolUInt32Dictionary *dict =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolUInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolUInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const uint32_t kValues[] = { 100U, 101U };
+  GPBBoolUInt32Dictionary *dict =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolUInt32Dictionary *dict2 =
+      [GPBBoolUInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolUInt32Dictionary *dict = [GPBBoolUInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:100U forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const uint32_t kValues[] = { 101U };
+  GPBBoolUInt32Dictionary *dict2 =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 101U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const uint32_t kValues[] = { 100U, 101U };
+  GPBBoolUInt32Dictionary *dict =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const uint32_t kValues[] = { 100U, 101U };
+  GPBBoolUInt32Dictionary *dict =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict setValue:101U forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict setValue:100U forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 100U);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const uint32_t kValues2[] = { 101U, 100U };
+  GPBBoolUInt32Dictionary *dict2 =
+      [[GPBBoolUInt32Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(Int32, int32_t, 200, 201)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Int32
+
+@interface GPBBoolInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBBoolInt32Dictionary *dict = [[GPBBoolInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolInt32Dictionary *dict = [GPBBoolInt32Dictionary dictionaryWithValue:200 forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, 200);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const int32_t kValues[] = { 200, 201 };
+  GPBBoolInt32Dictionary *dict =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 201);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  int32_t *seenValues = malloc(2 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const int32_t kValues1[] = { 200, 201 };
+  const int32_t kValues2[] = { 201, 200 };
+  const int32_t kValues3[] = { 201 };
+  GPBBoolInt32Dictionary *dict1 =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolInt32Dictionary *dict1prime =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolInt32Dictionary *dict2 =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolInt32Dictionary *dict3 =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolInt32Dictionary *dict4 =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const int32_t kValues[] = { 200, 201 };
+  GPBBoolInt32Dictionary *dict =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const int32_t kValues[] = { 200, 201 };
+  GPBBoolInt32Dictionary *dict =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolInt32Dictionary *dict2 =
+      [GPBBoolInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolInt32Dictionary *dict = [GPBBoolInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:200 forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const int32_t kValues[] = { 201 };
+  GPBBoolInt32Dictionary *dict2 =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 201);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const int32_t kValues[] = { 200, 201 };
+  GPBBoolInt32Dictionary *dict =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const int32_t kValues[] = { 200, 201 };
+  GPBBoolInt32Dictionary *dict =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict setValue:201 forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict setValue:200 forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 200);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const int32_t kValues2[] = { 201, 200 };
+  GPBBoolInt32Dictionary *dict2 =
+      [[GPBBoolInt32Dictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(UInt64, uint64_t, 300U, 301U)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> UInt64
+
+@interface GPBBoolUInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolUInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBBoolUInt64Dictionary *dict = [[GPBBoolUInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolUInt64Dictionary *dict = [GPBBoolUInt64Dictionary dictionaryWithValue:300U forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, 300U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const uint64_t kValues[] = { 300U, 301U };
+  GPBBoolUInt64Dictionary *dict =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  uint64_t *seenValues = malloc(2 * sizeof(uint64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const uint64_t kValues1[] = { 300U, 301U };
+  const uint64_t kValues2[] = { 301U, 300U };
+  const uint64_t kValues3[] = { 301U };
+  GPBBoolUInt64Dictionary *dict1 =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolUInt64Dictionary *dict1prime =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolUInt64Dictionary *dict2 =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolUInt64Dictionary *dict3 =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolUInt64Dictionary *dict4 =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const uint64_t kValues[] = { 300U, 301U };
+  GPBBoolUInt64Dictionary *dict =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolUInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolUInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const uint64_t kValues[] = { 300U, 301U };
+  GPBBoolUInt64Dictionary *dict =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolUInt64Dictionary *dict2 =
+      [GPBBoolUInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolUInt64Dictionary *dict = [GPBBoolUInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:300U forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const uint64_t kValues[] = { 301U };
+  GPBBoolUInt64Dictionary *dict2 =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 301U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const uint64_t kValues[] = { 300U, 301U };
+  GPBBoolUInt64Dictionary *dict =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const uint64_t kValues[] = { 300U, 301U };
+  GPBBoolUInt64Dictionary *dict =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict setValue:301U forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict setValue:300U forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 300U);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const uint64_t kValues2[] = { 301U, 300U };
+  GPBBoolUInt64Dictionary *dict2 =
+      [[GPBBoolUInt64Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(Int64, int64_t, 400, 401)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Int64
+
+@interface GPBBoolInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBBoolInt64Dictionary *dict = [[GPBBoolInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolInt64Dictionary *dict = [GPBBoolInt64Dictionary dictionaryWithValue:400 forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, 400);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const int64_t kValues[] = { 400, 401 };
+  GPBBoolInt64Dictionary *dict =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 401);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  int64_t *seenValues = malloc(2 * sizeof(int64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const int64_t kValues1[] = { 400, 401 };
+  const int64_t kValues2[] = { 401, 400 };
+  const int64_t kValues3[] = { 401 };
+  GPBBoolInt64Dictionary *dict1 =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolInt64Dictionary *dict1prime =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolInt64Dictionary *dict2 =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolInt64Dictionary *dict3 =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolInt64Dictionary *dict4 =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const int64_t kValues[] = { 400, 401 };
+  GPBBoolInt64Dictionary *dict =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const int64_t kValues[] = { 400, 401 };
+  GPBBoolInt64Dictionary *dict =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolInt64Dictionary *dict2 =
+      [GPBBoolInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolInt64Dictionary *dict = [GPBBoolInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:400 forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const int64_t kValues[] = { 401 };
+  GPBBoolInt64Dictionary *dict2 =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 401);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const int64_t kValues[] = { 400, 401 };
+  GPBBoolInt64Dictionary *dict =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const int64_t kValues[] = { 400, 401 };
+  GPBBoolInt64Dictionary *dict =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict setValue:401 forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict setValue:400 forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 400);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const int64_t kValues2[] = { 401, 400 };
+  GPBBoolInt64Dictionary *dict2 =
+      [[GPBBoolInt64Dictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(Bool, BOOL, NO, YES)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Bool
+
+@interface GPBBoolBoolDictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolBoolDictionaryTests
+
+- (void)testEmpty {
+  GPBBoolBoolDictionary *dict = [[GPBBoolBoolDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolBoolDictionary *dict = [GPBBoolBoolDictionary dictionaryWithValue:NO forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, NO);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const BOOL kValues[] = { NO, YES };
+  GPBBoolBoolDictionary *dict =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues
+                                            forKeys:kKeys
+                                              count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, YES);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  BOOL *seenValues = malloc(2 * sizeof(BOOL));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const BOOL kValues1[] = { NO, YES };
+  const BOOL kValues2[] = { YES, NO };
+  const BOOL kValues3[] = { YES };
+  GPBBoolBoolDictionary *dict1 =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues1
+                                            forKeys:kKeys1
+                                              count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolBoolDictionary *dict1prime =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues1
+                                            forKeys:kKeys1
+                                              count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolBoolDictionary *dict2 =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues2
+                                            forKeys:kKeys1
+                                              count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolBoolDictionary *dict3 =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues1
+                                            forKeys:kKeys2
+                                              count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolBoolDictionary *dict4 =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues3
+                                            forKeys:kKeys1
+                                              count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const BOOL kValues[] = { NO, YES };
+  GPBBoolBoolDictionary *dict =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues
+                                            forKeys:kKeys
+                                              count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolBoolDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolBoolDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const BOOL kValues[] = { NO, YES };
+  GPBBoolBoolDictionary *dict =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues
+                                            forKeys:kKeys
+                                              count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolBoolDictionary *dict2 =
+      [GPBBoolBoolDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolBoolDictionary *dict = [GPBBoolBoolDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:NO forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const BOOL kValues[] = { YES };
+  GPBBoolBoolDictionary *dict2 =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues
+                                            forKeys:kKeys
+                                              count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, YES);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const BOOL kValues[] = { NO, YES };
+  GPBBoolBoolDictionary *dict =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues
+                                     forKeys:kKeys
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const BOOL kValues[] = { NO, YES };
+  GPBBoolBoolDictionary *dict =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues
+                                     forKeys:kKeys
+                                       count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict setValue:YES forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict setValue:NO forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, NO);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const BOOL kValues2[] = { YES, NO };
+  GPBBoolBoolDictionary *dict2 =
+      [[GPBBoolBoolDictionary alloc] initWithValues:kValues2
+                                            forKeys:kKeys2
+                                              count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(Float, float, 500.f, 501.f)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Float
+
+@interface GPBBoolFloatDictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolFloatDictionaryTests
+
+- (void)testEmpty {
+  GPBBoolFloatDictionary *dict = [[GPBBoolFloatDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolFloatDictionary *dict = [GPBBoolFloatDictionary dictionaryWithValue:500.f forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, float aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, 500.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const float kValues[] = { 500.f, 501.f };
+  GPBBoolFloatDictionary *dict =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  float value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  float *seenValues = malloc(2 * sizeof(float));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, float aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const float kValues1[] = { 500.f, 501.f };
+  const float kValues2[] = { 501.f, 500.f };
+  const float kValues3[] = { 501.f };
+  GPBBoolFloatDictionary *dict1 =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolFloatDictionary *dict1prime =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolFloatDictionary *dict2 =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolFloatDictionary *dict3 =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolFloatDictionary *dict4 =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const float kValues[] = { 500.f, 501.f };
+  GPBBoolFloatDictionary *dict =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolFloatDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolFloatDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const float kValues[] = { 500.f, 501.f };
+  GPBBoolFloatDictionary *dict =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolFloatDictionary *dict2 =
+      [GPBBoolFloatDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolFloatDictionary *dict = [GPBBoolFloatDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:500.f forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const float kValues[] = { 501.f };
+  GPBBoolFloatDictionary *dict2 =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  float value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 501.f);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const float kValues[] = { 500.f, 501.f };
+  GPBBoolFloatDictionary *dict =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const float kValues[] = { 500.f, 501.f };
+  GPBBoolFloatDictionary *dict =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  float value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict setValue:501.f forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict setValue:500.f forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 500.f);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const float kValues2[] = { 501.f, 500.f };
+  GPBBoolFloatDictionary *dict2 =
+      [[GPBBoolFloatDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND BOOL_TESTS_FOR_POD_VALUE(Double, double, 600., 601.)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Double
+
+@interface GPBBoolDoubleDictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolDoubleDictionaryTests
+
+- (void)testEmpty {
+  GPBBoolDoubleDictionary *dict = [[GPBBoolDoubleDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolDoubleDictionary *dict = [GPBBoolDoubleDictionary dictionaryWithValue:600. forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, double aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqual(aValue, 600.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const double kValues[] = { 600., 601. };
+  GPBBoolDoubleDictionary *dict =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  double value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  double *seenValues = malloc(2 * sizeof(double));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, double aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const double kValues1[] = { 600., 601. };
+  const double kValues2[] = { 601., 600. };
+  const double kValues3[] = { 601. };
+  GPBBoolDoubleDictionary *dict1 =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolDoubleDictionary *dict1prime =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolDoubleDictionary *dict2 =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolDoubleDictionary *dict3 =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolDoubleDictionary *dict4 =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const double kValues[] = { 600., 601. };
+  GPBBoolDoubleDictionary *dict =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolDoubleDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolDoubleDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const double kValues[] = { 600., 601. };
+  GPBBoolDoubleDictionary *dict =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolDoubleDictionary *dict2 =
+      [GPBBoolDoubleDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolDoubleDictionary *dict = [GPBBoolDoubleDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:600. forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const double kValues[] = { 601. };
+  GPBBoolDoubleDictionary *dict2 =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  double value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 601.);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const double kValues[] = { 600., 601. };
+  GPBBoolDoubleDictionary *dict =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:YES value:NULL]);
+  XCTAssertFalse([dict valueForKey:NO value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const double kValues[] = { 600., 601. };
+  GPBBoolDoubleDictionary *dict =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  double value;
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict setValue:601. forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict setValue:600. forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 600.);
+
+  const BOOL kKeys2[] = { NO, YES };
+  const double kValues2[] = { 601., 600. };
+  GPBBoolDoubleDictionary *dict2 =
+      [[GPBBoolDoubleDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:YES value:NULL]);
+  XCTAssertTrue([dict valueForKey:YES value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:NO value:NULL]);
+  XCTAssertTrue([dict valueForKey:NO value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND TESTS_FOR_BOOL_KEY_OBJECT_VALUE(Object, id, @"abc", @"def")
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Object
+
+@interface GPBBoolObjectDictionaryTests : XCTestCase
+@end
+
+@implementation GPBBoolObjectDictionaryTests
+
+- (void)testEmpty {
+  GPBBoolObjectDictionary *dict = [[GPBBoolObjectDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:YES]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBBoolObjectDictionary *dict = [GPBBoolObjectDictionary dictionaryWithValue:@"abc" forKey:YES];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertNil([dict valueForKey:NO]);
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, id aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, YES);
+    XCTAssertEqualObjects(aValue, @"abc");
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const BOOL kKeys[] = { YES, NO };
+  const id kValues[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary *dict =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:NO], @"def");
+
+  __block NSUInteger idx = 0;
+  BOOL *seenKeys = malloc(2 * sizeof(BOOL));
+  id *seenValues = malloc(2 * sizeof(id));
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, id aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 2U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 2; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 2) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqualObjects(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(BOOL aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 0) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const BOOL kKeys1[] = { YES, NO };
+  const BOOL kKeys2[] = { NO, YES };
+  const id kValues1[] = { @"abc", @"def" };
+  const id kValues2[] = { @"def", @"abc" };
+  const id kValues3[] = { @"def" };
+  GPBBoolObjectDictionary *dict1 =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBBoolObjectDictionary *dict1prime =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBBoolObjectDictionary *dict2 =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBBoolObjectDictionary *dict3 =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBBoolObjectDictionary *dict4 =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 Fewer pairs; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const BOOL kKeys[] = { YES, NO };
+  const id kValues[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary *dict =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolObjectDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBBoolObjectDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const BOOL kKeys[] = { YES, NO };
+  const id kValues[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary *dict =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBBoolObjectDictionary *dict2 =
+      [GPBBoolObjectDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBBoolObjectDictionary *dict = [GPBBoolObjectDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:@"abc" forKey:YES];
+  XCTAssertEqual(dict.count, 1U);
+
+  const BOOL kKeys[] = { NO };
+  const id kValues[] = { @"def" };
+  GPBBoolObjectDictionary *dict2 =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:NO], @"def");
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const BOOL kKeys[] = { YES, NO};
+  const id kValues[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary *dict =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertNil([dict valueForKey:NO]);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:NO];
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertNil([dict valueForKey:NO]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:YES]);
+  XCTAssertNil([dict valueForKey:NO]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const BOOL kKeys[] = { YES, NO };
+  const id kValues[] = { @"abc", @"def" };
+  GPBBoolObjectDictionary *dict =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:NO], @"def");
+
+  [dict setValue:@"def" forKey:YES];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"def");
+  XCTAssertEqualObjects([dict valueForKey:NO], @"def");
+
+  [dict setValue:@"abc" forKey:NO];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"def");
+  XCTAssertEqualObjects([dict valueForKey:NO], @"abc");
+
+  const BOOL kKeys2[] = { NO, YES };
+  const id kValues2[] = { @"def", @"abc" };
+  GPBBoolObjectDictionary *dict2 =
+      [[GPBBoolObjectDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:YES], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:NO], @"def");
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND-END (8 expansions)
+
+

+ 3650 - 0
objectivec/Tests/GPBDictionaryTests+Int32.m

@@ -0,0 +1,3650 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+
+// Pull in the macros (using an external file because expanding all tests
+// in a single file makes a file that is failing to work with within Xcode.
+//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
+
+//%PDDM-EXPAND TEST_FOR_POD_KEY(Int32, int32_t, 11, 12, 13, 14)
+// This block of code is generated, do not edit it directly.
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// To let the testing macros work, add some extra methods to simplify things.
+@interface GPBInt32EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(int32_t)key;
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count;
+@end
+
+static BOOL TestingEnum_IsValidValue(int32_t value) {
+  switch (value) {
+    case 700:
+    case 701:
+    case 702:
+    case 703:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+@implementation GPBInt32EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(int32_t)key {
+  // Cast is needed to compiler knows what class we are invoking initWithValues: on to get the
+  // type correct.
+  return [[(GPBInt32EnumDictionary*)[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                                  rawValues:&value
+                                                                    forKeys:&key
+                                                                      count:1] autorelease];
+}
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  return [self initWithValidationFunction:TestingEnum_IsValidValue
+                                rawValues:values
+                                  forKeys:keys
+                                    count:count];
+}
+@end
+
+
+#pragma mark - Int32 -> UInt32
+
+@interface GPBInt32UInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32UInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBInt32UInt32Dictionary *dict = [[GPBInt32UInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32UInt32Dictionary *dict = [GPBInt32UInt32Dictionary dictionaryWithValue:100U forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 100U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const uint32_t kValues[] = { 100U, 101U, 102U };
+  GPBInt32UInt32Dictionary *dict =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  uint32_t *seenValues = malloc(3 * sizeof(uint32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const uint32_t kValues1[] = { 100U, 101U, 102U };
+  const uint32_t kValues2[] = { 100U, 103U, 102U };
+  const uint32_t kValues3[] = { 100U, 101U, 102U, 103U };
+  GPBInt32UInt32Dictionary *dict1 =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32UInt32Dictionary *dict1prime =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32UInt32Dictionary *dict2 =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32UInt32Dictionary *dict3 =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32UInt32Dictionary *dict4 =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt32UInt32Dictionary *dict =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32UInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32UInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt32UInt32Dictionary *dict =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32UInt32Dictionary *dict2 =
+      [GPBInt32UInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32UInt32Dictionary *dict = [GPBInt32UInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:100U forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const uint32_t kValues[] = { 101U, 102U, 103U };
+  GPBInt32UInt32Dictionary *dict2 =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 103U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt32UInt32Dictionary *dict =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt32UInt32Dictionary *dict =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:103U forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:101U forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const uint32_t kValues2[] = { 102U, 100U };
+  GPBInt32UInt32Dictionary *dict2 =
+      [[GPBInt32UInt32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Int32
+
+@interface GPBInt32Int32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32Int32DictionaryTests
+
+- (void)testEmpty {
+  GPBInt32Int32Dictionary *dict = [[GPBInt32Int32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32Int32Dictionary *dict = [GPBInt32Int32Dictionary dictionaryWithValue:200 forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 200);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const int32_t kValues[] = { 200, 201, 202 };
+  GPBInt32Int32Dictionary *dict =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const int32_t kValues1[] = { 200, 201, 202 };
+  const int32_t kValues2[] = { 200, 203, 202 };
+  const int32_t kValues3[] = { 200, 201, 202, 203 };
+  GPBInt32Int32Dictionary *dict1 =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32Int32Dictionary *dict1prime =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32Int32Dictionary *dict2 =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32Int32Dictionary *dict3 =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32Int32Dictionary *dict4 =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt32Int32Dictionary *dict =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32Int32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32Int32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt32Int32Dictionary *dict =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32Int32Dictionary *dict2 =
+      [GPBInt32Int32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32Int32Dictionary *dict = [GPBInt32Int32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:200 forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const int32_t kValues[] = { 201, 202, 203 };
+  GPBInt32Int32Dictionary *dict2 =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 203);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt32Int32Dictionary *dict =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 203);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt32Int32Dictionary *dict =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:203 forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:201 forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 201);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const int32_t kValues2[] = { 202, 200 };
+  GPBInt32Int32Dictionary *dict2 =
+      [[GPBInt32Int32Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> UInt64
+
+@interface GPBInt32UInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32UInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBInt32UInt64Dictionary *dict = [[GPBInt32UInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32UInt64Dictionary *dict = [GPBInt32UInt64Dictionary dictionaryWithValue:300U forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 300U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const uint64_t kValues[] = { 300U, 301U, 302U };
+  GPBInt32UInt64Dictionary *dict =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  uint64_t *seenValues = malloc(3 * sizeof(uint64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const uint64_t kValues1[] = { 300U, 301U, 302U };
+  const uint64_t kValues2[] = { 300U, 303U, 302U };
+  const uint64_t kValues3[] = { 300U, 301U, 302U, 303U };
+  GPBInt32UInt64Dictionary *dict1 =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32UInt64Dictionary *dict1prime =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32UInt64Dictionary *dict2 =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32UInt64Dictionary *dict3 =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32UInt64Dictionary *dict4 =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt32UInt64Dictionary *dict =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32UInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32UInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt32UInt64Dictionary *dict =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32UInt64Dictionary *dict2 =
+      [GPBInt32UInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32UInt64Dictionary *dict = [GPBInt32UInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:300U forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const uint64_t kValues[] = { 301U, 302U, 303U };
+  GPBInt32UInt64Dictionary *dict2 =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 303U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt32UInt64Dictionary *dict =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt32UInt64Dictionary *dict =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:303U forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:301U forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const uint64_t kValues2[] = { 302U, 300U };
+  GPBInt32UInt64Dictionary *dict2 =
+      [[GPBInt32UInt64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Int64
+
+@interface GPBInt32Int64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32Int64DictionaryTests
+
+- (void)testEmpty {
+  GPBInt32Int64Dictionary *dict = [[GPBInt32Int64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32Int64Dictionary *dict = [GPBInt32Int64Dictionary dictionaryWithValue:400 forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 400);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const int64_t kValues[] = { 400, 401, 402 };
+  GPBInt32Int64Dictionary *dict =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  int64_t *seenValues = malloc(3 * sizeof(int64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const int64_t kValues1[] = { 400, 401, 402 };
+  const int64_t kValues2[] = { 400, 403, 402 };
+  const int64_t kValues3[] = { 400, 401, 402, 403 };
+  GPBInt32Int64Dictionary *dict1 =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32Int64Dictionary *dict1prime =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32Int64Dictionary *dict2 =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32Int64Dictionary *dict3 =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32Int64Dictionary *dict4 =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt32Int64Dictionary *dict =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32Int64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32Int64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt32Int64Dictionary *dict =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32Int64Dictionary *dict2 =
+      [GPBInt32Int64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32Int64Dictionary *dict = [GPBInt32Int64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:400 forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const int64_t kValues[] = { 401, 402, 403 };
+  GPBInt32Int64Dictionary *dict2 =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 403);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt32Int64Dictionary *dict =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 403);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt32Int64Dictionary *dict =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:403 forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:401 forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 401);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const int64_t kValues2[] = { 402, 400 };
+  GPBInt32Int64Dictionary *dict2 =
+      [[GPBInt32Int64Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Bool
+
+@interface GPBInt32BoolDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32BoolDictionaryTests
+
+- (void)testEmpty {
+  GPBInt32BoolDictionary *dict = [[GPBInt32BoolDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32BoolDictionary *dict = [GPBInt32BoolDictionary dictionaryWithValue:YES forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, YES);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const BOOL kValues[] = { YES, YES, NO };
+  GPBInt32BoolDictionary *dict =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  BOOL *seenValues = malloc(3 * sizeof(BOOL));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const BOOL kValues1[] = { YES, YES, NO };
+  const BOOL kValues2[] = { YES, NO, NO };
+  const BOOL kValues3[] = { YES, YES, NO, NO };
+  GPBInt32BoolDictionary *dict1 =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32BoolDictionary *dict1prime =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32BoolDictionary *dict2 =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32BoolDictionary *dict3 =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32BoolDictionary *dict4 =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt32BoolDictionary *dict =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32BoolDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32BoolDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt32BoolDictionary *dict =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32BoolDictionary *dict2 =
+      [GPBInt32BoolDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32BoolDictionary *dict = [GPBInt32BoolDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:YES forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const BOOL kValues[] = { YES, NO, NO };
+  GPBInt32BoolDictionary *dict2 =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, NO);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt32BoolDictionary *dict =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, NO);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt32BoolDictionary *dict =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:NO forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:YES forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, YES);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const BOOL kValues2[] = { NO, YES };
+  GPBInt32BoolDictionary *dict2 =
+      [[GPBInt32BoolDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Float
+
+@interface GPBInt32FloatDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32FloatDictionaryTests
+
+- (void)testEmpty {
+  GPBInt32FloatDictionary *dict = [[GPBInt32FloatDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32FloatDictionary *dict = [GPBInt32FloatDictionary dictionaryWithValue:500.f forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, float aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 500.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const float kValues[] = { 500.f, 501.f, 502.f };
+  GPBInt32FloatDictionary *dict =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  float *seenValues = malloc(3 * sizeof(float));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, float aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const float kValues1[] = { 500.f, 501.f, 502.f };
+  const float kValues2[] = { 500.f, 503.f, 502.f };
+  const float kValues3[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt32FloatDictionary *dict1 =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32FloatDictionary *dict1prime =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32FloatDictionary *dict2 =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32FloatDictionary *dict3 =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32FloatDictionary *dict4 =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt32FloatDictionary *dict =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32FloatDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32FloatDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt32FloatDictionary *dict =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32FloatDictionary *dict2 =
+      [GPBInt32FloatDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32FloatDictionary *dict = [GPBInt32FloatDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:500.f forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const float kValues[] = { 501.f, 502.f, 503.f };
+  GPBInt32FloatDictionary *dict2 =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  float value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 503.f);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt32FloatDictionary *dict =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt32FloatDictionary *dict =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  float value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:503.f forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:501.f forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const float kValues2[] = { 502.f, 500.f };
+  GPBInt32FloatDictionary *dict2 =
+      [[GPBInt32FloatDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Double
+
+@interface GPBInt32DoubleDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32DoubleDictionaryTests
+
+- (void)testEmpty {
+  GPBInt32DoubleDictionary *dict = [[GPBInt32DoubleDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32DoubleDictionary *dict = [GPBInt32DoubleDictionary dictionaryWithValue:600. forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, double aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 600.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const double kValues[] = { 600., 601., 602. };
+  GPBInt32DoubleDictionary *dict =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  double *seenValues = malloc(3 * sizeof(double));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, double aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const double kValues1[] = { 600., 601., 602. };
+  const double kValues2[] = { 600., 603., 602. };
+  const double kValues3[] = { 600., 601., 602., 603. };
+  GPBInt32DoubleDictionary *dict1 =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32DoubleDictionary *dict1prime =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32DoubleDictionary *dict2 =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32DoubleDictionary *dict3 =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32DoubleDictionary *dict4 =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt32DoubleDictionary *dict =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32DoubleDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32DoubleDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt32DoubleDictionary *dict =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32DoubleDictionary *dict2 =
+      [GPBInt32DoubleDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32DoubleDictionary *dict = [GPBInt32DoubleDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:600. forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const double kValues[] = { 601., 602., 603. };
+  GPBInt32DoubleDictionary *dict2 =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  double value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 603.);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt32DoubleDictionary *dict =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt32DoubleDictionary *dict =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  double value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:603. forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:601. forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const double kValues2[] = { 602., 600. };
+  GPBInt32DoubleDictionary *dict2 =
+      [[GPBInt32DoubleDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Enum
+
+@interface GPBInt32EnumDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32EnumDictionaryTests
+
+- (void)testEmpty {
+  GPBInt32EnumDictionary *dict = [[GPBInt32EnumDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32EnumDictionary *dict = [GPBInt32EnumDictionary dictionaryWithValue:700 forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqual(aValue, 700);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const int32_t kValues[] = { 700, 701, 702 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const int32_t kValues1[] = { 700, 701, 702 };
+  const int32_t kValues2[] = { 700, 703, 702 };
+  const int32_t kValues3[] = { 700, 701, 702, 703 };
+  GPBInt32EnumDictionary *dict1 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32EnumDictionary *dict1prime =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32EnumDictionary *dict2 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32EnumDictionary *dict3 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32EnumDictionary *dict4 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32EnumDictionary *dict2 =
+      [GPBInt32EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32EnumDictionary *dict = [GPBInt32EnumDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:700 forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const int32_t kValues[] = { 701, 702, 703 };
+  GPBInt32EnumDictionary *dict2 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 703);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 703);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:703 forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:701 forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 701);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const int32_t kValues2[] = { 702, 700 };
+  GPBInt32EnumDictionary *dict2 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 701);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Enum (Unknown Enums)
+
+@interface GPBInt32EnumDictionaryUnknownEnumTests : XCTestCase
+@end
+
+@implementation GPBInt32EnumDictionaryUnknownEnumTests
+
+- (void)testRawBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const int32_t kValues[] = { 700, 801, 702 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue(dict.validationFunc == TestingEnum_IsValidValue);  // Pointer comparison
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:11 rawValue:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:12 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:12 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:13 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:13 rawValue:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:14 rawValue:NULL]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        if (i == 1) {
+          XCTAssertEqual(kGPBUnrecognizedEnumeratorValue, seenValues[j], @"i = %d, j = %d", i, j);
+        } else {
+          XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+        }
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(int32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEqualityWithUnknowns {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const int32_t kValues1[] = { 700, 801, 702 };  // Unknown
+  const int32_t kValues2[] = { 700, 803, 702 };  // Unknown
+  const int32_t kValues3[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt32EnumDictionary *dict1 =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues1
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32EnumDictionary *dict1prime =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues1
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32EnumDictionary *dict2 =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues2
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32EnumDictionary *dict3 =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues1
+                                                         forKeys:kKeys2
+                                                           count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32EnumDictionary *dict4 =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues3
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopyWithUnknowns {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknown
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertEqualObjects(dict, dict2);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32EnumDictionary *dict2 =
+      [GPBInt32EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  [dict release];
+}
+
+- (void)testUnknownAdds {
+  GPBInt32EnumDictionary *dict =
+    [GPBInt32EnumDictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertThrowsSpecificNamed([dict setValue:801 forKey:12],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 0U);
+  [dict setRawValue:801 forKey:12];  // Unknown
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 11, 13, 14 };
+  const int32_t kValues[] = { 700, 702, 803 };  // Unknown
+  GPBInt32EnumDictionary *dict2 =
+      [[GPBInt32EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:12 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:12 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:14 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:14 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  [dict2 release];
+}
+
+- (void)testUnknownRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:14 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:14 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:11 value:NULL]);
+  XCTAssertFalse([dict valueForKey:12 value:NULL]);
+  XCTAssertFalse([dict valueForKey:13 value:NULL]);
+  XCTAssertFalse([dict valueForKey:14 value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutationUnknowns {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:12 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:14 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  XCTAssertThrowsSpecificNamed([dict setValue:803 forKey:11],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 value:NULL]);
+  XCTAssertTrue([dict valueForKey:11 value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:12 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:12 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:14 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:803 forKey:11];  // Unknown
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:11 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:12 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:12 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:14 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:700 forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:11 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:12 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:12 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:13 value:NULL]);
+  XCTAssertTrue([dict valueForKey:13 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 700);
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const int32_t kValues2[] = { 702, 801 };  // Unknown
+  GPBInt32EnumDictionary *dict2 =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues2
+                                                         forKeys:kKeys2
+                                                           count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:11 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:11 rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:12 value:NULL]);
+  XCTAssertTrue([dict valueForKey:12 value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:13 rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:13 rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:14 value:NULL]);
+  XCTAssertTrue([dict valueForKey:14 value:&value]);
+  XCTAssertEqual(value, 700);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testCopyUnknowns {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const int32_t kValues[] = { 700, 801, 702, 803 };
+  GPBInt32EnumDictionary *dict =
+      [[GPBInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int32 -> Object
+
+@interface GPBInt32ObjectDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt32ObjectDictionaryTests
+
+- (void)testEmpty {
+  GPBInt32ObjectDictionary *dict = [[GPBInt32ObjectDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:11]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt32ObjectDictionary *dict = [GPBInt32ObjectDictionary dictionaryWithValue:@"abc" forKey:11];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertNil([dict valueForKey:12]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, id aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 11);
+    XCTAssertEqualObjects(aValue, @"abc");
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int32_t kKeys[] = { 11, 12, 13 };
+  const id kValues[] = { @"abc", @"def", @"ghi" };
+  GPBInt32ObjectDictionary *dict =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:12], @"def");
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertNil([dict valueForKey:14]);
+
+  __block NSUInteger idx = 0;
+  int32_t *seenKeys = malloc(3 * sizeof(int32_t));
+  id *seenValues = malloc(3 * sizeof(id));
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, id aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqualObjects(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int32_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int32_t kKeys1[] = { 11, 12, 13, 14 };
+  const int32_t kKeys2[] = { 12, 11, 14 };
+  const id kValues1[] = { @"abc", @"def", @"ghi" };
+  const id kValues2[] = { @"abc", @"jkl", @"ghi" };
+  const id kValues3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary *dict1 =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt32ObjectDictionary *dict1prime =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt32ObjectDictionary *dict2 =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt32ObjectDictionary *dict3 =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt32ObjectDictionary *dict4 =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary *dict =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32ObjectDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt32ObjectDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary *dict =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt32ObjectDictionary *dict2 =
+      [GPBInt32ObjectDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt32ObjectDictionary *dict = [GPBInt32ObjectDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:@"abc" forKey:11];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int32_t kKeys[] = { 12, 13, 14 };
+  const id kValues[] = { @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary *dict2 =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:12], @"def");
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:14], @"jkl");
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary *dict =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertNil([dict valueForKey:12]);
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:14], @"jkl");
+
+  // Remove again does nothing.
+  [dict removeValueForKey:12];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertNil([dict valueForKey:12]);
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:14], @"jkl");
+
+  [dict removeValueForKey:14];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertNil([dict valueForKey:12]);
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertNil([dict valueForKey:14]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:11]);
+  XCTAssertNil([dict valueForKey:12]);
+  XCTAssertNil([dict valueForKey:13]);
+  XCTAssertNil([dict valueForKey:14]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int32_t kKeys[] = { 11, 12, 13, 14 };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt32ObjectDictionary *dict =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:12], @"def");
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:14], @"jkl");
+
+  [dict setValue:@"jkl" forKey:11];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:12], @"def");
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:14], @"jkl");
+
+  [dict setValue:@"def" forKey:14];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:12], @"def");
+  XCTAssertEqualObjects([dict valueForKey:13], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:14], @"def");
+
+  const int32_t kKeys2[] = { 12, 13 };
+  const id kValues2[] = { @"ghi", @"abc" };
+  GPBInt32ObjectDictionary *dict2 =
+      [[GPBInt32ObjectDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:11], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:12], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:13], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:14], @"def");
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND-END TEST_FOR_POD_KEY(Int32, int32_t, 11, 12, 13, 14)
+

+ 3650 - 0
objectivec/Tests/GPBDictionaryTests+Int64.m

@@ -0,0 +1,3650 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+
+// Pull in the macros (using an external file because expanding all tests
+// in a single file makes a file that is failing to work with within Xcode.
+//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
+
+//%PDDM-EXPAND TEST_FOR_POD_KEY(Int64, int64_t, 21LL, 22LL, 23LL, 24LL)
+// This block of code is generated, do not edit it directly.
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// To let the testing macros work, add some extra methods to simplify things.
+@interface GPBInt64EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(int64_t)key;
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count;
+@end
+
+static BOOL TestingEnum_IsValidValue(int32_t value) {
+  switch (value) {
+    case 700:
+    case 701:
+    case 702:
+    case 703:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+@implementation GPBInt64EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(int64_t)key {
+  // Cast is needed to compiler knows what class we are invoking initWithValues: on to get the
+  // type correct.
+  return [[(GPBInt64EnumDictionary*)[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                                  rawValues:&value
+                                                                    forKeys:&key
+                                                                      count:1] autorelease];
+}
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  return [self initWithValidationFunction:TestingEnum_IsValidValue
+                                rawValues:values
+                                  forKeys:keys
+                                    count:count];
+}
+@end
+
+
+#pragma mark - Int64 -> UInt32
+
+@interface GPBInt64UInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64UInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBInt64UInt32Dictionary *dict = [[GPBInt64UInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64UInt32Dictionary *dict = [GPBInt64UInt32Dictionary dictionaryWithValue:100U forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 100U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const uint32_t kValues[] = { 100U, 101U, 102U };
+  GPBInt64UInt32Dictionary *dict =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  uint32_t *seenValues = malloc(3 * sizeof(uint32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const uint32_t kValues1[] = { 100U, 101U, 102U };
+  const uint32_t kValues2[] = { 100U, 103U, 102U };
+  const uint32_t kValues3[] = { 100U, 101U, 102U, 103U };
+  GPBInt64UInt32Dictionary *dict1 =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64UInt32Dictionary *dict1prime =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64UInt32Dictionary *dict2 =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64UInt32Dictionary *dict3 =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64UInt32Dictionary *dict4 =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt64UInt32Dictionary *dict =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64UInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64UInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt64UInt32Dictionary *dict =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64UInt32Dictionary *dict2 =
+      [GPBInt64UInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64UInt32Dictionary *dict = [GPBInt64UInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:100U forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const uint32_t kValues[] = { 101U, 102U, 103U };
+  GPBInt64UInt32Dictionary *dict2 =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 103U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt64UInt32Dictionary *dict =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBInt64UInt32Dictionary *dict =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:103U forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:101U forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const uint32_t kValues2[] = { 102U, 100U };
+  GPBInt64UInt32Dictionary *dict2 =
+      [[GPBInt64UInt32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Int32
+
+@interface GPBInt64Int32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64Int32DictionaryTests
+
+- (void)testEmpty {
+  GPBInt64Int32Dictionary *dict = [[GPBInt64Int32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64Int32Dictionary *dict = [GPBInt64Int32Dictionary dictionaryWithValue:200 forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 200);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const int32_t kValues[] = { 200, 201, 202 };
+  GPBInt64Int32Dictionary *dict =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const int32_t kValues1[] = { 200, 201, 202 };
+  const int32_t kValues2[] = { 200, 203, 202 };
+  const int32_t kValues3[] = { 200, 201, 202, 203 };
+  GPBInt64Int32Dictionary *dict1 =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64Int32Dictionary *dict1prime =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64Int32Dictionary *dict2 =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64Int32Dictionary *dict3 =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64Int32Dictionary *dict4 =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt64Int32Dictionary *dict =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64Int32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64Int32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt64Int32Dictionary *dict =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64Int32Dictionary *dict2 =
+      [GPBInt64Int32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64Int32Dictionary *dict = [GPBInt64Int32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:200 forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 201, 202, 203 };
+  GPBInt64Int32Dictionary *dict2 =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 203);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt64Int32Dictionary *dict =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBInt64Int32Dictionary *dict =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:203 forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:201 forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 201);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const int32_t kValues2[] = { 202, 200 };
+  GPBInt64Int32Dictionary *dict2 =
+      [[GPBInt64Int32Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> UInt64
+
+@interface GPBInt64UInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64UInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBInt64UInt64Dictionary *dict = [[GPBInt64UInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64UInt64Dictionary *dict = [GPBInt64UInt64Dictionary dictionaryWithValue:300U forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 300U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const uint64_t kValues[] = { 300U, 301U, 302U };
+  GPBInt64UInt64Dictionary *dict =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  uint64_t *seenValues = malloc(3 * sizeof(uint64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const uint64_t kValues1[] = { 300U, 301U, 302U };
+  const uint64_t kValues2[] = { 300U, 303U, 302U };
+  const uint64_t kValues3[] = { 300U, 301U, 302U, 303U };
+  GPBInt64UInt64Dictionary *dict1 =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64UInt64Dictionary *dict1prime =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64UInt64Dictionary *dict2 =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64UInt64Dictionary *dict3 =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64UInt64Dictionary *dict4 =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt64UInt64Dictionary *dict =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64UInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64UInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt64UInt64Dictionary *dict =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64UInt64Dictionary *dict2 =
+      [GPBInt64UInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64UInt64Dictionary *dict = [GPBInt64UInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:300U forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const uint64_t kValues[] = { 301U, 302U, 303U };
+  GPBInt64UInt64Dictionary *dict2 =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 303U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt64UInt64Dictionary *dict =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBInt64UInt64Dictionary *dict =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:303U forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:301U forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const uint64_t kValues2[] = { 302U, 300U };
+  GPBInt64UInt64Dictionary *dict2 =
+      [[GPBInt64UInt64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Int64
+
+@interface GPBInt64Int64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64Int64DictionaryTests
+
+- (void)testEmpty {
+  GPBInt64Int64Dictionary *dict = [[GPBInt64Int64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64Int64Dictionary *dict = [GPBInt64Int64Dictionary dictionaryWithValue:400 forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 400);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const int64_t kValues[] = { 400, 401, 402 };
+  GPBInt64Int64Dictionary *dict =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  int64_t *seenValues = malloc(3 * sizeof(int64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const int64_t kValues1[] = { 400, 401, 402 };
+  const int64_t kValues2[] = { 400, 403, 402 };
+  const int64_t kValues3[] = { 400, 401, 402, 403 };
+  GPBInt64Int64Dictionary *dict1 =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64Int64Dictionary *dict1prime =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64Int64Dictionary *dict2 =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64Int64Dictionary *dict3 =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64Int64Dictionary *dict4 =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt64Int64Dictionary *dict =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64Int64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64Int64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt64Int64Dictionary *dict =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64Int64Dictionary *dict2 =
+      [GPBInt64Int64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64Int64Dictionary *dict = [GPBInt64Int64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:400 forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const int64_t kValues[] = { 401, 402, 403 };
+  GPBInt64Int64Dictionary *dict2 =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 403);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt64Int64Dictionary *dict =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBInt64Int64Dictionary *dict =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:403 forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:401 forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 401);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const int64_t kValues2[] = { 402, 400 };
+  GPBInt64Int64Dictionary *dict2 =
+      [[GPBInt64Int64Dictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Bool
+
+@interface GPBInt64BoolDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64BoolDictionaryTests
+
+- (void)testEmpty {
+  GPBInt64BoolDictionary *dict = [[GPBInt64BoolDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64BoolDictionary *dict = [GPBInt64BoolDictionary dictionaryWithValue:YES forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, YES);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const BOOL kValues[] = { YES, YES, NO };
+  GPBInt64BoolDictionary *dict =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  BOOL *seenValues = malloc(3 * sizeof(BOOL));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const BOOL kValues1[] = { YES, YES, NO };
+  const BOOL kValues2[] = { YES, NO, NO };
+  const BOOL kValues3[] = { YES, YES, NO, NO };
+  GPBInt64BoolDictionary *dict1 =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64BoolDictionary *dict1prime =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64BoolDictionary *dict2 =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64BoolDictionary *dict3 =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64BoolDictionary *dict4 =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt64BoolDictionary *dict =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64BoolDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64BoolDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt64BoolDictionary *dict =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64BoolDictionary *dict2 =
+      [GPBInt64BoolDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64BoolDictionary *dict = [GPBInt64BoolDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:YES forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const BOOL kValues[] = { YES, NO, NO };
+  GPBInt64BoolDictionary *dict2 =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, NO);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt64BoolDictionary *dict =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBInt64BoolDictionary *dict =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:NO forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:YES forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, YES);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const BOOL kValues2[] = { NO, YES };
+  GPBInt64BoolDictionary *dict2 =
+      [[GPBInt64BoolDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Float
+
+@interface GPBInt64FloatDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64FloatDictionaryTests
+
+- (void)testEmpty {
+  GPBInt64FloatDictionary *dict = [[GPBInt64FloatDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64FloatDictionary *dict = [GPBInt64FloatDictionary dictionaryWithValue:500.f forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, float aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 500.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const float kValues[] = { 500.f, 501.f, 502.f };
+  GPBInt64FloatDictionary *dict =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  float *seenValues = malloc(3 * sizeof(float));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, float aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const float kValues1[] = { 500.f, 501.f, 502.f };
+  const float kValues2[] = { 500.f, 503.f, 502.f };
+  const float kValues3[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt64FloatDictionary *dict1 =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64FloatDictionary *dict1prime =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64FloatDictionary *dict2 =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64FloatDictionary *dict3 =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64FloatDictionary *dict4 =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt64FloatDictionary *dict =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64FloatDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64FloatDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt64FloatDictionary *dict =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64FloatDictionary *dict2 =
+      [GPBInt64FloatDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64FloatDictionary *dict = [GPBInt64FloatDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:500.f forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const float kValues[] = { 501.f, 502.f, 503.f };
+  GPBInt64FloatDictionary *dict2 =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  float value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt64FloatDictionary *dict =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBInt64FloatDictionary *dict =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  float value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:503.f forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:501.f forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const float kValues2[] = { 502.f, 500.f };
+  GPBInt64FloatDictionary *dict2 =
+      [[GPBInt64FloatDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Double
+
+@interface GPBInt64DoubleDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64DoubleDictionaryTests
+
+- (void)testEmpty {
+  GPBInt64DoubleDictionary *dict = [[GPBInt64DoubleDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64DoubleDictionary *dict = [GPBInt64DoubleDictionary dictionaryWithValue:600. forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, double aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 600.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const double kValues[] = { 600., 601., 602. };
+  GPBInt64DoubleDictionary *dict =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  double *seenValues = malloc(3 * sizeof(double));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, double aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const double kValues1[] = { 600., 601., 602. };
+  const double kValues2[] = { 600., 603., 602. };
+  const double kValues3[] = { 600., 601., 602., 603. };
+  GPBInt64DoubleDictionary *dict1 =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64DoubleDictionary *dict1prime =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64DoubleDictionary *dict2 =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64DoubleDictionary *dict3 =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64DoubleDictionary *dict4 =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt64DoubleDictionary *dict =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64DoubleDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64DoubleDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt64DoubleDictionary *dict =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64DoubleDictionary *dict2 =
+      [GPBInt64DoubleDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64DoubleDictionary *dict = [GPBInt64DoubleDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:600. forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const double kValues[] = { 601., 602., 603. };
+  GPBInt64DoubleDictionary *dict2 =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  double value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 603.);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt64DoubleDictionary *dict =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBInt64DoubleDictionary *dict =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  double value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:603. forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:601. forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const double kValues2[] = { 602., 600. };
+  GPBInt64DoubleDictionary *dict2 =
+      [[GPBInt64DoubleDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Enum
+
+@interface GPBInt64EnumDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64EnumDictionaryTests
+
+- (void)testEmpty {
+  GPBInt64EnumDictionary *dict = [[GPBInt64EnumDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64EnumDictionary *dict = [GPBInt64EnumDictionary dictionaryWithValue:700 forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqual(aValue, 700);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const int32_t kValues[] = { 700, 701, 702 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const int32_t kValues1[] = { 700, 701, 702 };
+  const int32_t kValues2[] = { 700, 703, 702 };
+  const int32_t kValues3[] = { 700, 701, 702, 703 };
+  GPBInt64EnumDictionary *dict1 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64EnumDictionary *dict1prime =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64EnumDictionary *dict2 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64EnumDictionary *dict3 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues1
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64EnumDictionary *dict4 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues3
+                                             forKeys:kKeys1
+                                               count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64EnumDictionary *dict2 =
+      [GPBInt64EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64EnumDictionary *dict = [GPBInt64EnumDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:700 forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 701, 702, 703 };
+  GPBInt64EnumDictionary *dict2 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 703);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                      forKeys:kKeys
+                                        count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:703 forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:701 forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 701);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const int32_t kValues2[] = { 702, 700 };
+  GPBInt64EnumDictionary *dict2 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues2
+                                             forKeys:kKeys2
+                                               count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 701);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Enum (Unknown Enums)
+
+@interface GPBInt64EnumDictionaryUnknownEnumTests : XCTestCase
+@end
+
+@implementation GPBInt64EnumDictionaryUnknownEnumTests
+
+- (void)testRawBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const int32_t kValues[] = { 700, 801, 702 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue(dict.validationFunc == TestingEnum_IsValidValue);  // Pointer comparison
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:23LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL rawValue:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:24LL rawValue:NULL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        if (i == 1) {
+          XCTAssertEqual(kGPBUnrecognizedEnumeratorValue, seenValues[j], @"i = %d, j = %d", i, j);
+        } else {
+          XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+        }
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(int64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEqualityWithUnknowns {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const int32_t kValues1[] = { 700, 801, 702 };  // Unknown
+  const int32_t kValues2[] = { 700, 803, 702 };  // Unknown
+  const int32_t kValues3[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt64EnumDictionary *dict1 =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues1
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64EnumDictionary *dict1prime =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues1
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64EnumDictionary *dict2 =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues2
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64EnumDictionary *dict3 =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues1
+                                                         forKeys:kKeys2
+                                                           count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64EnumDictionary *dict4 =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues3
+                                                         forKeys:kKeys1
+                                                           count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopyWithUnknowns {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknown
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertEqualObjects(dict, dict2);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64EnumDictionary *dict2 =
+      [GPBInt64EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  [dict release];
+}
+
+- (void)testUnknownAdds {
+  GPBInt64EnumDictionary *dict =
+    [GPBInt64EnumDictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertThrowsSpecificNamed([dict setValue:801 forKey:22LL],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 0U);
+  [dict setRawValue:801 forKey:22LL];  // Unknown
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 21LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 702, 803 };  // Unknown
+  GPBInt64EnumDictionary *dict2 =
+      [[GPBInt64EnumDictionary alloc] initWithValues:kValues
+                                             forKeys:kKeys
+                                               count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  [dict2 release];
+}
+
+- (void)testUnknownRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:21LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:22LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:23LL value:NULL]);
+  XCTAssertFalse([dict valueForKey:24LL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutationUnknowns {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  XCTAssertThrowsSpecificNamed([dict setValue:803 forKey:21LL],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:803 forKey:21LL];  // Unknown
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:700 forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:23LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 700);
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const int32_t kValues2[] = { 702, 801 };  // Unknown
+  GPBInt64EnumDictionary *dict2 =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues2
+                                                         forKeys:kKeys2
+                                                           count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:21LL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:22LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:22LL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:23LL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:23LL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:24LL value:NULL]);
+  XCTAssertTrue([dict valueForKey:24LL value:&value]);
+  XCTAssertEqual(value, 700);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testCopyUnknowns {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };
+  GPBInt64EnumDictionary *dict =
+      [[GPBInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                       rawValues:kValues
+                                                         forKeys:kKeys
+                                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - Int64 -> Object
+
+@interface GPBInt64ObjectDictionaryTests : XCTestCase
+@end
+
+@implementation GPBInt64ObjectDictionaryTests
+
+- (void)testEmpty {
+  GPBInt64ObjectDictionary *dict = [[GPBInt64ObjectDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:21LL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBInt64ObjectDictionary *dict = [GPBInt64ObjectDictionary dictionaryWithValue:@"abc" forKey:21LL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertNil([dict valueForKey:22LL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, id aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 21LL);
+    XCTAssertEqualObjects(aValue, @"abc");
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL };
+  const id kValues[] = { @"abc", @"def", @"ghi" };
+  GPBInt64ObjectDictionary *dict =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:22LL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertNil([dict valueForKey:24LL]);
+
+  __block NSUInteger idx = 0;
+  int64_t *seenKeys = malloc(3 * sizeof(int64_t));
+  id *seenValues = malloc(3 * sizeof(id));
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, id aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqualObjects(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(int64_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const int64_t kKeys1[] = { 21LL, 22LL, 23LL, 24LL };
+  const int64_t kKeys2[] = { 22LL, 21LL, 24LL };
+  const id kValues1[] = { @"abc", @"def", @"ghi" };
+  const id kValues2[] = { @"abc", @"jkl", @"ghi" };
+  const id kValues3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary *dict1 =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBInt64ObjectDictionary *dict1prime =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBInt64ObjectDictionary *dict2 =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBInt64ObjectDictionary *dict3 =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBInt64ObjectDictionary *dict4 =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary *dict =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64ObjectDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBInt64ObjectDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary *dict =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBInt64ObjectDictionary *dict2 =
+      [GPBInt64ObjectDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBInt64ObjectDictionary *dict = [GPBInt64ObjectDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:@"abc" forKey:21LL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const int64_t kKeys[] = { 22LL, 23LL, 24LL };
+  const id kValues[] = { @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary *dict2 =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:22LL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"jkl");
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary *dict =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertNil([dict valueForKey:22LL]);
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"jkl");
+
+  // Remove again does nothing.
+  [dict removeValueForKey:22LL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertNil([dict valueForKey:22LL]);
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"jkl");
+
+  [dict removeValueForKey:24LL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertNil([dict valueForKey:22LL]);
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertNil([dict valueForKey:24LL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:21LL]);
+  XCTAssertNil([dict valueForKey:22LL]);
+  XCTAssertNil([dict valueForKey:23LL]);
+  XCTAssertNil([dict valueForKey:24LL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const int64_t kKeys[] = { 21LL, 22LL, 23LL, 24LL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBInt64ObjectDictionary *dict =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:22LL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"jkl");
+
+  [dict setValue:@"jkl" forKey:21LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:22LL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"jkl");
+
+  [dict setValue:@"def" forKey:24LL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:22LL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"def");
+
+  const int64_t kKeys2[] = { 22LL, 23LL };
+  const id kValues2[] = { @"ghi", @"abc" };
+  GPBInt64ObjectDictionary *dict2 =
+      [[GPBInt64ObjectDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:21LL], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:22LL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:23LL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:24LL], @"def");
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND-END TEST_FOR_POD_KEY(Int64, int64_t, 21LL, 22LL, 23LL, 24LL)
+

+ 3362 - 0
objectivec/Tests/GPBDictionaryTests+String.m

@@ -0,0 +1,3362 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+
+// Pull in the macros (using an external file because expanding all tests
+// in a single file makes a file that is failing to work with within Xcode.
+//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
+
+//%PDDM-EXPAND TESTS_FOR_POD_VALUES(String, NSString, *, Objects, @"foo", @"bar", @"baz", @"mumble")
+// This block of code is generated, do not edit it directly.
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// To let the testing macros work, add some extra methods to simplify things.
+@interface GPBStringEnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(NSString *)key;
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count;
+@end
+
+static BOOL TestingEnum_IsValidValue(int32_t value) {
+  switch (value) {
+    case 700:
+    case 701:
+    case 702:
+    case 703:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+@implementation GPBStringEnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(NSString *)key {
+  // Cast is needed to compiler knows what class we are invoking initWithValues: on to get the
+  // type correct.
+  return [[(GPBStringEnumDictionary*)[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                                   rawValues:&value
+                                                                     forKeys:&key
+                                                                       count:1] autorelease];
+}
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  return [self initWithValidationFunction:TestingEnum_IsValidValue
+                                rawValues:values
+                                  forKeys:keys
+                                    count:count];
+}
+@end
+
+
+#pragma mark - String -> UInt32
+
+@interface GPBStringUInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringUInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBStringUInt32Dictionary *dict = [[GPBStringUInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringUInt32Dictionary *dict = [GPBStringUInt32Dictionary dictionaryWithValue:100U forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 100U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const uint32_t kValues[] = { 100U, 101U, 102U };
+  GPBStringUInt32Dictionary *dict =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  uint32_t *seenValues = malloc(3 * sizeof(uint32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const uint32_t kValues1[] = { 100U, 101U, 102U };
+  const uint32_t kValues2[] = { 100U, 103U, 102U };
+  const uint32_t kValues3[] = { 100U, 101U, 102U, 103U };
+  GPBStringUInt32Dictionary *dict1 =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringUInt32Dictionary *dict1prime =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringUInt32Dictionary *dict2 =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringUInt32Dictionary *dict3 =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringUInt32Dictionary *dict4 =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBStringUInt32Dictionary *dict =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringUInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringUInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBStringUInt32Dictionary *dict =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringUInt32Dictionary *dict2 =
+      [GPBStringUInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringUInt32Dictionary *dict = [GPBStringUInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:100U forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const uint32_t kValues[] = { 101U, 102U, 103U };
+  GPBStringUInt32Dictionary *dict2 =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 103U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBStringUInt32Dictionary *dict =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBStringUInt32Dictionary *dict =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:103U forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:101U forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const uint32_t kValues2[] = { 102U, 100U };
+  GPBStringUInt32Dictionary *dict2 =
+      [[GPBStringUInt32Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Int32
+
+@interface GPBStringInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBStringInt32Dictionary *dict = [[GPBStringInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringInt32Dictionary *dict = [GPBStringInt32Dictionary dictionaryWithValue:200 forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 200);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const int32_t kValues[] = { 200, 201, 202 };
+  GPBStringInt32Dictionary *dict =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const int32_t kValues1[] = { 200, 201, 202 };
+  const int32_t kValues2[] = { 200, 203, 202 };
+  const int32_t kValues3[] = { 200, 201, 202, 203 };
+  GPBStringInt32Dictionary *dict1 =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringInt32Dictionary *dict1prime =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringInt32Dictionary *dict2 =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringInt32Dictionary *dict3 =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringInt32Dictionary *dict4 =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBStringInt32Dictionary *dict =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBStringInt32Dictionary *dict =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringInt32Dictionary *dict2 =
+      [GPBStringInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringInt32Dictionary *dict = [GPBStringInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:200 forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 201, 202, 203 };
+  GPBStringInt32Dictionary *dict2 =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 203);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBStringInt32Dictionary *dict =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 203);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBStringInt32Dictionary *dict =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:203 forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:201 forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 201);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const int32_t kValues2[] = { 202, 200 };
+  GPBStringInt32Dictionary *dict2 =
+      [[GPBStringInt32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> UInt64
+
+@interface GPBStringUInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringUInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBStringUInt64Dictionary *dict = [[GPBStringUInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringUInt64Dictionary *dict = [GPBStringUInt64Dictionary dictionaryWithValue:300U forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 300U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const uint64_t kValues[] = { 300U, 301U, 302U };
+  GPBStringUInt64Dictionary *dict =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  uint64_t *seenValues = malloc(3 * sizeof(uint64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const uint64_t kValues1[] = { 300U, 301U, 302U };
+  const uint64_t kValues2[] = { 300U, 303U, 302U };
+  const uint64_t kValues3[] = { 300U, 301U, 302U, 303U };
+  GPBStringUInt64Dictionary *dict1 =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringUInt64Dictionary *dict1prime =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringUInt64Dictionary *dict2 =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringUInt64Dictionary *dict3 =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringUInt64Dictionary *dict4 =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBStringUInt64Dictionary *dict =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringUInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringUInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBStringUInt64Dictionary *dict =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringUInt64Dictionary *dict2 =
+      [GPBStringUInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringUInt64Dictionary *dict = [GPBStringUInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:300U forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const uint64_t kValues[] = { 301U, 302U, 303U };
+  GPBStringUInt64Dictionary *dict2 =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 303U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBStringUInt64Dictionary *dict =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBStringUInt64Dictionary *dict =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:303U forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:301U forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const uint64_t kValues2[] = { 302U, 300U };
+  GPBStringUInt64Dictionary *dict2 =
+      [[GPBStringUInt64Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Int64
+
+@interface GPBStringInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBStringInt64Dictionary *dict = [[GPBStringInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringInt64Dictionary *dict = [GPBStringInt64Dictionary dictionaryWithValue:400 forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 400);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const int64_t kValues[] = { 400, 401, 402 };
+  GPBStringInt64Dictionary *dict =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  int64_t *seenValues = malloc(3 * sizeof(int64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const int64_t kValues1[] = { 400, 401, 402 };
+  const int64_t kValues2[] = { 400, 403, 402 };
+  const int64_t kValues3[] = { 400, 401, 402, 403 };
+  GPBStringInt64Dictionary *dict1 =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringInt64Dictionary *dict1prime =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringInt64Dictionary *dict2 =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringInt64Dictionary *dict3 =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringInt64Dictionary *dict4 =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBStringInt64Dictionary *dict =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBStringInt64Dictionary *dict =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringInt64Dictionary *dict2 =
+      [GPBStringInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringInt64Dictionary *dict = [GPBStringInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:400 forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const int64_t kValues[] = { 401, 402, 403 };
+  GPBStringInt64Dictionary *dict2 =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 403);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBStringInt64Dictionary *dict =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 403);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBStringInt64Dictionary *dict =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:403 forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:401 forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 401);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const int64_t kValues2[] = { 402, 400 };
+  GPBStringInt64Dictionary *dict2 =
+      [[GPBStringInt64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Bool
+
+@interface GPBStringBoolDictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringBoolDictionaryTests
+
+- (void)testEmpty {
+  GPBStringBoolDictionary *dict = [[GPBStringBoolDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringBoolDictionary *dict = [GPBStringBoolDictionary dictionaryWithValue:YES forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, YES);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const BOOL kValues[] = { YES, YES, NO };
+  GPBStringBoolDictionary *dict =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  BOOL *seenValues = malloc(3 * sizeof(BOOL));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const BOOL kValues1[] = { YES, YES, NO };
+  const BOOL kValues2[] = { YES, NO, NO };
+  const BOOL kValues3[] = { YES, YES, NO, NO };
+  GPBStringBoolDictionary *dict1 =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringBoolDictionary *dict1prime =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringBoolDictionary *dict2 =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringBoolDictionary *dict3 =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringBoolDictionary *dict4 =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBStringBoolDictionary *dict =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringBoolDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringBoolDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBStringBoolDictionary *dict =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringBoolDictionary *dict2 =
+      [GPBStringBoolDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringBoolDictionary *dict = [GPBStringBoolDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:YES forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const BOOL kValues[] = { YES, NO, NO };
+  GPBStringBoolDictionary *dict2 =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, NO);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBStringBoolDictionary *dict =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, NO);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBStringBoolDictionary *dict =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:NO forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:YES forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, YES);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const BOOL kValues2[] = { NO, YES };
+  GPBStringBoolDictionary *dict2 =
+      [[GPBStringBoolDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Float
+
+@interface GPBStringFloatDictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringFloatDictionaryTests
+
+- (void)testEmpty {
+  GPBStringFloatDictionary *dict = [[GPBStringFloatDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringFloatDictionary *dict = [GPBStringFloatDictionary dictionaryWithValue:500.f forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, float aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 500.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const float kValues[] = { 500.f, 501.f, 502.f };
+  GPBStringFloatDictionary *dict =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  float *seenValues = malloc(3 * sizeof(float));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, float aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const float kValues1[] = { 500.f, 501.f, 502.f };
+  const float kValues2[] = { 500.f, 503.f, 502.f };
+  const float kValues3[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBStringFloatDictionary *dict1 =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringFloatDictionary *dict1prime =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringFloatDictionary *dict2 =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringFloatDictionary *dict3 =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringFloatDictionary *dict4 =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBStringFloatDictionary *dict =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringFloatDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringFloatDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBStringFloatDictionary *dict =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringFloatDictionary *dict2 =
+      [GPBStringFloatDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringFloatDictionary *dict = [GPBStringFloatDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:500.f forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const float kValues[] = { 501.f, 502.f, 503.f };
+  GPBStringFloatDictionary *dict2 =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  float value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 503.f);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBStringFloatDictionary *dict =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBStringFloatDictionary *dict =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  float value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:503.f forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:501.f forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const float kValues2[] = { 502.f, 500.f };
+  GPBStringFloatDictionary *dict2 =
+      [[GPBStringFloatDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Double
+
+@interface GPBStringDoubleDictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringDoubleDictionaryTests
+
+- (void)testEmpty {
+  GPBStringDoubleDictionary *dict = [[GPBStringDoubleDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringDoubleDictionary *dict = [GPBStringDoubleDictionary dictionaryWithValue:600. forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, double aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 600.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const double kValues[] = { 600., 601., 602. };
+  GPBStringDoubleDictionary *dict =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  double *seenValues = malloc(3 * sizeof(double));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, double aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const double kValues1[] = { 600., 601., 602. };
+  const double kValues2[] = { 600., 603., 602. };
+  const double kValues3[] = { 600., 601., 602., 603. };
+  GPBStringDoubleDictionary *dict1 =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringDoubleDictionary *dict1prime =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringDoubleDictionary *dict2 =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringDoubleDictionary *dict3 =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringDoubleDictionary *dict4 =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBStringDoubleDictionary *dict =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringDoubleDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringDoubleDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBStringDoubleDictionary *dict =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringDoubleDictionary *dict2 =
+      [GPBStringDoubleDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringDoubleDictionary *dict = [GPBStringDoubleDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:600. forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const double kValues[] = { 601., 602., 603. };
+  GPBStringDoubleDictionary *dict2 =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  double value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 603.);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBStringDoubleDictionary *dict =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBStringDoubleDictionary *dict =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  double value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:603. forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:601. forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const double kValues2[] = { 602., 600. };
+  GPBStringDoubleDictionary *dict2 =
+      [[GPBStringDoubleDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Enum
+
+@interface GPBStringEnumDictionaryTests : XCTestCase
+@end
+
+@implementation GPBStringEnumDictionaryTests
+
+- (void)testEmpty {
+  GPBStringEnumDictionary *dict = [[GPBStringEnumDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBStringEnumDictionary *dict = [GPBStringEnumDictionary dictionaryWithValue:700 forKey:@"foo"];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqualObjects(aKey, @"foo");
+    XCTAssertEqual(aValue, 700);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const int32_t kValues[] = { 700, 701, 702 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const int32_t kValues1[] = { 700, 701, 702 };
+  const int32_t kValues2[] = { 700, 703, 702 };
+  const int32_t kValues3[] = { 700, 701, 702, 703 };
+  GPBStringEnumDictionary *dict1 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringEnumDictionary *dict1prime =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringEnumDictionary *dict2 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringEnumDictionary *dict3 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringEnumDictionary *dict4 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringEnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringEnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringEnumDictionary *dict2 =
+      [GPBStringEnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBStringEnumDictionary *dict = [GPBStringEnumDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:700 forKey:@"foo"];
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 701, 702, 703 };
+  GPBStringEnumDictionary *dict2 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 703);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 703);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:703 forKey:@"foo"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:701 forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 701);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const int32_t kValues2[] = { 702, 700 };
+  GPBStringEnumDictionary *dict2 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 701);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - String -> Enum (Unknown Enums)
+
+@interface GPBStringEnumDictionaryUnknownEnumTests : XCTestCase
+@end
+
+@implementation GPBStringEnumDictionaryUnknownEnumTests
+
+- (void)testRawBasics {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz" };
+  const int32_t kValues[] = { 700, 801, 702 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue(dict.validationFunc == TestingEnum_IsValidValue);  // Pointer comparison
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"baz" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" rawValue:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:@"mumble" rawValue:NULL]);
+
+  __block NSUInteger idx = 0;
+  NSString **seenKeys = malloc(3 * sizeof(NSString*));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        if (i == 1) {
+          XCTAssertEqual(kGPBUnrecognizedEnumeratorValue, seenValues[j], @"i = %d, j = %d", i, j);
+        } else {
+          XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+        }
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if ([kKeys[i] isEqual:seenKeys[j]]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(NSString *aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEqualityWithUnknowns {
+  const NSString *kKeys1[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const NSString *kKeys2[] = { @"bar", @"foo", @"mumble" };
+  const int32_t kValues1[] = { 700, 801, 702 };  // Unknown
+  const int32_t kValues2[] = { 700, 803, 702 };  // Unknown
+  const int32_t kValues3[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBStringEnumDictionary *dict1 =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBStringEnumDictionary *dict1prime =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBStringEnumDictionary *dict2 =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues2
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBStringEnumDictionary *dict3 =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys2
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBStringEnumDictionary *dict4 =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues3
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopyWithUnknowns {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknown
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringEnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertEqualObjects(dict, dict2);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringEnumDictionary *dict2 =
+      [GPBStringEnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  [dict release];
+}
+
+- (void)testUnknownAdds {
+  GPBStringEnumDictionary *dict =
+    [GPBStringEnumDictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertThrowsSpecificNamed([dict setValue:801 forKey:@"bar"],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 0U);
+  [dict setRawValue:801 forKey:@"bar"];  // Unknown
+  XCTAssertEqual(dict.count, 1U);
+
+  const NSString *kKeys[] = { @"foo", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 702, 803 };  // Unknown
+  GPBStringEnumDictionary *dict2 =
+      [[GPBStringEnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  [dict2 release];
+}
+
+- (void)testUnknownRemove {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:@"bar"];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict removeValueForKey:@"mumble"];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertFalse([dict valueForKey:@"mumble" value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutationUnknowns {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  XCTAssertThrowsSpecificNamed([dict setValue:803 forKey:@"foo"],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:803 forKey:@"foo"];  // Unknown
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:700 forKey:@"mumble"];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"baz" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 700);
+
+  const NSString *kKeys2[] = { @"bar", @"baz" };
+  const int32_t kValues2[] = { 702, 801 };  // Unknown
+  GPBStringEnumDictionary *dict2 =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues2
+                                                          forKeys:kKeys2
+                                                            count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"foo" rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:@"bar" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"bar" value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:@"baz" rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:@"baz" rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:NULL]);
+  XCTAssertTrue([dict valueForKey:@"mumble" value:&value]);
+  XCTAssertEqual(value, 700);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testCopyUnknowns {
+  const NSString *kKeys[] = { @"foo", @"bar", @"baz", @"mumble" };
+  const int32_t kValues[] = { 700, 801, 702, 803 };
+  GPBStringEnumDictionary *dict =
+      [[GPBStringEnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBStringEnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertTrue([dict2 isKindOfClass:[GPBStringEnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND-END TESTS_FOR_POD_VALUES(String, NSString, *, Objects, @"foo", @"bar", @"baz", @"mumble")
+

+ 3650 - 0
objectivec/Tests/GPBDictionaryTests+UInt32.m

@@ -0,0 +1,3650 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+
+// Pull in the macros (using an external file because expanding all tests
+// in a single file makes a file that is failing to work with within Xcode.
+//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
+
+//%PDDM-EXPAND TEST_FOR_POD_KEY(UInt32, uint32_t, 1U, 2U, 3U, 4U)
+// This block of code is generated, do not edit it directly.
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// To let the testing macros work, add some extra methods to simplify things.
+@interface GPBUInt32EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(uint32_t)key;
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count;
+@end
+
+static BOOL TestingEnum_IsValidValue(int32_t value) {
+  switch (value) {
+    case 700:
+    case 701:
+    case 702:
+    case 703:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+@implementation GPBUInt32EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(uint32_t)key {
+  // Cast is needed to compiler knows what class we are invoking initWithValues: on to get the
+  // type correct.
+  return [[(GPBUInt32EnumDictionary*)[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                                   rawValues:&value
+                                                                     forKeys:&key
+                                                                       count:1] autorelease];
+}
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  return [self initWithValidationFunction:TestingEnum_IsValidValue
+                                rawValues:values
+                                  forKeys:keys
+                                    count:count];
+}
+@end
+
+
+#pragma mark - UInt32 -> UInt32
+
+@interface GPBUInt32UInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32UInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32UInt32Dictionary *dict = [[GPBUInt32UInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32UInt32Dictionary *dict = [GPBUInt32UInt32Dictionary dictionaryWithValue:100U forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 100U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const uint32_t kValues[] = { 100U, 101U, 102U };
+  GPBUInt32UInt32Dictionary *dict =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  uint32_t *seenValues = malloc(3 * sizeof(uint32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const uint32_t kValues1[] = { 100U, 101U, 102U };
+  const uint32_t kValues2[] = { 100U, 103U, 102U };
+  const uint32_t kValues3[] = { 100U, 101U, 102U, 103U };
+  GPBUInt32UInt32Dictionary *dict1 =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32UInt32Dictionary *dict1prime =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32UInt32Dictionary *dict2 =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32UInt32Dictionary *dict3 =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32UInt32Dictionary *dict4 =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt32UInt32Dictionary *dict =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32UInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32UInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt32UInt32Dictionary *dict =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32UInt32Dictionary *dict2 =
+      [GPBUInt32UInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32UInt32Dictionary *dict = [GPBUInt32UInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:100U forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const uint32_t kValues[] = { 101U, 102U, 103U };
+  GPBUInt32UInt32Dictionary *dict2 =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 103U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt32UInt32Dictionary *dict =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt32UInt32Dictionary *dict =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:103U forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:101U forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const uint32_t kValues2[] = { 102U, 100U };
+  GPBUInt32UInt32Dictionary *dict2 =
+      [[GPBUInt32UInt32Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Int32
+
+@interface GPBUInt32Int32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32Int32DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32Int32Dictionary *dict = [[GPBUInt32Int32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32Int32Dictionary *dict = [GPBUInt32Int32Dictionary dictionaryWithValue:200 forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 200);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const int32_t kValues[] = { 200, 201, 202 };
+  GPBUInt32Int32Dictionary *dict =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const int32_t kValues1[] = { 200, 201, 202 };
+  const int32_t kValues2[] = { 200, 203, 202 };
+  const int32_t kValues3[] = { 200, 201, 202, 203 };
+  GPBUInt32Int32Dictionary *dict1 =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32Int32Dictionary *dict1prime =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32Int32Dictionary *dict2 =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32Int32Dictionary *dict3 =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32Int32Dictionary *dict4 =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt32Int32Dictionary *dict =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32Int32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32Int32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt32Int32Dictionary *dict =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32Int32Dictionary *dict2 =
+      [GPBUInt32Int32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32Int32Dictionary *dict = [GPBUInt32Int32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:200 forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const int32_t kValues[] = { 201, 202, 203 };
+  GPBUInt32Int32Dictionary *dict2 =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 203);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt32Int32Dictionary *dict =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 203);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt32Int32Dictionary *dict =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:203 forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:201 forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 201);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const int32_t kValues2[] = { 202, 200 };
+  GPBUInt32Int32Dictionary *dict2 =
+      [[GPBUInt32Int32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> UInt64
+
+@interface GPBUInt32UInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32UInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32UInt64Dictionary *dict = [[GPBUInt32UInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32UInt64Dictionary *dict = [GPBUInt32UInt64Dictionary dictionaryWithValue:300U forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 300U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const uint64_t kValues[] = { 300U, 301U, 302U };
+  GPBUInt32UInt64Dictionary *dict =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  uint64_t *seenValues = malloc(3 * sizeof(uint64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const uint64_t kValues1[] = { 300U, 301U, 302U };
+  const uint64_t kValues2[] = { 300U, 303U, 302U };
+  const uint64_t kValues3[] = { 300U, 301U, 302U, 303U };
+  GPBUInt32UInt64Dictionary *dict1 =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32UInt64Dictionary *dict1prime =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32UInt64Dictionary *dict2 =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32UInt64Dictionary *dict3 =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32UInt64Dictionary *dict4 =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt32UInt64Dictionary *dict =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32UInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32UInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt32UInt64Dictionary *dict =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32UInt64Dictionary *dict2 =
+      [GPBUInt32UInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32UInt64Dictionary *dict = [GPBUInt32UInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:300U forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const uint64_t kValues[] = { 301U, 302U, 303U };
+  GPBUInt32UInt64Dictionary *dict2 =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 303U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt32UInt64Dictionary *dict =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt32UInt64Dictionary *dict =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:303U forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:301U forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const uint64_t kValues2[] = { 302U, 300U };
+  GPBUInt32UInt64Dictionary *dict2 =
+      [[GPBUInt32UInt64Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Int64
+
+@interface GPBUInt32Int64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32Int64DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32Int64Dictionary *dict = [[GPBUInt32Int64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32Int64Dictionary *dict = [GPBUInt32Int64Dictionary dictionaryWithValue:400 forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 400);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const int64_t kValues[] = { 400, 401, 402 };
+  GPBUInt32Int64Dictionary *dict =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  int64_t *seenValues = malloc(3 * sizeof(int64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const int64_t kValues1[] = { 400, 401, 402 };
+  const int64_t kValues2[] = { 400, 403, 402 };
+  const int64_t kValues3[] = { 400, 401, 402, 403 };
+  GPBUInt32Int64Dictionary *dict1 =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32Int64Dictionary *dict1prime =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32Int64Dictionary *dict2 =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32Int64Dictionary *dict3 =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32Int64Dictionary *dict4 =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt32Int64Dictionary *dict =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32Int64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32Int64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt32Int64Dictionary *dict =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32Int64Dictionary *dict2 =
+      [GPBUInt32Int64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32Int64Dictionary *dict = [GPBUInt32Int64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:400 forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const int64_t kValues[] = { 401, 402, 403 };
+  GPBUInt32Int64Dictionary *dict2 =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 403);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt32Int64Dictionary *dict =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 403);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt32Int64Dictionary *dict =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:403 forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:401 forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 401);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const int64_t kValues2[] = { 402, 400 };
+  GPBUInt32Int64Dictionary *dict2 =
+      [[GPBUInt32Int64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Bool
+
+@interface GPBUInt32BoolDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32BoolDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32BoolDictionary *dict = [[GPBUInt32BoolDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32BoolDictionary *dict = [GPBUInt32BoolDictionary dictionaryWithValue:YES forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, YES);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const BOOL kValues[] = { YES, YES, NO };
+  GPBUInt32BoolDictionary *dict =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  BOOL *seenValues = malloc(3 * sizeof(BOOL));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const BOOL kValues1[] = { YES, YES, NO };
+  const BOOL kValues2[] = { YES, NO, NO };
+  const BOOL kValues3[] = { YES, YES, NO, NO };
+  GPBUInt32BoolDictionary *dict1 =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32BoolDictionary *dict1prime =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32BoolDictionary *dict2 =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32BoolDictionary *dict3 =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32BoolDictionary *dict4 =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt32BoolDictionary *dict =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32BoolDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32BoolDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt32BoolDictionary *dict =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32BoolDictionary *dict2 =
+      [GPBUInt32BoolDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32BoolDictionary *dict = [GPBUInt32BoolDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:YES forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const BOOL kValues[] = { YES, NO, NO };
+  GPBUInt32BoolDictionary *dict2 =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, NO);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt32BoolDictionary *dict =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, NO);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt32BoolDictionary *dict =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:NO forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:YES forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, YES);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const BOOL kValues2[] = { NO, YES };
+  GPBUInt32BoolDictionary *dict2 =
+      [[GPBUInt32BoolDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Float
+
+@interface GPBUInt32FloatDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32FloatDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32FloatDictionary *dict = [[GPBUInt32FloatDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32FloatDictionary *dict = [GPBUInt32FloatDictionary dictionaryWithValue:500.f forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, float aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 500.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const float kValues[] = { 500.f, 501.f, 502.f };
+  GPBUInt32FloatDictionary *dict =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  float *seenValues = malloc(3 * sizeof(float));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, float aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const float kValues1[] = { 500.f, 501.f, 502.f };
+  const float kValues2[] = { 500.f, 503.f, 502.f };
+  const float kValues3[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt32FloatDictionary *dict1 =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32FloatDictionary *dict1prime =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32FloatDictionary *dict2 =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32FloatDictionary *dict3 =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32FloatDictionary *dict4 =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt32FloatDictionary *dict =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32FloatDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32FloatDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt32FloatDictionary *dict =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32FloatDictionary *dict2 =
+      [GPBUInt32FloatDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32FloatDictionary *dict = [GPBUInt32FloatDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:500.f forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const float kValues[] = { 501.f, 502.f, 503.f };
+  GPBUInt32FloatDictionary *dict2 =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  float value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 503.f);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt32FloatDictionary *dict =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt32FloatDictionary *dict =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  float value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:503.f forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:501.f forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const float kValues2[] = { 502.f, 500.f };
+  GPBUInt32FloatDictionary *dict2 =
+      [[GPBUInt32FloatDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Double
+
+@interface GPBUInt32DoubleDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32DoubleDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32DoubleDictionary *dict = [[GPBUInt32DoubleDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32DoubleDictionary *dict = [GPBUInt32DoubleDictionary dictionaryWithValue:600. forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, double aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 600.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const double kValues[] = { 600., 601., 602. };
+  GPBUInt32DoubleDictionary *dict =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  double *seenValues = malloc(3 * sizeof(double));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, double aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const double kValues1[] = { 600., 601., 602. };
+  const double kValues2[] = { 600., 603., 602. };
+  const double kValues3[] = { 600., 601., 602., 603. };
+  GPBUInt32DoubleDictionary *dict1 =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32DoubleDictionary *dict1prime =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32DoubleDictionary *dict2 =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32DoubleDictionary *dict3 =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32DoubleDictionary *dict4 =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt32DoubleDictionary *dict =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32DoubleDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32DoubleDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt32DoubleDictionary *dict =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32DoubleDictionary *dict2 =
+      [GPBUInt32DoubleDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32DoubleDictionary *dict = [GPBUInt32DoubleDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:600. forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const double kValues[] = { 601., 602., 603. };
+  GPBUInt32DoubleDictionary *dict2 =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  double value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 603.);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt32DoubleDictionary *dict =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt32DoubleDictionary *dict =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  double value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:603. forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:601. forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const double kValues2[] = { 602., 600. };
+  GPBUInt32DoubleDictionary *dict2 =
+      [[GPBUInt32DoubleDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Enum
+
+@interface GPBUInt32EnumDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32EnumDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32EnumDictionary *dict = [[GPBUInt32EnumDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32EnumDictionary *dict = [GPBUInt32EnumDictionary dictionaryWithValue:700 forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqual(aValue, 700);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const int32_t kValues[] = { 700, 701, 702 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const int32_t kValues1[] = { 700, 701, 702 };
+  const int32_t kValues2[] = { 700, 703, 702 };
+  const int32_t kValues3[] = { 700, 701, 702, 703 };
+  GPBUInt32EnumDictionary *dict1 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32EnumDictionary *dict1prime =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32EnumDictionary *dict2 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32EnumDictionary *dict3 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32EnumDictionary *dict4 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32EnumDictionary *dict2 =
+      [GPBUInt32EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32EnumDictionary *dict = [GPBUInt32EnumDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:700 forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const int32_t kValues[] = { 701, 702, 703 };
+  GPBUInt32EnumDictionary *dict2 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 703);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 703);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:703 forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:701 forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 701);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const int32_t kValues2[] = { 702, 700 };
+  GPBUInt32EnumDictionary *dict2 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 701);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Enum (Unknown Enums)
+
+@interface GPBUInt32EnumDictionaryUnknownEnumTests : XCTestCase
+@end
+
+@implementation GPBUInt32EnumDictionaryUnknownEnumTests
+
+- (void)testRawBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const int32_t kValues[] = { 700, 801, 702 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue(dict.validationFunc == TestingEnum_IsValidValue);  // Pointer comparison
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:1U rawValue:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:2U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:2U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:3U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:3U rawValue:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:4U rawValue:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        if (i == 1) {
+          XCTAssertEqual(kGPBUnrecognizedEnumeratorValue, seenValues[j], @"i = %d, j = %d", i, j);
+        } else {
+          XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+        }
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(uint32_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEqualityWithUnknowns {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const int32_t kValues1[] = { 700, 801, 702 };  // Unknown
+  const int32_t kValues2[] = { 700, 803, 702 };  // Unknown
+  const int32_t kValues3[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt32EnumDictionary *dict1 =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32EnumDictionary *dict1prime =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32EnumDictionary *dict2 =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues2
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32EnumDictionary *dict3 =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys2
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32EnumDictionary *dict4 =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues3
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopyWithUnknowns {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknown
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertEqualObjects(dict, dict2);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32EnumDictionary *dict2 =
+      [GPBUInt32EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  [dict release];
+}
+
+- (void)testUnknownAdds {
+  GPBUInt32EnumDictionary *dict =
+    [GPBUInt32EnumDictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertThrowsSpecificNamed([dict setValue:801 forKey:2U],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 0U);
+  [dict setRawValue:801 forKey:2U];  // Unknown
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 1U, 3U, 4U };
+  const int32_t kValues[] = { 700, 702, 803 };  // Unknown
+  GPBUInt32EnumDictionary *dict2 =
+      [[GPBUInt32EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:2U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:2U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:4U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:4U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  [dict2 release];
+}
+
+- (void)testUnknownRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:4U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:4U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:1U value:NULL]);
+  XCTAssertFalse([dict valueForKey:2U value:NULL]);
+  XCTAssertFalse([dict valueForKey:3U value:NULL]);
+  XCTAssertFalse([dict valueForKey:4U value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutationUnknowns {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:2U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:4U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  XCTAssertThrowsSpecificNamed([dict setValue:803 forKey:1U],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U value:NULL]);
+  XCTAssertTrue([dict valueForKey:1U value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:2U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:2U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:4U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:803 forKey:1U];  // Unknown
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:1U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:2U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:2U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:4U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:700 forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:1U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:2U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:2U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:3U value:NULL]);
+  XCTAssertTrue([dict valueForKey:3U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 700);
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const int32_t kValues2[] = { 702, 801 };  // Unknown
+  GPBUInt32EnumDictionary *dict2 =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues2
+                                                          forKeys:kKeys2
+                                                            count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:1U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:1U rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:2U value:NULL]);
+  XCTAssertTrue([dict valueForKey:2U value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:3U rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:3U rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:4U value:NULL]);
+  XCTAssertTrue([dict valueForKey:4U value:&value]);
+  XCTAssertEqual(value, 700);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testCopyUnknowns {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const int32_t kValues[] = { 700, 801, 702, 803 };
+  GPBUInt32EnumDictionary *dict =
+      [[GPBUInt32EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt32 -> Object
+
+@interface GPBUInt32ObjectDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt32ObjectDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt32ObjectDictionary *dict = [[GPBUInt32ObjectDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:1U]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt32ObjectDictionary *dict = [GPBUInt32ObjectDictionary dictionaryWithValue:@"abc" forKey:1U];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertNil([dict valueForKey:2U]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, id aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 1U);
+    XCTAssertEqualObjects(aValue, @"abc");
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint32_t kKeys[] = { 1U, 2U, 3U };
+  const id kValues[] = { @"abc", @"def", @"ghi" };
+  GPBUInt32ObjectDictionary *dict =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:2U], @"def");
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertNil([dict valueForKey:4U]);
+
+  __block NSUInteger idx = 0;
+  uint32_t *seenKeys = malloc(3 * sizeof(uint32_t));
+  id *seenValues = malloc(3 * sizeof(id));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, id aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqualObjects(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint32_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint32_t kKeys1[] = { 1U, 2U, 3U, 4U };
+  const uint32_t kKeys2[] = { 2U, 1U, 4U };
+  const id kValues1[] = { @"abc", @"def", @"ghi" };
+  const id kValues2[] = { @"abc", @"jkl", @"ghi" };
+  const id kValues3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary *dict1 =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt32ObjectDictionary *dict1prime =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt32ObjectDictionary *dict2 =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt32ObjectDictionary *dict3 =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt32ObjectDictionary *dict4 =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary *dict =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32ObjectDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt32ObjectDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary *dict =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt32ObjectDictionary *dict2 =
+      [GPBUInt32ObjectDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt32ObjectDictionary *dict = [GPBUInt32ObjectDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:@"abc" forKey:1U];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint32_t kKeys[] = { 2U, 3U, 4U };
+  const id kValues[] = { @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary *dict2 =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:2U], @"def");
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"jkl");
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary *dict =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertNil([dict valueForKey:2U]);
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"jkl");
+
+  // Remove again does nothing.
+  [dict removeValueForKey:2U];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertNil([dict valueForKey:2U]);
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"jkl");
+
+  [dict removeValueForKey:4U];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertNil([dict valueForKey:2U]);
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertNil([dict valueForKey:4U]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:1U]);
+  XCTAssertNil([dict valueForKey:2U]);
+  XCTAssertNil([dict valueForKey:3U]);
+  XCTAssertNil([dict valueForKey:4U]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint32_t kKeys[] = { 1U, 2U, 3U, 4U };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt32ObjectDictionary *dict =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:2U], @"def");
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"jkl");
+
+  [dict setValue:@"jkl" forKey:1U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:2U], @"def");
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"jkl");
+
+  [dict setValue:@"def" forKey:4U];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:2U], @"def");
+  XCTAssertEqualObjects([dict valueForKey:3U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"def");
+
+  const uint32_t kKeys2[] = { 2U, 3U };
+  const id kValues2[] = { @"ghi", @"abc" };
+  GPBUInt32ObjectDictionary *dict2 =
+      [[GPBUInt32ObjectDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:1U], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:2U], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:3U], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:4U], @"def");
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND-END TEST_FOR_POD_KEY(UInt32, uint32_t, 1U, 2U, 3U, 4U)
+

+ 3649 - 0
objectivec/Tests/GPBDictionaryTests+UInt64.m

@@ -0,0 +1,3649 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+
+#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
+
+// Pull in the macros (using an external file because expanding all tests
+// in a single file makes a file that is failing to work with within Xcode.
+//%PDDM-IMPORT-DEFINES GPBDictionaryTests.pddm
+
+//%PDDM-EXPAND TEST_FOR_POD_KEY(UInt64, uint64_t, 31ULL, 32ULL, 33ULL, 34ULL)
+// This block of code is generated, do not edit it directly.
+
+#ifndef GPBARRAYSIZE
+#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+#endif  // GPBARRAYSIZE
+
+// To let the testing macros work, add some extra methods to simplify things.
+@interface GPBUInt64EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(uint64_t)key;
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count;
+@end
+
+static BOOL TestingEnum_IsValidValue(int32_t value) {
+  switch (value) {
+    case 700:
+    case 701:
+    case 702:
+    case 703:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+@implementation GPBUInt64EnumDictionary (TestingTweak)
++ (instancetype)dictionaryWithValue:(int32_t)value forKey:(uint64_t)key {
+  // Cast is needed to compiler knows what class we are invoking initWithValues: on to get the
+  // type correct.
+  return [[(GPBUInt64EnumDictionary*)[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                                   rawValues:&value
+                                                                     forKeys:&key
+                                                                       count:1] autorelease];
+}
+- (instancetype)initWithValues:(const int32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  return [self initWithValidationFunction:TestingEnum_IsValidValue
+                                rawValues:values
+                                  forKeys:keys
+                                    count:count];
+}
+@end
+
+
+#pragma mark - UInt64 -> UInt32
+
+@interface GPBUInt64UInt32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64UInt32DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64UInt32Dictionary *dict = [[GPBUInt64UInt32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64UInt32Dictionary *dict = [GPBUInt64UInt32Dictionary dictionaryWithValue:100U forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 100U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const uint32_t kValues[] = { 100U, 101U, 102U };
+  GPBUInt64UInt32Dictionary *dict =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  uint32_t *seenValues = malloc(3 * sizeof(uint32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const uint32_t kValues1[] = { 100U, 101U, 102U };
+  const uint32_t kValues2[] = { 100U, 103U, 102U };
+  const uint32_t kValues3[] = { 100U, 101U, 102U, 103U };
+  GPBUInt64UInt32Dictionary *dict1 =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64UInt32Dictionary *dict1prime =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64UInt32Dictionary *dict2 =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64UInt32Dictionary *dict3 =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64UInt32Dictionary *dict4 =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt64UInt32Dictionary *dict =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64UInt32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64UInt32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt64UInt32Dictionary *dict =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64UInt32Dictionary *dict2 =
+      [GPBUInt64UInt32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64UInt32Dictionary *dict = [GPBUInt64UInt32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:100U forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const uint32_t kValues[] = { 101U, 102U, 103U };
+  GPBUInt64UInt32Dictionary *dict2 =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt64UInt32Dictionary *dict =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint32_t kValues[] = { 100U, 101U, 102U, 103U };
+  GPBUInt64UInt32Dictionary *dict =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:103U forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+
+  [dict setValue:101U forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const uint32_t kValues2[] = { 102U, 100U };
+  GPBUInt64UInt32Dictionary *dict2 =
+      [[GPBUInt64UInt32Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 103U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 102U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 100U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 101U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Int32
+
+@interface GPBUInt64Int32DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64Int32DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64Int32Dictionary *dict = [[GPBUInt64Int32Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64Int32Dictionary *dict = [GPBUInt64Int32Dictionary dictionaryWithValue:200 forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 200);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const int32_t kValues[] = { 200, 201, 202 };
+  GPBUInt64Int32Dictionary *dict =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const int32_t kValues1[] = { 200, 201, 202 };
+  const int32_t kValues2[] = { 200, 203, 202 };
+  const int32_t kValues3[] = { 200, 201, 202, 203 };
+  GPBUInt64Int32Dictionary *dict1 =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64Int32Dictionary *dict1prime =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64Int32Dictionary *dict2 =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64Int32Dictionary *dict3 =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64Int32Dictionary *dict4 =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt64Int32Dictionary *dict =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64Int32Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64Int32Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt64Int32Dictionary *dict =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64Int32Dictionary *dict2 =
+      [GPBUInt64Int32Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64Int32Dictionary *dict = [GPBUInt64Int32Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:200 forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 201, 202, 203 };
+  GPBUInt64Int32Dictionary *dict2 =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 203);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt64Int32Dictionary *dict =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 200, 201, 202, 203 };
+  GPBUInt64Int32Dictionary *dict =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:203 forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 203);
+
+  [dict setValue:201 forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 201);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 201);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const int32_t kValues2[] = { 202, 200 };
+  GPBUInt64Int32Dictionary *dict2 =
+      [[GPBUInt64Int32Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 203);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 202);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 200);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 201);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> UInt64
+
+@interface GPBUInt64UInt64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64UInt64DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64UInt64Dictionary *dict = [[GPBUInt64UInt64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64UInt64Dictionary *dict = [GPBUInt64UInt64Dictionary dictionaryWithValue:300U forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 300U);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const uint64_t kValues[] = { 300U, 301U, 302U };
+  GPBUInt64UInt64Dictionary *dict =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  uint64_t *seenValues = malloc(3 * sizeof(uint64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, uint64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const uint64_t kValues1[] = { 300U, 301U, 302U };
+  const uint64_t kValues2[] = { 300U, 303U, 302U };
+  const uint64_t kValues3[] = { 300U, 301U, 302U, 303U };
+  GPBUInt64UInt64Dictionary *dict1 =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64UInt64Dictionary *dict1prime =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64UInt64Dictionary *dict2 =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64UInt64Dictionary *dict3 =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64UInt64Dictionary *dict4 =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt64UInt64Dictionary *dict =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64UInt64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64UInt64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt64UInt64Dictionary *dict =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64UInt64Dictionary *dict2 =
+      [GPBUInt64UInt64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64UInt64Dictionary *dict = [GPBUInt64UInt64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:300U forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const uint64_t kValues[] = { 301U, 302U, 303U };
+  GPBUInt64UInt64Dictionary *dict2 =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt64UInt64Dictionary *dict =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kValues[] = { 300U, 301U, 302U, 303U };
+  GPBUInt64UInt64Dictionary *dict =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  uint64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:303U forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+
+  [dict setValue:301U forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const uint64_t kValues2[] = { 302U, 300U };
+  GPBUInt64UInt64Dictionary *dict2 =
+      [[GPBUInt64UInt64Dictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 303U);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 302U);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 300U);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 301U);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Int64
+
+@interface GPBUInt64Int64DictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64Int64DictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64Int64Dictionary *dict = [[GPBUInt64Int64Dictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64Int64Dictionary *dict = [GPBUInt64Int64Dictionary dictionaryWithValue:400 forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 400);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const int64_t kValues[] = { 400, 401, 402 };
+  GPBUInt64Int64Dictionary *dict =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  int64_t *seenValues = malloc(3 * sizeof(int64_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int64_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int64_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const int64_t kValues1[] = { 400, 401, 402 };
+  const int64_t kValues2[] = { 400, 403, 402 };
+  const int64_t kValues3[] = { 400, 401, 402, 403 };
+  GPBUInt64Int64Dictionary *dict1 =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64Int64Dictionary *dict1prime =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64Int64Dictionary *dict2 =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64Int64Dictionary *dict3 =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64Int64Dictionary *dict4 =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt64Int64Dictionary *dict =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64Int64Dictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64Int64Dictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt64Int64Dictionary *dict =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64Int64Dictionary *dict2 =
+      [GPBUInt64Int64Dictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64Int64Dictionary *dict = [GPBUInt64Int64Dictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:400 forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const int64_t kValues[] = { 401, 402, 403 };
+  GPBUInt64Int64Dictionary *dict2 =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 403);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt64Int64Dictionary *dict =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int64_t kValues[] = { 400, 401, 402, 403 };
+  GPBUInt64Int64Dictionary *dict =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int64_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:403 forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 403);
+
+  [dict setValue:401 forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 401);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 401);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const int64_t kValues2[] = { 402, 400 };
+  GPBUInt64Int64Dictionary *dict2 =
+      [[GPBUInt64Int64Dictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 403);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 402);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 400);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 401);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Bool
+
+@interface GPBUInt64BoolDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64BoolDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64BoolDictionary *dict = [[GPBUInt64BoolDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64BoolDictionary *dict = [GPBUInt64BoolDictionary dictionaryWithValue:YES forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, YES);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const BOOL kValues[] = { YES, YES, NO };
+  GPBUInt64BoolDictionary *dict =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  BOOL *seenValues = malloc(3 * sizeof(BOOL));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, BOOL aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, BOOL aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const BOOL kValues1[] = { YES, YES, NO };
+  const BOOL kValues2[] = { YES, NO, NO };
+  const BOOL kValues3[] = { YES, YES, NO, NO };
+  GPBUInt64BoolDictionary *dict1 =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64BoolDictionary *dict1prime =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64BoolDictionary *dict2 =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64BoolDictionary *dict3 =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64BoolDictionary *dict4 =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt64BoolDictionary *dict =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64BoolDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64BoolDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt64BoolDictionary *dict =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64BoolDictionary *dict2 =
+      [GPBUInt64BoolDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64BoolDictionary *dict = [GPBUInt64BoolDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:YES forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const BOOL kValues[] = { YES, NO, NO };
+  GPBUInt64BoolDictionary *dict2 =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt64BoolDictionary *dict =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const BOOL kValues[] = { YES, YES, NO, NO };
+  GPBUInt64BoolDictionary *dict =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  BOOL value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:NO forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, NO);
+
+  [dict setValue:YES forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, YES);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const BOOL kValues2[] = { NO, YES };
+  GPBUInt64BoolDictionary *dict2 =
+      [[GPBUInt64BoolDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, NO);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, YES);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, YES);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Float
+
+@interface GPBUInt64FloatDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64FloatDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64FloatDictionary *dict = [[GPBUInt64FloatDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64FloatDictionary *dict = [GPBUInt64FloatDictionary dictionaryWithValue:500.f forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  float value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, float aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 500.f);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const float kValues[] = { 500.f, 501.f, 502.f };
+  GPBUInt64FloatDictionary *dict =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  float *seenValues = malloc(3 * sizeof(float));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, float aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, float aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const float kValues1[] = { 500.f, 501.f, 502.f };
+  const float kValues2[] = { 500.f, 503.f, 502.f };
+  const float kValues3[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt64FloatDictionary *dict1 =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64FloatDictionary *dict1prime =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64FloatDictionary *dict2 =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64FloatDictionary *dict3 =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues1
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64FloatDictionary *dict4 =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues3
+                                               forKeys:kKeys1
+                                                 count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt64FloatDictionary *dict =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64FloatDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64FloatDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt64FloatDictionary *dict =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64FloatDictionary *dict2 =
+      [GPBUInt64FloatDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64FloatDictionary *dict = [GPBUInt64FloatDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:500.f forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const float kValues[] = { 501.f, 502.f, 503.f };
+  GPBUInt64FloatDictionary *dict2 =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues
+                                               forKeys:kKeys
+                                                 count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  float value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt64FloatDictionary *dict =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  float value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const float kValues[] = { 500.f, 501.f, 502.f, 503.f };
+  GPBUInt64FloatDictionary *dict =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues
+                                        forKeys:kKeys
+                                          count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  float value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:503.f forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+
+  [dict setValue:501.f forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const float kValues2[] = { 502.f, 500.f };
+  GPBUInt64FloatDictionary *dict2 =
+      [[GPBUInt64FloatDictionary alloc] initWithValues:kValues2
+                                               forKeys:kKeys2
+                                                 count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 503.f);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 502.f);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 500.f);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 501.f);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Double
+
+@interface GPBUInt64DoubleDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64DoubleDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64DoubleDictionary *dict = [[GPBUInt64DoubleDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64DoubleDictionary *dict = [GPBUInt64DoubleDictionary dictionaryWithValue:600. forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  double value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, double aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 600.);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const double kValues[] = { 600., 601., 602. };
+  GPBUInt64DoubleDictionary *dict =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  double *seenValues = malloc(3 * sizeof(double));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, double aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, double aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const double kValues1[] = { 600., 601., 602. };
+  const double kValues2[] = { 600., 603., 602. };
+  const double kValues3[] = { 600., 601., 602., 603. };
+  GPBUInt64DoubleDictionary *dict1 =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64DoubleDictionary *dict1prime =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64DoubleDictionary *dict2 =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64DoubleDictionary *dict3 =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64DoubleDictionary *dict4 =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt64DoubleDictionary *dict =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64DoubleDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64DoubleDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt64DoubleDictionary *dict =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64DoubleDictionary *dict2 =
+      [GPBUInt64DoubleDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64DoubleDictionary *dict = [GPBUInt64DoubleDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:600. forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const double kValues[] = { 601., 602., 603. };
+  GPBUInt64DoubleDictionary *dict2 =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  double value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt64DoubleDictionary *dict =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  double value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const double kValues[] = { 600., 601., 602., 603. };
+  GPBUInt64DoubleDictionary *dict =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  double value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:603. forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+
+  [dict setValue:601. forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const double kValues2[] = { 602., 600. };
+  GPBUInt64DoubleDictionary *dict2 =
+      [[GPBUInt64DoubleDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 603.);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 602.);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 600.);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 601.);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Enum
+
+@interface GPBUInt64EnumDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64EnumDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64EnumDictionary *dict = [[GPBUInt64EnumDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64EnumDictionary *dict = [GPBUInt64EnumDictionary dictionaryWithValue:700 forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqual(aValue, 700);
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const int32_t kValues[] = { 700, 701, 702 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const int32_t kValues1[] = { 700, 701, 702 };
+  const int32_t kValues2[] = { 700, 703, 702 };
+  const int32_t kValues3[] = { 700, 701, 702, 703 };
+  GPBUInt64EnumDictionary *dict1 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64EnumDictionary *dict1prime =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64EnumDictionary *dict2 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64EnumDictionary *dict3 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues1
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64EnumDictionary *dict4 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues3
+                                              forKeys:kKeys1
+                                                count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64EnumDictionary *dict2 =
+      [GPBUInt64EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64EnumDictionary *dict = [GPBUInt64EnumDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:700 forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 701, 702, 703 };
+  GPBUInt64EnumDictionary *dict2 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 703);
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 701, 702, 703 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                       forKeys:kKeys
+                                         count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:703 forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 703);
+
+  [dict setValue:701 forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 701);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 701);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const int32_t kValues2[] = { 702, 700 };
+  GPBUInt64EnumDictionary *dict2 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues2
+                                              forKeys:kKeys2
+                                                count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 703);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 701);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Enum (Unknown Enums)
+
+@interface GPBUInt64EnumDictionaryUnknownEnumTests : XCTestCase
+@end
+
+@implementation GPBUInt64EnumDictionaryUnknownEnumTests
+
+- (void)testRawBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const int32_t kValues[] = { 700, 801, 702 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue(dict.validationFunc == TestingEnum_IsValidValue);  // Pointer comparison
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:33ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL rawValue:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:34ULL rawValue:NULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  int32_t *seenValues = malloc(3 * sizeof(int32_t));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        if (i == 1) {
+          XCTAssertEqual(kGPBUnrecognizedEnumeratorValue, seenValues[j], @"i = %d, j = %d", i, j);
+        } else {
+          XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+        }
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqual(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndRawValuesUsingBlock:^(uint64_t aKey, int32_t aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEqualityWithUnknowns {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const int32_t kValues1[] = { 700, 801, 702 };  // Unknown
+  const int32_t kValues2[] = { 700, 803, 702 };  // Unknown
+  const int32_t kValues3[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt64EnumDictionary *dict1 =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64EnumDictionary *dict1prime =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64EnumDictionary *dict2 =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues2
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64EnumDictionary *dict3 =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues1
+                                                          forKeys:kKeys2
+                                                            count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64EnumDictionary *dict4 =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues3
+                                                          forKeys:kKeys1
+                                                            count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopyWithUnknowns {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknown
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertEqualObjects(dict, dict2);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64EnumDictionary *dict2 =
+      [GPBUInt64EnumDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  [dict release];
+}
+
+- (void)testUnknownAdds {
+  GPBUInt64EnumDictionary *dict =
+    [GPBUInt64EnumDictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertThrowsSpecificNamed([dict setValue:801 forKey:32ULL],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 0U);
+  [dict setRawValue:801 forKey:32ULL];  // Unknown
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 31ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 702, 803 };  // Unknown
+  GPBUInt64EnumDictionary *dict2 =
+      [[GPBUInt64EnumDictionary alloc] initWithValues:kValues
+                                              forKeys:kKeys
+                                                count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, kGPBUnrecognizedEnumeratorValue);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  [dict2 release];
+}
+
+- (void)testUnknownRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertFalse([dict valueForKey:31ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:32ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:33ULL value:NULL]);
+  XCTAssertFalse([dict valueForKey:34ULL value:NULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutationUnknowns {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };  // Unknowns
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  int32_t value;
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  XCTAssertThrowsSpecificNamed([dict setValue:803 forKey:31ULL],  // Unknown
+                               NSException, NSInvalidArgumentException);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL value:&value]);
+  XCTAssertEqual(value, 700);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:803 forKey:31ULL];  // Unknown
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+
+  [dict setRawValue:700 forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:33ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 700);
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const int32_t kValues2[] = { 702, 801 };  // Unknown
+  GPBUInt64EnumDictionary *dict2 =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues2
+                                                          forKeys:kKeys2
+                                                            count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addRawEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:31ULL rawValue:&value]);
+  XCTAssertEqual(value, 803);
+  XCTAssertTrue([dict valueForKey:32ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:32ULL value:&value]);
+  XCTAssertEqual(value, 702);
+  XCTAssertTrue([dict valueForKey:33ULL rawValue:NULL]);
+  XCTAssertTrue([dict valueForKey:33ULL rawValue:&value]);
+  XCTAssertEqual(value, 801);
+  XCTAssertTrue([dict valueForKey:34ULL value:NULL]);
+  XCTAssertTrue([dict valueForKey:34ULL value:&value]);
+  XCTAssertEqual(value, 700);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testCopyUnknowns {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const int32_t kValues[] = { 700, 801, 702, 803 };
+  GPBUInt64EnumDictionary *dict =
+      [[GPBUInt64EnumDictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+                                                        rawValues:kValues
+                                                          forKeys:kKeys
+                                                            count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64EnumDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64EnumDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+#pragma mark - UInt64 -> Object
+
+@interface GPBUInt64ObjectDictionaryTests : XCTestCase
+@end
+
+@implementation GPBUInt64ObjectDictionaryTests
+
+- (void)testEmpty {
+  GPBUInt64ObjectDictionary *dict = [[GPBUInt64ObjectDictionary alloc] init];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:31ULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue, stop)
+    XCTFail(@"Shouldn't get here!");
+  }];
+  [dict release];
+}
+
+- (void)testOne {
+  GPBUInt64ObjectDictionary *dict = [GPBUInt64ObjectDictionary dictionaryWithValue:@"abc" forKey:31ULL];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 1U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertNil([dict valueForKey:32ULL]);
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, id aValue, BOOL *stop) {
+    XCTAssertEqual(aKey, 31ULL);
+    XCTAssertEqualObjects(aValue, @"abc");
+    XCTAssertNotEqual(stop, NULL);
+  }];
+}
+
+- (void)testBasics {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL };
+  const id kValues[] = { @"abc", @"def", @"ghi" };
+  GPBUInt64ObjectDictionary *dict =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:32ULL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertNil([dict valueForKey:34ULL]);
+
+  __block NSUInteger idx = 0;
+  uint64_t *seenKeys = malloc(3 * sizeof(uint64_t));
+  id *seenValues = malloc(3 * sizeof(id));
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, id aValue, BOOL *stop) {
+    XCTAssertLessThan(idx, 3U);
+    seenKeys[idx] = aKey;
+    seenValues[idx] = aValue;
+    XCTAssertNotEqual(stop, NULL);
+    ++idx;
+  }];
+  for (int i = 0; i < 3; ++i) {
+    BOOL foundKey = NO;
+    for (int j = 0; (j < 3) && !foundKey; ++j) {
+      if (kKeys[i] == seenKeys[j]) {
+        foundKey = YES;
+        XCTAssertEqualObjects(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+      }
+    }
+    XCTAssertTrue(foundKey, @"i = %d", i);
+  }
+  free(seenKeys);
+  free(seenValues);
+
+  // Stopping the enumeration.
+  idx = 0;
+  [dict enumerateKeysAndValuesUsingBlock:^(uint64_t aKey, id aValue, BOOL *stop) {
+    #pragma unused(aKey, aValue)
+    if (idx == 1) *stop = YES;
+    XCTAssertNotEqual(idx, 2U);
+    ++idx;
+  }];
+  [dict release];
+}
+
+- (void)testEquality {
+  const uint64_t kKeys1[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const uint64_t kKeys2[] = { 32ULL, 31ULL, 34ULL };
+  const id kValues1[] = { @"abc", @"def", @"ghi" };
+  const id kValues2[] = { @"abc", @"jkl", @"ghi" };
+  const id kValues3[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary *dict1 =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1);
+  GPBUInt64ObjectDictionary *dict1prime =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict1prime);
+  GPBUInt64ObjectDictionary *dict2 =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  GPBUInt64ObjectDictionary *dict3 =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues1
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues1)];
+  XCTAssertNotNil(dict3);
+  GPBUInt64ObjectDictionary *dict4 =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues3
+                                                forKeys:kKeys1
+                                                  count:GPBARRAYSIZE(kValues3)];
+  XCTAssertNotNil(dict4);
+
+  // 1/1Prime should be different objects, but equal.
+  XCTAssertNotEqual(dict1, dict1prime);
+  XCTAssertEqualObjects(dict1, dict1prime);
+  // Equal, so they must have same hash.
+  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+
+  // 2 is save keys, different values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict2);
+
+  // 3 is different keys, samae values; not equal.
+  XCTAssertNotEqualObjects(dict1, dict3);
+
+  // 4 extra pair; not equal
+  XCTAssertNotEqualObjects(dict1, dict4);
+
+  [dict1 release];
+  [dict1prime release];
+  [dict2 release];
+  [dict3 release];
+  [dict4 release];
+}
+
+- (void)testCopy {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary *dict =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64ObjectDictionary *dict2 = [dict copy];
+  XCTAssertNotNil(dict2);
+
+  // Should be new object but equal.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  XCTAssertTrue([dict2 isKindOfClass:[GPBUInt64ObjectDictionary class]]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testDictionaryFromDictionary {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary *dict =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+
+  GPBUInt64ObjectDictionary *dict2 =
+      [GPBUInt64ObjectDictionary dictionaryWithDictionary:dict];
+  XCTAssertNotNil(dict2);
+
+  // Should be new pointer, but equal objects.
+  XCTAssertNotEqual(dict, dict2);
+  XCTAssertEqualObjects(dict, dict2);
+  [dict release];
+}
+
+- (void)testAdds {
+  GPBUInt64ObjectDictionary *dict = [GPBUInt64ObjectDictionary dictionary];
+  XCTAssertNotNil(dict);
+
+  XCTAssertEqual(dict.count, 0U);
+  [dict setValue:@"abc" forKey:31ULL];
+  XCTAssertEqual(dict.count, 1U);
+
+  const uint64_t kKeys[] = { 32ULL, 33ULL, 34ULL };
+  const id kValues[] = { @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary *dict2 =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues
+                                                forKeys:kKeys
+                                                  count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:32ULL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"jkl");
+  [dict2 release];
+}
+
+- (void)testRemove {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary *dict =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertNil([dict valueForKey:32ULL]);
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"jkl");
+
+  // Remove again does nothing.
+  [dict removeValueForKey:32ULL];
+  XCTAssertEqual(dict.count, 3U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertNil([dict valueForKey:32ULL]);
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"jkl");
+
+  [dict removeValueForKey:34ULL];
+  XCTAssertEqual(dict.count, 2U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertNil([dict valueForKey:32ULL]);
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertNil([dict valueForKey:34ULL]);
+
+  [dict removeAll];
+  XCTAssertEqual(dict.count, 0U);
+  XCTAssertNil([dict valueForKey:31ULL]);
+  XCTAssertNil([dict valueForKey:32ULL]);
+  XCTAssertNil([dict valueForKey:33ULL]);
+  XCTAssertNil([dict valueForKey:34ULL]);
+  [dict release];
+}
+
+- (void)testInplaceMutation {
+  const uint64_t kKeys[] = { 31ULL, 32ULL, 33ULL, 34ULL };
+  const id kValues[] = { @"abc", @"def", @"ghi", @"jkl" };
+  GPBUInt64ObjectDictionary *dict =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues
+                                         forKeys:kKeys
+                                           count:GPBARRAYSIZE(kValues)];
+  XCTAssertNotNil(dict);
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:32ULL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"jkl");
+
+  [dict setValue:@"jkl" forKey:31ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:32ULL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"jkl");
+
+  [dict setValue:@"def" forKey:34ULL];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:32ULL], @"def");
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"def");
+
+  const uint64_t kKeys2[] = { 32ULL, 33ULL };
+  const id kValues2[] = { @"ghi", @"abc" };
+  GPBUInt64ObjectDictionary *dict2 =
+      [[GPBUInt64ObjectDictionary alloc] initWithValues:kValues2
+                                                forKeys:kKeys2
+                                                  count:GPBARRAYSIZE(kValues2)];
+  XCTAssertNotNil(dict2);
+  [dict addEntriesFromDictionary:dict2];
+  XCTAssertEqual(dict.count, 4U);
+  XCTAssertEqualObjects([dict valueForKey:31ULL], @"jkl");
+  XCTAssertEqualObjects([dict valueForKey:32ULL], @"ghi");
+  XCTAssertEqualObjects([dict valueForKey:33ULL], @"abc");
+  XCTAssertEqualObjects([dict valueForKey:34ULL], @"def");
+
+  [dict2 release];
+  [dict release];
+}
+
+@end
+
+//%PDDM-EXPAND-END TEST_FOR_POD_KEY(UInt64, uint64_t, 31ULL, 32ULL, 33ULL, 34ULL)

+ 1044 - 0
objectivec/Tests/GPBDictionaryTests.pddm

@@ -0,0 +1,1044 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//%PDDM-DEFINE TEST_FOR_POD_KEY(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4)
+//%TESTS_FOR_POD_VALUES(KEY_NAME, KEY_TYPE, , , KEY1, KEY2, KEY3, KEY4)
+//%TESTS_FOR_POD_KEY_OBJECT_VALUE(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4, Object, id, @"abc", @"def", @"ghi", @"jkl")
+
+//%PDDM-DEFINE TESTS_FOR_POD_VALUES(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4)
+//%TEST_HELPERS(KEY_NAME, KEY_TYPE, KisP)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, UInt32, uint32_t, , 100U, 101U, 102U, 103U)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Int32, int32_t, , 200, 201, 202, 203)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, UInt64, uint64_t, , 300U, 301U, 302U, 303U)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Int64, int64_t, , 400, 401, 402, 403)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Bool, BOOL, , YES, YES, NO, NO)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Float, float, , 500.f, 501.f, 502.f, 503.f)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Double, double, , 600., 601., 602., 603.)
+//%TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Enum, int32_t, Raw, 700, 701, 702, 703)
+//%TESTS_FOR_ENUM_VALUE_RAW_ADDITIONS(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4)
+
+//%PDDM-DEFINE TESTS_FOR_POD_VALUE(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, VACCESSOR, VAL1, VAL2, VAL3, VAL4)
+//%TESTS_COMMON(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, , POD, VACCESSOR, VAL1, VAL2, VAL3, VAL4)
+
+//%PDDM-DEFINE TESTS_FOR_POD_KEY_OBJECT_VALUE(KEY_NAME, KEY_TYPE, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, VAL1, VAL2, VAL3, VAL4)
+//%TESTS_COMMON(KEY_NAME, KEY_TYPE, , , KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, Objects, OBJECT, , VAL1, VAL2, VAL3, VAL4)
+
+//%PDDM-DEFINE TESTS_COMMON(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, VSUFFIX, VHELPER, VACCESSOR, VAL1, VAL2, VAL3, VAL4)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@interface GPB##KEY_NAME##VALUE_NAME##DictionaryTests : XCTestCase
+//%@end
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##DictionaryTests
+//%
+//%- (void)testEmpty {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 0U);
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    #pragma unused(aKey, aValue, stop)
+//%    XCTFail(@"Shouldn't get here!");
+//%  }];
+//%  [dict release];
+//%}
+//%
+//%- (void)testOne {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithValue:VAL1 forKey:KEY1];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 1U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    XCTAssertEqual##KSUFFIX(aKey, KEY1);
+//%    XCTAssertEqual##VSUFFIX(aValue, VAL1);
+//%    XCTAssertNotEqual(stop, NULL);
+//%  }];
+//%}
+//%
+//%- (void)testBasics {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 3U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY4)
+//%
+//%  __block NSUInteger idx = 0;
+//%  KEY_TYPE KisP##*seenKeys = malloc(3 * sizeof(KEY_TYPE##KisP));
+//%  VALUE_TYPE *seenValues = malloc(3 * sizeof(VALUE_TYPE));
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    XCTAssertLessThan(idx, 3U);
+//%    seenKeys[idx] = aKey;
+//%    seenValues[idx] = aValue;
+//%    XCTAssertNotEqual(stop, NULL);
+//%    ++idx;
+//%  }];
+//%  for (int i = 0; i < 3; ++i) {
+//%    BOOL foundKey = NO;
+//%    for (int j = 0; (j < 3) && !foundKey; ++j) {
+//%      if (COMPARE_KEYS##KSUFFIX(kKeys[i], seenKeys[j])) {
+//%        foundKey = YES;
+//%        XCTAssertEqual##VSUFFIX(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+//%      }
+//%    }
+//%    XCTAssertTrue(foundKey, @"i = %d", i);
+//%  }
+//%  free(seenKeys);
+//%  free(seenValues);
+//%
+//%  // Stopping the enumeration.
+//%  idx = 0;
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    #pragma unused(aKey, aValue)
+//%    if (idx == 1) *stop = YES;
+//%    XCTAssertNotEqual(idx, 2U);
+//%    ++idx;
+//%  }];
+//%  [dict release];
+//%}
+//%
+//%- (void)testEquality {
+//%  const KEY_TYPE KisP##kKeys1[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY1, KEY4 };
+//%  const VALUE_TYPE kValues1[] = { VAL1, VAL2, VAL3 };
+//%  const VALUE_TYPE kValues2[] = { VAL1, VAL4, VAL3 };
+//%  const VALUE_TYPE kValues3[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict1);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1prime =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict1prime);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues2
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(dict2);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict3 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys2
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict3);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict4 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues3
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues3)];
+//%  XCTAssertNotNil(dict4);
+//%
+//%  // 1/1Prime should be different objects, but equal.
+//%  XCTAssertNotEqual(dict1, dict1prime);
+//%  XCTAssertEqualObjects(dict1, dict1prime);
+//%  // Equal, so they must have same hash.
+//%  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+//%
+//%  // 2 is save keys, different values; not equal.
+//%  XCTAssertNotEqualObjects(dict1, dict2);
+//%
+//%  // 3 is different keys, samae values; not equal.
+//%  XCTAssertNotEqualObjects(dict1, dict3);
+//%
+//%  // 4 extra pair; not equal
+//%  XCTAssertNotEqualObjects(dict1, dict4);
+//%
+//%  [dict1 release];
+//%  [dict1prime release];
+//%  [dict2 release];
+//%  [dict3 release];
+//%  [dict4 release];
+//%}
+//%
+//%- (void)testCopy {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new object but equal.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqualObjects(dict, dict2);
+//%  XCTAssertTrue([dict2 isKindOfClass:[GPB##KEY_NAME##VALUE_NAME##Dictionary class]]);
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%- (void)testDictionaryFromDictionary {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithDictionary:dict];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new pointer, but equal objects.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqualObjects(dict, dict2);
+//%  [dict release];
+//%}
+//%
+//%- (void)testAdds {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionary];
+//%  XCTAssertNotNil(dict);
+//%
+//%  XCTAssertEqual(dict.count, 0U);
+//%  [dict setValue:VAL1 forKey:KEY1];
+//%  XCTAssertEqual(dict.count, 1U);
+//%
+//%  const KEY_TYPE KisP##kKeys[] = { KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict2);
+//%  [dict add##VACCESSOR##EntriesFromDictionary:dict2];
+//%  XCTAssertEqual(dict.count, 4U);
+//%
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%  [dict2 release];
+//%}
+//%
+//%- (void)testRemove {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                 forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                   count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 4U);
+//%
+//%  [dict removeValueForKey:KEY2];
+//%  XCTAssertEqual(dict.count, 3U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  // Remove again does nothing.
+//%  [dict removeValueForKey:KEY2];
+//%  XCTAssertEqual(dict.count, 3U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  [dict removeValueForKey:KEY4];
+//%  XCTAssertEqual(dict.count, 2U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY4)
+//%
+//%  [dict removeAll];
+//%  XCTAssertEqual(dict.count, 0U);
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY3)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY4)
+//%  [dict release];
+//%}
+//%
+//%- (void)testInplaceMutation {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                 forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                   count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 4U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  [dict setValue:VAL4 forKey:KEY1];
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL4)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  [dict setValue:VAL2 forKey:KEY4];
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL4)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL2)
+//%
+//%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY3 };
+//%  const VALUE_TYPE kValues2[] = { VAL3, VAL1 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues2
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys2
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(dict2);
+//%  [dict add##VACCESSOR##EntriesFromDictionary:dict2];
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL4)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL2)
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE TESTS_FOR_ENUM_VALUE_RAW_ADDITIONS(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4)
+//%TESTS_FOR_ENUM_VALUE_RAW_ADDITIONS2(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, Enum, int32_t, , POD, 700, 801, 702, 803)
+//%PDDM-DEFINE TESTS_FOR_ENUM_VALUE_RAW_ADDITIONS2(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, KEY3, KEY4, VALUE_NAME, VALUE_TYPE, VSUFFIX, VHELPER, VAL1, VAL2, VAL3, VAL4)
+//%#pragma mark - KEY_NAME -> VALUE_NAME (Unknown Enums)
+//%
+//%@interface GPB##KEY_NAME##VALUE_NAME##DictionaryUnknownEnumTests : XCTestCase
+//%@end
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##DictionaryUnknownEnumTests
+//%
+//%- (void)testRawBasics {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 3U);
+//%  XCTAssertTrue(dict.validationFunc == TestingEnum_IsValidValue);  // Pointer comparison
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_RAW_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, kGPBUnrecognizedEnumeratorValue)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%RAW_VALUE_NOT_FOUND##VHELPER(dict, KEY4)
+//%
+//%  __block NSUInteger idx = 0;
+//%  KEY_TYPE KisP##*seenKeys = malloc(3 * sizeof(KEY_TYPE##KisP));
+//%  VALUE_TYPE *seenValues = malloc(3 * sizeof(VALUE_TYPE));
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    XCTAssertLessThan(idx, 3U);
+//%    seenKeys[idx] = aKey;
+//%    seenValues[idx] = aValue;
+//%    XCTAssertNotEqual(stop, NULL);
+//%    ++idx;
+//%  }];
+//%  for (int i = 0; i < 3; ++i) {
+//%    BOOL foundKey = NO;
+//%    for (int j = 0; (j < 3) && !foundKey; ++j) {
+//%      if (COMPARE_KEYS##KSUFFIX(kKeys[i], seenKeys[j])) {
+//%        foundKey = YES;
+//%        if (i == 1) {
+//%          XCTAssertEqual##VSUFFIX(kGPBUnrecognizedEnumeratorValue, seenValues[j], @"i = %d, j = %d", i, j);
+//%        } else {
+//%          XCTAssertEqual##VSUFFIX(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+//%        }
+//%      }
+//%    }
+//%    XCTAssertTrue(foundKey, @"i = %d", i);
+//%  }
+//%  idx = 0;
+//%  [dict enumerateKeysAndRawValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    XCTAssertLessThan(idx, 3U);
+//%    seenKeys[idx] = aKey;
+//%    seenValues[idx] = aValue;
+//%    XCTAssertNotEqual(stop, NULL);
+//%    ++idx;
+//%  }];
+//%  for (int i = 0; i < 3; ++i) {
+//%    BOOL foundKey = NO;
+//%    for (int j = 0; (j < 3) && !foundKey; ++j) {
+//%      if (COMPARE_KEYS##KSUFFIX(kKeys[i], seenKeys[j])) {
+//%        foundKey = YES;
+//%        XCTAssertEqual##VSUFFIX(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+//%      }
+//%    }
+//%    XCTAssertTrue(foundKey, @"i = %d", i);
+//%  }
+//%  free(seenKeys);
+//%  free(seenValues);
+//%
+//%  // Stopping the enumeration.
+//%  idx = 0;
+//%  [dict enumerateKeysAndRawValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    #pragma unused(aKey, aValue)
+//%    if (idx == 1) *stop = YES;
+//%    XCTAssertNotEqual(idx, 2U);
+//%    ++idx;
+//%  }];
+//%  [dict release];
+//%}
+//%
+//%- (void)testEqualityWithUnknowns {
+//%  const KEY_TYPE KisP##kKeys1[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY1, KEY4 };
+//%  const VALUE_TYPE kValues1[] = { VAL1, VAL2, VAL3 };  // Unknown
+//%  const VALUE_TYPE kValues2[] = { VAL1, VAL4, VAL3 };  // Unknown
+//%  const VALUE_TYPE kValues3[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict1);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1prime =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict1prime);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues2
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(dict2);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict3 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys2
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict3);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict4 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues3
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues3)];
+//%  XCTAssertNotNil(dict4);
+//%
+//%  // 1/1Prime should be different objects, but equal.
+//%  XCTAssertNotEqual(dict1, dict1prime);
+//%  XCTAssertEqualObjects(dict1, dict1prime);
+//%  // Equal, so they must have same hash.
+//%  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+//%
+//%  // 2 is save keys, different values; not equal.
+//%  XCTAssertNotEqualObjects(dict1, dict2);
+//%
+//%  // 3 is different keys, samae values; not equal.
+//%  XCTAssertNotEqualObjects(dict1, dict3);
+//%
+//%  // 4 extra pair; not equal
+//%  XCTAssertNotEqualObjects(dict1, dict4);
+//%
+//%  [dict1 release];
+//%  [dict1prime release];
+//%  [dict2 release];
+//%  [dict3 release];
+//%  [dict4 release];
+//%}
+//%
+//%- (void)testCopyWithUnknowns {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknown
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new pointer, but equal objects.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+//%  XCTAssertEqualObjects(dict, dict2);
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%- (void)testDictionaryFromDictionary {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithDictionary:dict];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new pointer, but equal objects.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqualObjects(dict, dict2);
+//%  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+//%  [dict release];
+//%}
+//%
+//%- (void)testUnknownAdds {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%    [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithValidationFunction:TestingEnum_IsValidValue];
+//%  XCTAssertNotNil(dict);
+//%
+//%  XCTAssertEqual(dict.count, 0U);
+//%  XCTAssertThrowsSpecificNamed([dict setValue:VAL2 forKey:KEY2],  // Unknown
+//%                               NSException, NSInvalidArgumentException);
+//%  XCTAssertEqual(dict.count, 0U);
+//%  [dict setRawValue:VAL2 forKey:KEY2];  // Unknown
+//%  XCTAssertEqual(dict.count, 1U);
+//%
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL3, VAL4 };  // Unknown
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict2);
+//%  [dict addRawEntriesFromDictionary:dict2];
+//%  XCTAssertEqual(dict.count, 4U);
+//%
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, kGPBUnrecognizedEnumeratorValue)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, kGPBUnrecognizedEnumeratorValue)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%  [dict2 release];
+//%}
+//%
+//%- (void)testUnknownRemove {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 4U);
+//%
+//%  [dict removeValueForKey:KEY2];
+//%  XCTAssertEqual(dict.count, 3U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  // Remove again does nothing.
+//%  [dict removeValueForKey:KEY2];
+//%  XCTAssertEqual(dict.count, 3U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  [dict removeValueForKey:KEY4];
+//%  XCTAssertEqual(dict.count, 2U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY4)
+//%
+//%  [dict removeAll];
+//%  XCTAssertEqual(dict.count, 0U);
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY3)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY4)
+//%  [dict release];
+//%}
+//%
+//%- (void)testInplaceMutationUnknowns {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };  // Unknowns
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 4U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  XCTAssertThrowsSpecificNamed([dict setValue:VAL4 forKey:KEY1],  // Unknown
+//%                               NSException, NSInvalidArgumentException);
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  [dict setRawValue:VAL4 forKey:KEY1];  // Unknown
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY1, VAL4)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY4, VAL4)
+//%
+//%  [dict setRawValue:VAL1 forKey:KEY4];
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY1, VAL4)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY3, VAL3)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL1)
+//%
+//%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY3 };
+//%  const VALUE_TYPE kValues2[] = { VAL3, VAL2 };  // Unknown
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues2
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys2
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(dict2);
+//%  [dict addRawEntriesFromDictionary:dict2];
+//%  XCTAssertEqual(dict.count, 4U);
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY1, VAL4)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL3)
+//%TEST_RAW_VALUE##VHELPER(dict, value, KEY3, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY4, VAL1)
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%- (void)testCopyUnknowns {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2, KEY3, KEY4 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2, VAL3, VAL4 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%           KEY_NAME$S VALUE_NAME$S                                  rawValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                                    forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                                      count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new pointer, but equal objects.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqualObjects(dict, dict2);
+//%  XCTAssertEqual(dict.validationFunc, dict2.validationFunc);  // Pointer comparison
+//%  XCTAssertTrue([dict2 isKindOfClass:[GPB##KEY_NAME##VALUE_NAME##Dictionary class]]);
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%@end
+//%
+
+//
+// Helpers for PODs
+//
+
+//%PDDM-DEFINE DECLARE_VALUE_STORAGEPOD(VALUE_TYPE, NAME)
+//%  VALUE_TYPE NAME;
+//%
+//%PDDM-DEFINE VALUE_NOT_FOUNDPOD(DICT, KEY)
+//%  XCTAssertFalse([DICT valueForKey:KEY value:NULL]);
+//%PDDM-DEFINE TEST_VALUEPOD(DICT, STORAGE, KEY, VALUE)
+//%  XCTAssertTrue([DICT valueForKey:KEY value:NULL]);
+//%  XCTAssertTrue([DICT valueForKey:KEY value:&STORAGE]);
+//%  XCTAssertEqual(STORAGE, VALUE);
+//%PDDM-DEFINE COMPARE_KEYS(KEY1, KEY2)
+//%KEY1 == KEY2
+//%PDDM-DEFINE RAW_VALUE_NOT_FOUNDPOD(DICT, KEY)
+//%  XCTAssertFalse([DICT valueForKey:KEY rawValue:NULL]);
+//%PDDM-DEFINE TEST_RAW_VALUEPOD(DICT, STORAGE, KEY, VALUE)
+//%  XCTAssertTrue([DICT valueForKey:KEY rawValue:NULL]);
+//%  XCTAssertTrue([DICT valueForKey:KEY rawValue:&STORAGE]);
+//%  XCTAssertEqual(STORAGE, VALUE);
+
+//
+// Helpers for Objects
+//
+
+//%PDDM-DEFINE DECLARE_VALUE_STORAGEOBJECT(VALUE_TYPE, NAME)
+// Empty
+//%PDDM-DEFINE VALUE_NOT_FOUNDOBJECT(DICT, KEY)
+//%  XCTAssertNil([DICT valueForKey:KEY]);
+//%PDDM-DEFINE TEST_VALUEOBJECT(DICT, STORAGE, KEY, VALUE)
+//%  XCTAssertEqualObjects([DICT valueForKey:KEY], VALUE);
+//%PDDM-DEFINE COMPARE_KEYSObjects(KEY1, KEY2)
+//%[KEY1 isEqual:KEY2]
+
+//
+// Helpers for tests.
+//
+
+//%PDDM-DEFINE TEST_HELPERS(KEY_NAME, KEY_TYPE, KisP)
+//%#ifndef GPBARRAYSIZE
+//%#define GPBARRAYSIZE(a) ((sizeof(a) / sizeof((a[0]))))
+//%#endif  // GPBARRAYSIZE
+//%
+//%// To let the testing macros work, add some extra methods to simplify things.
+//%@interface GPB##KEY_NAME##EnumDictionary (TestingTweak)
+//%+ (instancetype)dictionaryWithValue:(int32_t)value forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%- (instancetype)initWithValues:(const int32_t [])values
+//%                       forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                         count:(NSUInteger)count;
+//%@end
+//%
+//%static BOOL TestingEnum_IsValidValue(int32_t value) {
+//%  switch (value) {
+//%    case 700:
+//%    case 701:
+//%    case 702:
+//%    case 703:
+//%      return YES;
+//%    default:
+//%      return NO;
+//%  }
+//%}
+//%
+//%@implementation GPB##KEY_NAME##EnumDictionary (TestingTweak)
+//%+ (instancetype)dictionaryWithValue:(int32_t)value forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  // Cast is needed to compiler knows what class we are invoking initWithValues: on to get the
+//%  // type correct.
+//%  return [[(GPB##KEY_NAME##EnumDictionary*)[self alloc] initWithValidationFunction:TestingEnum_IsValidValue
+//%                KEY_NAME$S                                             rawValues:&value
+//%                KEY_NAME$S                                               forKeys:&key
+//%                KEY_NAME$S                                                 count:1] autorelease];
+//%}
+//%- (instancetype)initWithValues:(const int32_t [])values
+//%                       forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                         count:(NSUInteger)count {
+//%  return [self initWithValidationFunction:TestingEnum_IsValidValue
+//%                                rawValues:values
+//%                                  forKeys:keys
+//%                                    count:count];
+//%}
+//%@end
+//%
+//%
+
+
+//
+// BOOL test macros
+//
+//TODO(thomasvl): enum tests
+
+//%PDDM-DEFINE BOOL_TESTS_FOR_POD_VALUE(VALUE_NAME, VALUE_TYPE, VAL1, VAL2)
+//%BOOL_TESTS_COMMON(Bool, BOOL, , , YES, NO, VALUE_NAME, VALUE_TYPE, , POD, VAL1, VAL2)
+
+//%PDDM-DEFINE TESTS_FOR_BOOL_KEY_OBJECT_VALUE(VALUE_NAME, VALUE_TYPE, VAL1, VAL2)
+//%BOOL_TESTS_COMMON(Bool, BOOL, , , YES, NO, VALUE_NAME, VALUE_TYPE, Objects, OBJECT, VAL1, VAL2)
+
+//%PDDM-DEFINE BOOL_TESTS_COMMON(KEY_NAME, KEY_TYPE, KisP, KSUFFIX, KEY1, KEY2, VALUE_NAME, VALUE_TYPE, VSUFFIX, VHELPER, VAL1, VAL2)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@interface GPB##KEY_NAME##VALUE_NAME##DictionaryTests : XCTestCase
+//%@end
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##DictionaryTests
+//%
+//%- (void)testEmpty {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 0U);
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    #pragma unused(aKey, aValue, stop)
+//%    XCTFail(@"Shouldn't get here!");
+//%  }];
+//%  [dict release];
+//%}
+//%
+//%- (void)testOne {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithValue:VAL1 forKey:KEY1];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 1U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    XCTAssertEqual##KSUFFIX(aKey, KEY1);
+//%    XCTAssertEqual##VSUFFIX(aValue, VAL1);
+//%    XCTAssertNotEqual(stop, NULL);
+//%  }];
+//%}
+//%
+//%- (void)testBasics {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 2U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%
+//%  __block NSUInteger idx = 0;
+//%  KEY_TYPE KisP##*seenKeys = malloc(2 * sizeof(KEY_TYPE##KisP));
+//%  VALUE_TYPE *seenValues = malloc(2 * sizeof(VALUE_TYPE));
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    XCTAssertLessThan(idx, 2U);
+//%    seenKeys[idx] = aKey;
+//%    seenValues[idx] = aValue;
+//%    XCTAssertNotEqual(stop, NULL);
+//%    ++idx;
+//%  }];
+//%  for (int i = 0; i < 2; ++i) {
+//%    BOOL foundKey = NO;
+//%    for (int j = 0; (j < 2) && !foundKey; ++j) {
+//%      if (COMPARE_KEYS##KSUFFIX(kKeys[i], seenKeys[j])) {
+//%        foundKey = YES;
+//%        XCTAssertEqual##VSUFFIX(kValues[i], seenValues[j], @"i = %d, j = %d", i, j);
+//%      }
+//%    }
+//%    XCTAssertTrue(foundKey, @"i = %d", i);
+//%  }
+//%  free(seenKeys);
+//%  free(seenValues);
+//%
+//%  // Stopping the enumeration.
+//%  idx = 0;
+//%  [dict enumerateKeysAndValuesUsingBlock:^(KEY_TYPE KisP##aKey, VALUE_TYPE aValue, BOOL *stop) {
+//%    #pragma unused(aKey, aValue)
+//%    if (idx == 0) *stop = YES;
+//%    XCTAssertNotEqual(idx, 2U);
+//%    ++idx;
+//%  }];
+//%  [dict release];
+//%}
+//%
+//%- (void)testEquality {
+//%  const KEY_TYPE KisP##kKeys1[] = { KEY1, KEY2 };
+//%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY1 };
+//%  const VALUE_TYPE kValues1[] = { VAL1, VAL2 };
+//%  const VALUE_TYPE kValues2[] = { VAL2, VAL1 };
+//%  const VALUE_TYPE kValues3[] = { VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict1);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict1prime =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict1prime);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues2
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(dict2);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict3 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues1
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys2
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues1)];
+//%  XCTAssertNotNil(dict3);
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict4 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues3
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys1
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues3)];
+//%  XCTAssertNotNil(dict4);
+//%
+//%  // 1/1Prime should be different objects, but equal.
+//%  XCTAssertNotEqual(dict1, dict1prime);
+//%  XCTAssertEqualObjects(dict1, dict1prime);
+//%  // Equal, so they must have same hash.
+//%  XCTAssertEqual([dict1 hash], [dict1prime hash]);
+//%
+//%  // 2 is save keys, different values; not equal.
+//%  XCTAssertNotEqualObjects(dict1, dict2);
+//%
+//%  // 3 is different keys, samae values; not equal.
+//%  XCTAssertNotEqualObjects(dict1, dict3);
+//%
+//%  // 4 Fewer pairs; not equal
+//%  XCTAssertNotEqualObjects(dict1, dict4);
+//%
+//%  [dict1 release];
+//%  [dict1prime release];
+//%  [dict2 release];
+//%  [dict3 release];
+//%  [dict4 release];
+//%}
+//%
+//%- (void)testCopy {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 = [dict copy];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new object but equal.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqualObjects(dict, dict2);
+//%  XCTAssertTrue([dict2 isKindOfClass:[GPB##KEY_NAME##VALUE_NAME##Dictionary class]]);
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%- (void)testDictionaryFromDictionary {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionaryWithDictionary:dict];
+//%  XCTAssertNotNil(dict2);
+//%
+//%  // Should be new pointer, but equal objects.
+//%  XCTAssertNotEqual(dict, dict2);
+//%  XCTAssertEqualObjects(dict, dict2);
+//%  [dict release];
+//%}
+//%
+//%- (void)testAdds {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict = [GPB##KEY_NAME##VALUE_NAME##Dictionary dictionary];
+//%  XCTAssertNotNil(dict);
+//%
+//%  XCTAssertEqual(dict.count, 0U);
+//%  [dict setValue:VAL1 forKey:KEY1];
+//%  XCTAssertEqual(dict.count, 1U);
+//%
+//%  const KEY_TYPE KisP##kKeys[] = { KEY2 };
+//%  const VALUE_TYPE kValues[] = { VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict2);
+//%  [dict addEntriesFromDictionary:dict2];
+//%  XCTAssertEqual(dict.count, 2U);
+//%
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%  [dict2 release];
+//%}
+//%
+//%- (void)testRemove {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2};
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                 forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                   count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 2U);
+//%
+//%  [dict removeValueForKey:KEY2];
+//%  XCTAssertEqual(dict.count, 1U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%
+//%  // Remove again does nothing.
+//%  [dict removeValueForKey:KEY2];
+//%  XCTAssertEqual(dict.count, 1U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%
+//%  [dict removeAll];
+//%  XCTAssertEqual(dict.count, 0U);
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY1)
+//%VALUE_NOT_FOUND##VHELPER(dict, KEY2)
+//%  [dict release];
+//%}
+//%
+//%- (void)testInplaceMutation {
+//%  const KEY_TYPE KisP##kKeys[] = { KEY1, KEY2 };
+//%  const VALUE_TYPE kValues[] = { VAL1, VAL2 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues
+//%           KEY_NAME$S VALUE_NAME$S                 forKeys:kKeys
+//%           KEY_NAME$S VALUE_NAME$S                   count:GPBARRAYSIZE(kValues)];
+//%  XCTAssertNotNil(dict);
+//%  XCTAssertEqual(dict.count, 2U);
+//%DECLARE_VALUE_STORAGE##VHELPER(VALUE_TYPE, value)TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%
+//%  [dict setValue:VAL2 forKey:KEY1];
+//%  XCTAssertEqual(dict.count, 2U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%
+//%  [dict setValue:VAL1 forKey:KEY2];
+//%  XCTAssertEqual(dict.count, 2U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL2)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL1)
+//%
+//%  const KEY_TYPE KisP##kKeys2[] = { KEY2, KEY1 };
+//%  const VALUE_TYPE kValues2[] = { VAL2, VAL1 };
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *dict2 =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] initWithValues:kValues2
+//%           KEY_NAME$S VALUE_NAME$S                        forKeys:kKeys2
+//%           KEY_NAME$S VALUE_NAME$S                          count:GPBARRAYSIZE(kValues2)];
+//%  XCTAssertNotNil(dict2);
+//%  [dict addEntriesFromDictionary:dict2];
+//%  XCTAssertEqual(dict.count, 2U);
+//%TEST_VALUE##VHELPER(dict, value, KEY1, VAL1)
+//%TEST_VALUE##VHELPER(dict, value, KEY2, VAL2)
+//%
+//%  [dict2 release];
+//%  [dict release];
+//%}
+//%
+//%@end
+//%
+

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików