Merge branch 'gerben-fast-table' into fast-table
diff --git a/BUILD b/BUILD
index 161edd8..e1114a5 100644
--- a/BUILD
+++ b/BUILD
@@ -79,13 +79,16 @@
     name = "upb",
     srcs = [
         "upb/decode.c",
+        "upb/decode.int.h",
         "upb/decode_fast.c",
+        "upb/decode_fast.h",
         "upb/encode.c",
         "upb/msg.c",
         "upb/msg.h",
         "upb/table.c",
         "upb/table.int.h",
         "upb/upb.c",
+        "upb/upb.int.h",
     ],
     hdrs = [
         "upb/decode.h",
@@ -110,6 +113,7 @@
 cc_library(
     name = "generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
     hdrs = [
+        "upb/decode_fast.h",
         "upb/msg.h",
         "upb/port_def.inc",
         "upb/port_undef.inc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5ce67f..5e48701 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,13 +64,16 @@
   upb/port.c)
 add_library(upb
   upb/decode.c
+  upb/decode.int.h
   upb/decode_fast.c
+  upb/decode_fast.h
   upb/encode.c
   upb/msg.c
   upb/msg.h
   upb/table.c
   upb/table.int.h
   upb/upb.c
+  upb/upb.int.h
   upb/decode.h
   upb/encode.h
   upb/upb.h
diff --git a/benchmark.py b/benchmark.py
index 7b5717f..70a3cce 100755
--- a/benchmark.py
+++ b/benchmark.py
@@ -27,30 +27,33 @@
 def Run(cmd):
   subprocess.check_call(cmd, shell=True)
 
-def Benchmark(outbase, runs=12):
+def Benchmark(outbase, bench_cpu=True, runs=12):
   tmpfile = "/tmp/bench-output.json"
   Run("rm -rf {}".format(tmpfile))
   Run("CC=clang bazel test :all")
-  Run("CC=clang bazel build -c opt --copt=-march=native :benchmark")
 
-  Run("./bazel-bin/benchmark --benchmark_out_format=json --benchmark_out={} --benchmark_repetitions={}".format(tmpfile, runs))
+  if bench_cpu:
+    Run("CC=clang bazel build -c opt --copt=-march=native :benchmark")
+
+    Run("./bazel-bin/benchmark --benchmark_out_format=json --benchmark_out={} --benchmark_repetitions={}".format(tmpfile, runs))
+    with open(tmpfile) as f:
+      bench_json = json.load(f)
+
+    # Translate into the format expected by benchstat.
+    with open(outbase + ".txt", "w") as f:
+      for run in bench_json["benchmarks"]:
+        name = re.sub(r'^BM_', 'Benchmark', run["name"])
+        if name.endswith("_mean") or name.endswith("_median") or name.endswith("_stddev"):
+          continue
+        values = (name, run["iterations"], run["cpu_time"])
+        print("{} {} {} ns/op".format(*values), file=f)
 
   Run("CC=clang bazel build -c opt --copt=-g :conformance_upb")
   Run("cp -f bazel-bin/conformance_upb {}.bin".format(outbase))
 
-  with open(tmpfile) as f:
-    bench_json = json.load(f)
-
-  # Translate into the format expected by benchstat.
-  with open(outbase + ".txt", "w") as f:
-    for run in bench_json["benchmarks"]:
-      name = re.sub(r'^BM_', 'Benchmark', run["name"])
-      if name.endswith("_mean") or name.endswith("_median") or name.endswith("_stddev"):
-        continue
-      values = (name, run["iterations"], run["cpu_time"])
-      print("{} {} {} ns/op".format(*values), file=f)
 
 baseline = "master"
+bench_cpu = True
 
 if len(sys.argv) > 1:
   baseline = sys.argv[1]
@@ -60,16 +63,17 @@
     pass
 
 # Benchmark our current directory first, since it's more likely to be broken.
-Benchmark("/tmp/new")
+Benchmark("/tmp/new", bench_cpu)
 
 # Benchmark the baseline.
 with GitWorktree(baseline):
-  Benchmark("/tmp/old")
+  Benchmark("/tmp/old", bench_cpu)
 
 print()
 print()
 
-Run("~/go/bin/benchstat /tmp/old.txt /tmp/new.txt")
+if bench_cpu:
+  Run("~/go/bin/benchstat /tmp/old.txt /tmp/new.txt")
 
 print()
 print()
diff --git a/generated_for_cmake/google/protobuf/descriptor.upb.h b/generated_for_cmake/google/protobuf/descriptor.upb.h
index 64701a0..08d5c7f 100644
--- a/generated_for_cmake/google/protobuf/descriptor.upb.h
+++ b/generated_for_cmake/google/protobuf/descriptor.upb.h
@@ -11,6 +11,7 @@
 
 #include "upb/msg.h"
 #include "upb/decode.h"
+#include "upb/decode_fast.h"
 #include "upb/encode.h"
 
 #include "upb/port_def.inc"
@@ -174,12 +175,12 @@
   return (google_protobuf_FileDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
 }
 UPB_INLINE google_protobuf_FileDescriptorProto** google_protobuf_FileDescriptorSet_resize_file(google_protobuf_FileDescriptorSet *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_FileDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_FileDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_FileDescriptorProto* google_protobuf_FileDescriptorSet_add_file(google_protobuf_FileDescriptorSet *msg, upb_arena *arena) {
   struct google_protobuf_FileDescriptorProto* sub = (struct google_protobuf_FileDescriptorProto*)_upb_msg_new(&google_protobuf_FileDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -232,22 +233,22 @@
   return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(36, 72), len);
 }
 UPB_INLINE upb_strview* google_protobuf_FileDescriptorProto_resize_dependency(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(36, 72), len, UPB_TYPE_STRING, arena);
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(36, 72), len, UPB_SIZE(3, 4), arena);
 }
 UPB_INLINE bool google_protobuf_FileDescriptorProto_add_dependency(google_protobuf_FileDescriptorProto *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(36, 72), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(36, 72), UPB_SIZE(8, 16), UPB_SIZE(3, 4), &val,
       arena);
 }
 UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_FileDescriptorProto_mutable_message_type(google_protobuf_FileDescriptorProto *msg, size_t *len) {
   return (google_protobuf_DescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(40, 80), len);
 }
 UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_FileDescriptorProto_resize_message_type(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_DescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(40, 80), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_DescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(40, 80), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_DescriptorProto* google_protobuf_FileDescriptorProto_add_message_type(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_DescriptorProto* sub = (struct google_protobuf_DescriptorProto*)_upb_msg_new(&google_protobuf_DescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(40, 80), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(40, 80), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -255,12 +256,12 @@
   return (google_protobuf_EnumDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(44, 88), len);
 }
 UPB_INLINE google_protobuf_EnumDescriptorProto** google_protobuf_FileDescriptorProto_resize_enum_type(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_EnumDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(44, 88), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_EnumDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(44, 88), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_EnumDescriptorProto* google_protobuf_FileDescriptorProto_add_enum_type(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_EnumDescriptorProto* sub = (struct google_protobuf_EnumDescriptorProto*)_upb_msg_new(&google_protobuf_EnumDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(44, 88), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(44, 88), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -268,12 +269,12 @@
   return (google_protobuf_ServiceDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(48, 96), len);
 }
 UPB_INLINE google_protobuf_ServiceDescriptorProto** google_protobuf_FileDescriptorProto_resize_service(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_ServiceDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(48, 96), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_ServiceDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(48, 96), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_ServiceDescriptorProto* google_protobuf_FileDescriptorProto_add_service(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_ServiceDescriptorProto* sub = (struct google_protobuf_ServiceDescriptorProto*)_upb_msg_new(&google_protobuf_ServiceDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(48, 96), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(48, 96), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -281,12 +282,12 @@
   return (google_protobuf_FieldDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(52, 104), len);
 }
 UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_FileDescriptorProto_resize_extension(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(52, 104), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(52, 104), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_FieldDescriptorProto* google_protobuf_FileDescriptorProto_add_extension(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_FieldDescriptorProto* sub = (struct google_protobuf_FieldDescriptorProto*)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(52, 104), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(52, 104), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -320,20 +321,20 @@
   return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(56, 112), len);
 }
 UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_resize_public_dependency(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(56, 112), len, UPB_TYPE_INT32, arena);
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(56, 112), len, 2, arena);
 }
 UPB_INLINE bool google_protobuf_FileDescriptorProto_add_public_dependency(google_protobuf_FileDescriptorProto *msg, int32_t val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(56, 112), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(56, 112), UPB_SIZE(4, 4), 2, &val,
       arena);
 }
 UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_mutable_weak_dependency(google_protobuf_FileDescriptorProto *msg, size_t *len) {
   return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(60, 120), len);
 }
 UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_resize_weak_dependency(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(60, 120), len, UPB_TYPE_INT32, arena);
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(60, 120), len, 2, arena);
 }
 UPB_INLINE bool google_protobuf_FileDescriptorProto_add_weak_dependency(google_protobuf_FileDescriptorProto *msg, int32_t val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(60, 120), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(60, 120), UPB_SIZE(4, 4), 2, &val,
       arena);
 }
 UPB_INLINE void google_protobuf_FileDescriptorProto_set_syntax(google_protobuf_FileDescriptorProto *msg, upb_strview value) {
@@ -383,12 +384,12 @@
   return (google_protobuf_FieldDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 32), len);
 }
 UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_DescriptorProto_resize_field(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_FieldDescriptorProto* google_protobuf_DescriptorProto_add_field(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_FieldDescriptorProto* sub = (struct google_protobuf_FieldDescriptorProto*)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -396,12 +397,12 @@
   return (google_protobuf_DescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 40), len);
 }
 UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_DescriptorProto_resize_nested_type(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_DescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_DescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_DescriptorProto* google_protobuf_DescriptorProto_add_nested_type(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_DescriptorProto* sub = (struct google_protobuf_DescriptorProto*)_upb_msg_new(&google_protobuf_DescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(20, 40), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(20, 40), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -409,12 +410,12 @@
   return (google_protobuf_EnumDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 48), len);
 }
 UPB_INLINE google_protobuf_EnumDescriptorProto** google_protobuf_DescriptorProto_resize_enum_type(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_EnumDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_EnumDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_EnumDescriptorProto* google_protobuf_DescriptorProto_add_enum_type(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_EnumDescriptorProto* sub = (struct google_protobuf_EnumDescriptorProto*)_upb_msg_new(&google_protobuf_EnumDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(24, 48), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(24, 48), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -422,12 +423,12 @@
   return (google_protobuf_DescriptorProto_ExtensionRange**)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 56), len);
 }
 UPB_INLINE google_protobuf_DescriptorProto_ExtensionRange** google_protobuf_DescriptorProto_resize_extension_range(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_DescriptorProto_ExtensionRange**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 56), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_DescriptorProto_ExtensionRange**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 56), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_DescriptorProto_ExtensionRange* google_protobuf_DescriptorProto_add_extension_range(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_DescriptorProto_ExtensionRange* sub = (struct google_protobuf_DescriptorProto_ExtensionRange*)_upb_msg_new(&google_protobuf_DescriptorProto_ExtensionRange_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(28, 56), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(28, 56), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -435,12 +436,12 @@
   return (google_protobuf_FieldDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(32, 64), len);
 }
 UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_DescriptorProto_resize_extension(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(32, 64), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(32, 64), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_FieldDescriptorProto* google_protobuf_DescriptorProto_add_extension(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_FieldDescriptorProto* sub = (struct google_protobuf_FieldDescriptorProto*)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(32, 64), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(32, 64), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -461,12 +462,12 @@
   return (google_protobuf_OneofDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(36, 72), len);
 }
 UPB_INLINE google_protobuf_OneofDescriptorProto** google_protobuf_DescriptorProto_resize_oneof_decl(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_OneofDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(36, 72), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_OneofDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(36, 72), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_OneofDescriptorProto* google_protobuf_DescriptorProto_add_oneof_decl(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_OneofDescriptorProto* sub = (struct google_protobuf_OneofDescriptorProto*)_upb_msg_new(&google_protobuf_OneofDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(36, 72), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(36, 72), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -474,12 +475,12 @@
   return (google_protobuf_DescriptorProto_ReservedRange**)_upb_array_mutable_accessor(msg, UPB_SIZE(40, 80), len);
 }
 UPB_INLINE google_protobuf_DescriptorProto_ReservedRange** google_protobuf_DescriptorProto_resize_reserved_range(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_DescriptorProto_ReservedRange**)_upb_array_resize_accessor(msg, UPB_SIZE(40, 80), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_DescriptorProto_ReservedRange**)_upb_array_resize_accessor(msg, UPB_SIZE(40, 80), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_DescriptorProto_ReservedRange* google_protobuf_DescriptorProto_add_reserved_range(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_DescriptorProto_ReservedRange* sub = (struct google_protobuf_DescriptorProto_ReservedRange*)_upb_msg_new(&google_protobuf_DescriptorProto_ReservedRange_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(40, 80), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(40, 80), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -487,10 +488,10 @@
   return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(44, 88), len);
 }
 UPB_INLINE upb_strview* google_protobuf_DescriptorProto_resize_reserved_name(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(44, 88), len, UPB_TYPE_STRING, arena);
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(44, 88), len, UPB_SIZE(3, 4), arena);
 }
 UPB_INLINE bool google_protobuf_DescriptorProto_add_reserved_name(google_protobuf_DescriptorProto *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(44, 88), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(44, 88), UPB_SIZE(8, 16), UPB_SIZE(3, 4), &val,
       arena);
 }
 
@@ -586,12 +587,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_ExtensionRangeOptions_resize_uninterpreted_option(google_protobuf_ExtensionRangeOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_ExtensionRangeOptions_add_uninterpreted_option(google_protobuf_ExtensionRangeOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -756,12 +757,12 @@
   return (google_protobuf_EnumValueDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 32), len);
 }
 UPB_INLINE google_protobuf_EnumValueDescriptorProto** google_protobuf_EnumDescriptorProto_resize_value(google_protobuf_EnumDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_EnumValueDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_EnumValueDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_EnumValueDescriptorProto* google_protobuf_EnumDescriptorProto_add_value(google_protobuf_EnumDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_EnumValueDescriptorProto* sub = (struct google_protobuf_EnumValueDescriptorProto*)_upb_msg_new(&google_protobuf_EnumValueDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -782,12 +783,12 @@
   return (google_protobuf_EnumDescriptorProto_EnumReservedRange**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 40), len);
 }
 UPB_INLINE google_protobuf_EnumDescriptorProto_EnumReservedRange** google_protobuf_EnumDescriptorProto_resize_reserved_range(google_protobuf_EnumDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_EnumDescriptorProto_EnumReservedRange**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_EnumDescriptorProto_EnumReservedRange**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_EnumDescriptorProto_EnumReservedRange* google_protobuf_EnumDescriptorProto_add_reserved_range(google_protobuf_EnumDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_EnumDescriptorProto_EnumReservedRange* sub = (struct google_protobuf_EnumDescriptorProto_EnumReservedRange*)_upb_msg_new(&google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(20, 40), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(20, 40), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -795,10 +796,10 @@
   return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 48), len);
 }
 UPB_INLINE upb_strview* google_protobuf_EnumDescriptorProto_resize_reserved_name(google_protobuf_EnumDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_TYPE_STRING, arena);
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_SIZE(3, 4), arena);
 }
 UPB_INLINE bool google_protobuf_EnumDescriptorProto_add_reserved_name(google_protobuf_EnumDescriptorProto *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(24, 48), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(24, 48), UPB_SIZE(8, 16), UPB_SIZE(3, 4), &val,
       arena);
 }
 
@@ -902,12 +903,12 @@
   return (google_protobuf_MethodDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 32), len);
 }
 UPB_INLINE google_protobuf_MethodDescriptorProto** google_protobuf_ServiceDescriptorProto_resize_method(google_protobuf_ServiceDescriptorProto *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_MethodDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_MethodDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_MethodDescriptorProto* google_protobuf_ServiceDescriptorProto_add_method(google_protobuf_ServiceDescriptorProto *msg, upb_arena *arena) {
   struct google_protobuf_MethodDescriptorProto* sub = (struct google_protobuf_MethodDescriptorProto*)_upb_msg_new(&google_protobuf_MethodDescriptorProto_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1127,12 +1128,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(108, 192), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_FileOptions_resize_uninterpreted_option(google_protobuf_FileOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(108, 192), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(108, 192), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_FileOptions_add_uninterpreted_option(google_protobuf_FileOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(108, 192), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(108, 192), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1182,12 +1183,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(8, 8), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_MessageOptions_resize_uninterpreted_option(google_protobuf_MessageOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(8, 8), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(8, 8), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_MessageOptions_add_uninterpreted_option(google_protobuf_MessageOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(8, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(8, 8), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1249,12 +1250,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 32), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_FieldOptions_resize_uninterpreted_option(google_protobuf_FieldOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 32), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 32), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_FieldOptions_add_uninterpreted_option(google_protobuf_FieldOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(28, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(28, 32), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1280,12 +1281,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_OneofOptions_resize_uninterpreted_option(google_protobuf_OneofOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_OneofOptions_add_uninterpreted_option(google_protobuf_OneofOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1323,12 +1324,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_EnumOptions_resize_uninterpreted_option(google_protobuf_EnumOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_EnumOptions_add_uninterpreted_option(google_protobuf_EnumOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1360,12 +1361,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_EnumValueOptions_resize_uninterpreted_option(google_protobuf_EnumValueOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_EnumValueOptions_add_uninterpreted_option(google_protobuf_EnumValueOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1397,12 +1398,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_ServiceOptions_resize_uninterpreted_option(google_protobuf_ServiceOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_ServiceOptions_add_uninterpreted_option(google_protobuf_ServiceOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1440,12 +1441,12 @@
   return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 24), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_MethodOptions_resize_uninterpreted_option(google_protobuf_MethodOptions *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 24), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 24), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_MethodOptions_add_uninterpreted_option(google_protobuf_MethodOptions *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(20, 24), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(20, 24), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1483,12 +1484,12 @@
   return (google_protobuf_UninterpretedOption_NamePart**)_upb_array_mutable_accessor(msg, UPB_SIZE(56, 80), len);
 }
 UPB_INLINE google_protobuf_UninterpretedOption_NamePart** google_protobuf_UninterpretedOption_resize_name(google_protobuf_UninterpretedOption *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_UninterpretedOption_NamePart**)_upb_array_resize_accessor(msg, UPB_SIZE(56, 80), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_UninterpretedOption_NamePart**)_upb_array_resize_accessor(msg, UPB_SIZE(56, 80), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_UninterpretedOption_NamePart* google_protobuf_UninterpretedOption_add_name(google_protobuf_UninterpretedOption *msg, upb_arena *arena) {
   struct google_protobuf_UninterpretedOption_NamePart* sub = (struct google_protobuf_UninterpretedOption_NamePart*)_upb_msg_new(&google_protobuf_UninterpretedOption_NamePart_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(56, 80), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(56, 80), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1566,12 +1567,12 @@
   return (google_protobuf_SourceCodeInfo_Location**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
 }
 UPB_INLINE google_protobuf_SourceCodeInfo_Location** google_protobuf_SourceCodeInfo_resize_location(google_protobuf_SourceCodeInfo *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_SourceCodeInfo_Location**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_SourceCodeInfo_Location**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_SourceCodeInfo_Location* google_protobuf_SourceCodeInfo_add_location(google_protobuf_SourceCodeInfo *msg, upb_arena *arena) {
   struct google_protobuf_SourceCodeInfo_Location* sub = (struct google_protobuf_SourceCodeInfo_Location*)_upb_msg_new(&google_protobuf_SourceCodeInfo_Location_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1602,20 +1603,20 @@
   return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 40), len);
 }
 UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_resize_path(google_protobuf_SourceCodeInfo_Location *msg, size_t len, upb_arena *arena) {
-  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_TYPE_INT32, arena);
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, 2, arena);
 }
 UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_add_path(google_protobuf_SourceCodeInfo_Location *msg, int32_t val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(20, 40), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(20, 40), UPB_SIZE(4, 4), 2, &val,
       arena);
 }
 UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_mutable_span(google_protobuf_SourceCodeInfo_Location *msg, size_t *len) {
   return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 48), len);
 }
 UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_resize_span(google_protobuf_SourceCodeInfo_Location *msg, size_t len, upb_arena *arena) {
-  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_TYPE_INT32, arena);
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, 2, arena);
 }
 UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_add_span(google_protobuf_SourceCodeInfo_Location *msg, int32_t val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(24, 48), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(24, 48), UPB_SIZE(4, 4), 2, &val,
       arena);
 }
 UPB_INLINE void google_protobuf_SourceCodeInfo_Location_set_leading_comments(google_protobuf_SourceCodeInfo_Location *msg, upb_strview value) {
@@ -1630,10 +1631,10 @@
   return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 56), len);
 }
 UPB_INLINE upb_strview* google_protobuf_SourceCodeInfo_Location_resize_leading_detached_comments(google_protobuf_SourceCodeInfo_Location *msg, size_t len, upb_arena *arena) {
-  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(28, 56), len, UPB_TYPE_STRING, arena);
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(28, 56), len, UPB_SIZE(3, 4), arena);
 }
 UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_add_leading_detached_comments(google_protobuf_SourceCodeInfo_Location *msg, upb_strview val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(28, 56), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(28, 56), UPB_SIZE(8, 16), UPB_SIZE(3, 4), &val,
       arena);
 }
 
@@ -1658,12 +1659,12 @@
   return (google_protobuf_GeneratedCodeInfo_Annotation**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
 }
 UPB_INLINE google_protobuf_GeneratedCodeInfo_Annotation** google_protobuf_GeneratedCodeInfo_resize_annotation(google_protobuf_GeneratedCodeInfo *msg, size_t len, upb_arena *arena) {
-  return (google_protobuf_GeneratedCodeInfo_Annotation**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+  return (google_protobuf_GeneratedCodeInfo_Annotation**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_SIZE(2, 3), arena);
 }
 UPB_INLINE struct google_protobuf_GeneratedCodeInfo_Annotation* google_protobuf_GeneratedCodeInfo_add_annotation(google_protobuf_GeneratedCodeInfo *msg, upb_arena *arena) {
   struct google_protobuf_GeneratedCodeInfo_Annotation* sub = (struct google_protobuf_GeneratedCodeInfo_Annotation*)_upb_msg_new(&google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena);
   bool ok = _upb_array_append_accessor(
-      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_SIZE(2, 3), &sub, arena);
   if (!ok) return NULL;
   return sub;
 }
@@ -1694,10 +1695,10 @@
   return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 32), len);
 }
 UPB_INLINE int32_t* google_protobuf_GeneratedCodeInfo_Annotation_resize_path(google_protobuf_GeneratedCodeInfo_Annotation *msg, size_t len, upb_arena *arena) {
-  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(20, 32), len, UPB_TYPE_INT32, arena);
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(20, 32), len, 2, arena);
 }
 UPB_INLINE bool google_protobuf_GeneratedCodeInfo_Annotation_add_path(google_protobuf_GeneratedCodeInfo_Annotation *msg, int32_t val, upb_arena *arena) {
-  return _upb_array_append_accessor(msg, UPB_SIZE(20, 32), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+  return _upb_array_append_accessor(msg, UPB_SIZE(20, 32), UPB_SIZE(4, 4), 2, &val,
       arena);
 }
 UPB_INLINE void google_protobuf_GeneratedCodeInfo_Annotation_set_source_file(google_protobuf_GeneratedCodeInfo_Annotation *msg, upb_strview value) {
diff --git a/generated_for_cmake/upb/json/parser.c b/generated_for_cmake/upb/json/parser.c
index dd4fd82..7cdb4de 100644
--- a/generated_for_cmake/upb/json/parser.c
+++ b/generated_for_cmake/upb/json/parser.c
@@ -3295,7 +3295,7 @@
 
 static upb_json_parsermethod *parsermethod_new(upb_json_codecache *c,
                                                const upb_msgdef *md) {
-  upb_msg_field_iter i;
+  int i, n;
   upb_alloc *alloc = upb_arena_alloc(c->arena);
 
   upb_json_parsermethod *m = upb_malloc(alloc, sizeof(*m));
@@ -3310,10 +3310,9 @@
 
   /* Build name_table */
 
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
     upb_value v = upb_value_constptr(f);
     const char *name;
 
@@ -3402,7 +3401,7 @@
                                                     const upb_msgdef *md) {
   upb_json_parsermethod *m;
   upb_value v;
-  upb_msg_field_iter i;
+  int i, n;
   upb_alloc *alloc = upb_arena_alloc(c->arena);
 
   if (upb_inttable_lookupptr(&c->methods, md, &v)) {
@@ -3417,10 +3416,9 @@
 
   /* Populate parser methods for all submessages, so the name tables will
    * be available during parsing. */
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
 
     if (upb_fielddef_issubmsg(f)) {
       const upb_msgdef *subdef = upb_fielddef_msgsubdef(f);
diff --git a/tests/benchmark.cc b/tests/benchmark.cc
index 78dbeb6..ca6f640 100644
--- a/tests/benchmark.cc
+++ b/tests/benchmark.cc
@@ -7,8 +7,10 @@
 #include "google/protobuf/arena.h"
 #include "google/protobuf/descriptor.upb.h"
 #include "google/protobuf/descriptor.upbdefs.h"
+#include "google/protobuf/descriptor.pb.h"
 
 upb_strview descriptor = google_protobuf_descriptor_proto_upbdefinit.descriptor;
+namespace protobuf = ::google::protobuf;
 
 /* A buffer big enough to parse descriptor.proto without going to heap. */
 char buf[65535];
@@ -62,7 +64,6 @@
     }
     bytes += descriptor.size;
     upb_arena_free(arena);
-    //fprintf(stderr, "+++ finished parse: %zu\n", descriptor.size);
   }
   state.SetBytesProcessed(state.iterations() * descriptor.size);
 }
@@ -71,9 +72,9 @@
 static void BM_ParseDescriptor_Proto2_NoArena(benchmark::State& state) {
   size_t bytes = 0;
   for (auto _ : state) {
-    google::protobuf::FileDescriptorProto proto;
-    bool ok = proto.ParsePartialFromArray(descriptor.data, descriptor.size);
-
+    protobuf::FileDescriptorProto proto;
+    protobuf::StringPiece input(descriptor.data,descriptor.size);
+    bool ok = proto.ParseFrom<protobuf::MessageLite::kMergePartial>(input);
     if (!ok) {
       printf("Failed to parse.\n");
       exit(1);
@@ -87,10 +88,11 @@
 static void BM_ParseDescriptor_Proto2_Arena(benchmark::State& state) {
   size_t bytes = 0;
   for (auto _ : state) {
-    google::protobuf::Arena arena;
-    arena.Reset();
-    auto proto = google::protobuf::Arena::CreateMessage<google::protobuf::FileDescriptorProto>(&arena);
-    bool ok = proto->ParsePartialFromArray(descriptor.data, descriptor.size);
+    protobuf::Arena arena;
+    protobuf::StringPiece input(descriptor.data,descriptor.size);
+    auto proto = protobuf::Arena::CreateMessage<protobuf::FileDescriptorProto>(
+        &arena);
+    bool ok = proto->ParseFrom<protobuf::MessageLite::kMergePartial>(input);
 
     if (!ok) {
       printf("Failed to parse.\n");
@@ -104,15 +106,15 @@
 
 static void BM_ParseDescriptor_Proto2_Arena_LargeInitialBlock(benchmark::State& state) {
   size_t bytes = 0;
-  //fprintf(stderr, "size: %d\n", (int)descriptor.size);
-  google::protobuf::ArenaOptions options;
-  options.initial_block = buf;
-  options.initial_block_size = sizeof(buf);
+  protobuf::ArenaOptions opts;
+  opts.initial_block = buf;
+  opts.initial_block_size = sizeof(buf);
   for (auto _ : state) {
-    google::protobuf::Arena arena(options);
-    arena.Reset();
-    auto proto = google::protobuf::Arena::CreateMessage<google::protobuf::FileDescriptorProto>(&arena);
-    bool ok = proto->ParsePartialFromArray(descriptor.data, descriptor.size);
+    protobuf::Arena arena(opts);
+    protobuf::StringPiece input(descriptor.data,descriptor.size);
+    auto proto = protobuf::Arena::CreateMessage<protobuf::FileDescriptorProto>(
+        &arena);
+    bool ok = proto->ParseFrom<protobuf::MessageLite::kMergePartial>(input);
 
     if (!ok) {
       printf("Failed to parse.\n");
@@ -124,19 +126,19 @@
 }
 BENCHMARK(BM_ParseDescriptor_Proto2_Arena_LargeInitialBlock);
 
-static void BM_SerializeDescriptorProto2(benchmark::State& state) {
+static void BM_SerializeDescriptor_Proto2(benchmark::State& state) {
   size_t bytes = 0;
-  google::protobuf::FileDescriptorProto proto;
+  protobuf::FileDescriptorProto proto;
   proto.ParseFromArray(descriptor.data, descriptor.size);
   for (auto _ : state) {
-    proto.SerializeToArray(buf, sizeof(buf));
+    proto.SerializePartialToArray(buf, sizeof(buf));
     bytes += descriptor.size;
   }
   state.SetBytesProcessed(state.iterations() * descriptor.size);
 }
-BENCHMARK(BM_SerializeDescriptorProto2);
+BENCHMARK(BM_SerializeDescriptor_Proto2);
 
-static void BM_SerializeDescriptor(benchmark::State& state) {
+static void BM_SerializeDescriptor_Upb(benchmark::State& state) {
   int64_t total = 0;
   upb_arena* arena = upb_arena_new();
   google_protobuf_FileDescriptorProto* set =
@@ -158,4 +160,4 @@
   }
   state.SetBytesProcessed(total);
 }
-BENCHMARK(BM_SerializeDescriptor);
+BENCHMARK(BM_SerializeDescriptor_Upb);
diff --git a/tests/bindings/lua/test_upb.lua b/tests/bindings/lua/test_upb.lua
index 4586b34..87bdba6 100644
--- a/tests/bindings/lua/test_upb.lua
+++ b/tests/bindings/lua/test_upb.lua
@@ -37,6 +37,27 @@
   assert_nil(f:containing_oneof())
   assert_equal(m, f:containing_type())
   assert_equal(0, f:default())
+  local message_field_count = 0
+  for field in m:fields() do
+    message_field_count = message_field_count + 1
+  end
+  assert_equal(message_field_count, #m)
+
+  local message_oneof_count = 0
+  for oneof in m:oneofs() do
+    message_oneof_count = message_oneof_count + 1
+  end
+  assert_equal(message_oneof_count, m:oneof_count())
+
+  -- oneof
+  local o = m:lookup_name("oneof_field")
+  assert_equal("oneof_field", o:name())
+  assert_equal(m, o:containing_type())
+  local oneof_field_count = 0
+  for field in o:fields() do
+    oneof_field_count = oneof_field_count + 1
+  end
+  assert_equal(oneof_field_count, #o)
 
   -- enum
   local e = test_messages_proto3['TestAllTypesProto3.NestedEnum']
diff --git a/upb/bindings/lua/def.c b/upb/bindings/lua/def.c
index 6dc6b1a..0aa366d 100644
--- a/upb/bindings/lua/def.c
+++ b/upb/bindings/lua/def.c
@@ -159,10 +159,11 @@
 static int lupb_fielddef_number(lua_State *L) {
   const upb_fielddef *f = lupb_fielddef_check(L, 1);
   int32_t num = upb_fielddef_number(f);
-  if (num)
+  if (num) {
     lua_pushinteger(L, num);
-  else
+  } else {
     lua_pushnil(L);
+  }
   return 1;
 }
 
@@ -224,13 +225,54 @@
   return 1;
 }
 
-/* lupb_oneofdef_field()
+static int lupb_oneofdef_field(lua_State *L) {
+  const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+  int32_t idx = lupb_checkint32(L, 2);
+  int count = upb_oneofdef_fieldcount(o);
+
+  if (idx < 0 || idx >= count) {
+    const char *msg = lua_pushfstring(L, "index %d exceeds field count %d",
+                                      idx, count);
+    return luaL_argerror(L, 2, msg);
+  }
+
+  lupb_wrapper_pushwrapper(L, 1, upb_oneofdef_field(o, idx), LUPB_FIELDDEF);
+  return 1;
+}
+
+static int lupb_oneofiter_next(lua_State *L) {
+  const upb_oneofdef *o = lupb_oneofdef_check(L, lua_upvalueindex(1));
+  int *index = lua_touserdata(L, lua_upvalueindex(2));
+  const upb_fielddef *f;
+  if (*index == upb_oneofdef_fieldcount(o)) return 0;
+  f = upb_oneofdef_field(o, (*index)++);
+  lupb_wrapper_pushwrapper(L, lua_upvalueindex(1), f, LUPB_FIELDDEF);
+  return 1;
+}
+
+static int lupb_oneofdef_fields(lua_State *L) {
+  lupb_oneofdef_check(L, 1);
+  int *index = lua_newuserdata(L, sizeof(int));
+  *index = 0;
+
+  /* Closure upvalues are: oneofdef, index. */
+  lua_pushcclosure(L, &lupb_oneofiter_next, 2);
+  return 1;
+}
+
+static int lupb_oneofdef_len(lua_State *L) {
+  const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+  lua_pushinteger(L, upb_oneofdef_fieldcount(o));
+  return 1;
+}
+
+/* lupb_oneofdef_lookupfield()
  *
  * Handles:
- *   oneof.field(field_number)
- *   oneof.field(field_name)
+ *   oneof.lookup_field(field_number)
+ *   oneof.lookup_field(field_name)
  */
-static int lupb_oneofdef_field(lua_State *L) {
+static int lupb_oneofdef_lookupfield(lua_State *L) {
   const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
   const upb_fielddef *f;
 
@@ -252,33 +294,6 @@
   return 1;
 }
 
-static int lupb_oneofiter_next(lua_State *L) {
-  upb_oneof_iter *i = lua_touserdata(L, lua_upvalueindex(1));
-  const upb_fielddef *f;
-  if (upb_oneof_done(i)) return 0;
-  f = upb_oneof_iter_field(i);
-  upb_oneof_next(i);
-  lupb_symtab_pushwrapper(L, lua_upvalueindex(2), f, LUPB_FIELDDEF);
-  return 1;
-}
-
-static int lupb_oneofdef_fields(lua_State *L) {
-  const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
-  upb_oneof_iter *i = lua_newuserdata(L, sizeof(upb_oneof_iter));
-  lupb_wrapper_pushsymtab(L, 1);
-  upb_oneof_begin(i, o);
-
-  /* Closure upvalues are: iter, symtab. */
-  lua_pushcclosure(L, &lupb_oneofiter_next, 2);
-  return 1;
-}
-
-static int lupb_oneofdef_len(lua_State *L) {
-  const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
-  lua_pushinteger(L, upb_oneofdef_numfields(o));
-  return 1;
-}
-
 static int lupb_oneofdef_name(lua_State *L) {
   const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
   lua_pushstring(L, upb_oneofdef_name(o));
@@ -289,6 +304,7 @@
   {"containing_type", lupb_oneofdef_containingtype},
   {"field", lupb_oneofdef_field},
   {"fields", lupb_oneofdef_fields},
+  {"lookup_field", lupb_oneofdef_lookupfield},
   {"name", lupb_oneofdef_name},
   {NULL, NULL}
 };
@@ -309,9 +325,15 @@
   return lupb_wrapper_check(L, narg, LUPB_MSGDEF);
 }
 
-static int lupb_msgdef_len(lua_State *L) {
+static int lupb_msgdef_fieldcount(lua_State *L) {
   const upb_msgdef *m = lupb_msgdef_check(L, 1);
-  lua_pushinteger(L, upb_msgdef_numfields(m));
+  lua_pushinteger(L, upb_msgdef_fieldcount(m));
+  return 1;
+}
+
+static int lupb_msgdef_oneofcount(lua_State *L) {
+  const upb_msgdef *m = lupb_msgdef_check(L, 1);
+  lua_pushinteger(L, upb_msgdef_oneofcount(m));
   return 1;
 }
 
@@ -376,23 +398,21 @@
 }
 
 static int lupb_msgfielditer_next(lua_State *L) {
-  upb_msg_field_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+  const upb_msgdef *m = lupb_msgdef_check(L, lua_upvalueindex(1));
+  int *index = lua_touserdata(L, lua_upvalueindex(2));
   const upb_fielddef *f;
-
-  if (upb_msg_field_done(i)) return 0;
-  f = upb_msg_iter_field(i);
-  lupb_symtab_pushwrapper(L, lua_upvalueindex(2), f, LUPB_FIELDDEF);
-  upb_msg_field_next(i);
+  if (*index == upb_msgdef_fieldcount(m)) return 0;
+  f = upb_msgdef_field(m, (*index)++);
+  lupb_wrapper_pushwrapper(L, lua_upvalueindex(1), f, LUPB_FIELDDEF);
   return 1;
 }
 
 static int lupb_msgdef_fields(lua_State *L) {
-  const upb_msgdef *m = lupb_msgdef_check(L, 1);
-  upb_msg_field_iter *i = lua_newuserdata(L, sizeof(upb_msg_field_iter));
-  lupb_wrapper_pushsymtab(L, 1);
-  upb_msg_field_begin(i, m);
+  lupb_msgdef_check(L, 1);
+  int *index = lua_newuserdata(L, sizeof(int));
+  *index = 0;
 
-  /* Closure upvalues are: iter, symtab. */
+  /* Closure upvalues are: msgdef, index. */
   lua_pushcclosure(L, &lupb_msgfielditer_next, 2);
   return 1;
 }
@@ -411,22 +431,21 @@
 }
 
 static int lupb_msgoneofiter_next(lua_State *L) {
-  upb_msg_oneof_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+  const upb_msgdef *m = lupb_msgdef_check(L, lua_upvalueindex(1));
+  int *index = lua_touserdata(L, lua_upvalueindex(2));
   const upb_oneofdef *o;
-  if (upb_msg_oneof_done(i)) return 0;
-  o = upb_msg_iter_oneof(i);
-  upb_msg_oneof_next(i);
-  lupb_symtab_pushwrapper(L, lua_upvalueindex(2), o, LUPB_ONEOFDEF);
+  if (*index == upb_msgdef_oneofcount(m)) return 0;
+  o = upb_msgdef_oneof(m, (*index)++);
+  lupb_wrapper_pushwrapper(L, lua_upvalueindex(1), o, LUPB_ONEOFDEF);
   return 1;
 }
 
 static int lupb_msgdef_oneofs(lua_State *L) {
-  const upb_msgdef *m = lupb_msgdef_check(L, 1);
-  upb_msg_oneof_iter *i = lua_newuserdata(L, sizeof(upb_msg_oneof_iter));
-  lupb_wrapper_pushsymtab(L, 1);
-  upb_msg_oneof_begin(i, m);
+  lupb_msgdef_check(L, 1);
+  int *index = lua_newuserdata(L, sizeof(int));
+  *index = 0;
 
-  /* Closure upvalues are: iter, symtab. */
+  /* Closure upvalues are: msgdef, index. */
   lua_pushcclosure(L, &lupb_msgoneofiter_next, 2);
   return 1;
 }
@@ -452,7 +471,7 @@
 
 static const struct luaL_Reg lupb_msgdef_mm[] = {
   {"__call", lupb_msg_pushnew},
-  {"__len", lupb_msgdef_len},
+  {"__len", lupb_msgdef_fieldcount},
   {"__tostring", lupb_msgdef_tostring},
   {NULL, NULL}
 };
@@ -460,10 +479,12 @@
 static const struct luaL_Reg lupb_msgdef_m[] = {
   {"field", lupb_msgdef_field},
   {"fields", lupb_msgdef_fields},
+  {"field_count", lupb_msgdef_fieldcount},
   {"file", lupb_msgdef_file},
   {"full_name", lupb_msgdef_fullname},
   {"lookup_name", lupb_msgdef_lookupname},
   {"name", lupb_msgdef_name},
+  {"oneof_count", lupb_msgdef_oneofcount},
   {"oneofs", lupb_msgdef_oneofs},
   {"syntax", lupb_msgdef_syntax},
   {"_map_entry", lupb_msgdef_mapentry},
diff --git a/upb/decode.c b/upb/decode.c
index 8383cef..3b00f91 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -1,33 +1,37 @@
 
+#include "upb/decode.h"
+
 #include <setjmp.h>
 #include <string.h>
 
-#include "upb/decode.h"
+#include "upb/decode.int.h"
 #include "upb/upb.h"
+#include "upb/upb.int.h"
 
+/* Must be last. */
 #include "upb/port_def.inc"
 
-/* Maps descriptor type -> upb field type.  */
-static const uint8_t desctype_to_fieldtype[] = {
+/* Maps descriptor type -> elem_size_lg2.  */
+static const uint8_t desctype_to_elem_size_lg2[] = {
     -1,               /* invalid descriptor type */
-    UPB_TYPE_DOUBLE,  /* DOUBLE */
-    UPB_TYPE_FLOAT,   /* FLOAT */
-    UPB_TYPE_INT64,   /* INT64 */
-    UPB_TYPE_UINT64,  /* UINT64 */
-    UPB_TYPE_INT32,   /* INT32 */
-    UPB_TYPE_UINT64,  /* FIXED64 */
-    UPB_TYPE_UINT32,  /* FIXED32 */
-    UPB_TYPE_BOOL,    /* BOOL */
-    UPB_TYPE_STRING,  /* STRING */
-    UPB_TYPE_MESSAGE, /* GROUP */
-    UPB_TYPE_MESSAGE, /* MESSAGE */
-    UPB_TYPE_BYTES,   /* BYTES */
-    UPB_TYPE_UINT32,  /* UINT32 */
-    UPB_TYPE_ENUM,    /* ENUM */
-    UPB_TYPE_INT32,   /* SFIXED32 */
-    UPB_TYPE_INT64,   /* SFIXED64 */
-    UPB_TYPE_INT32,   /* SINT32 */
-    UPB_TYPE_INT64,   /* SINT64 */
+    3,  /* DOUBLE */
+    2,   /* FLOAT */
+    3,   /* INT64 */
+    3,  /* UINT64 */
+    2,   /* INT32 */
+    3,  /* FIXED64 */
+    2,  /* FIXED32 */
+    0,    /* BOOL */
+    UPB_SIZE(3, 4),  /* STRING */
+    UPB_SIZE(2, 3),  /* GROUP */
+    UPB_SIZE(2, 3),  /* MESSAGE */
+    UPB_SIZE(3, 4),  /* BYTES */
+    2,  /* UINT32 */
+    2,    /* ENUM */
+    2,   /* SFIXED32 */
+    3,   /* SFIXED64 */
+    2,   /* SINT32 */
+    3,   /* SINT64 */
 };
 
 /* Maps descriptor type -> upb map size.  */
@@ -185,75 +189,12 @@
   if (i != len) decode_err(d);
 }
 
-static void decode_stealmem(upb_decstate *d) {
-  _upb_arena_head *a = (_upb_arena_head*)d->arena;
-  d->arena_ptr = a->ptr;
-  d->arena_end = a->end;
-  a->ptr = a->end;
-}
-
-static void decode_donatemem(upb_decstate *d) {
-  _upb_arena_head *a = (_upb_arena_head*)d->arena;
-  UPB_ASSERT(a->end == d->arena_end);
-  a->ptr = d->arena_ptr;
-}
-
-UPB_NOINLINE
-void *decode_mallocfallback(upb_decstate *d, size_t size) {
-  char *ptr = _upb_arena_slowmalloc(d->arena, size);
-  if (!ptr) decode_err(d);
-  decode_stealmem(d);
-  return ptr;
-}
-
-UPB_NOINLINE
-static void decode_realloc(upb_decstate *d, upb_array *arr, size_t need_elem) {
-  decode_donatemem(d);
-  bool ok = _upb_array_realloc(arr, arr->len + need_elem, d->arena);
-  decode_stealmem(d);
-  if (!ok) decode_err(d);
-}
-
-UPB_FORCEINLINE
-static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t need_elem) {
-  if (arr->size - arr->len < need_elem) {
-    decode_realloc(d, arr, need_elem);
-    return true;
-  }
-  return false;
-}
-
-static upb_array *decode_newarr(upb_decstate *d, upb_fieldtype_t type) {
-  size_t elem_size_lg2 = _upb_fieldtype_to_sizelg2[type];
-  size_t count = type == UPB_TYPE_BOOL ? 8 : 4;
-  size_t size = sizeof(upb_array) + (count * (1 << elem_size_lg2));
-  upb_array *arr = decode_malloc(d, size);
-
-  if (!arr) {
+static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) {
+  bool need_realloc = arr->size - arr->len < elem;
+  if (need_realloc && !_upb_array_realloc(arr, arr->len + elem, &d->arena)) {
     decode_err(d);
   }
-
-  arr->data = _upb_array_tagptr(arr + 1, elem_size_lg2);
-  arr->len = 0;
-  arr->size = count;
-
-  return arr;
-}
-
-static void decode_addunknown(upb_decstate *d, upb_msg *msg, const char *ptr,
-                              size_t len) {
-  upb_msg_internal *in = upb_msg_getinternal(msg);
-  if (!in->unknown || in->unknown->size - in->unknown->len < len) {
-    bool ok;
-    decode_donatemem(d);
-    ok = _upb_msg_addunknown(msg, ptr, len, d->arena);
-    decode_stealmem(d);
-    if (!ok) decode_err(d);
-  } else {
-    char *dst = UPB_PTR_AT(in->unknown + 1, in->unknown->len, char);
-    memcpy(dst, ptr, len);
-    in->unknown->len += len;
-  }
+  return need_realloc;
 }
 
 UPB_NOINLINE
@@ -339,7 +280,8 @@
 
 static upb_msg *decode_newsubmsg(upb_decstate *d, const upb_msglayout *layout,
                                  const upb_msglayout_field *field) {
-  return decode_newmsg(d, layout->submsgs[field->submsg_index]);
+  const upb_msglayout *subl = layout->submsgs[field->submsg_index];
+  return _upb_msg_new_inl(subl, &d->arena);
 }
 
 static void decode_tosubmsg(upb_decstate *d, upb_msg *submsg,
@@ -384,14 +326,15 @@
   upb_array *arr = *arrp;
   void *mem;
 
-  if (!arr) {
-    upb_fieldtype_t type = desctype_to_fieldtype[field->descriptortype];
-    arr = decode_newarr(d, type);
+  if (arr) {
+    decode_reserve(d, arr, 1);
+  } else {
+    size_t lg2 = desctype_to_elem_size_lg2[field->descriptortype];
+    arr = _upb_array_new(&d->arena, 4, lg2);
+    if (!arr) decode_err(d);
     *arrp = arr;
   }
 
-  decode_reserve(d, arr, 1);
-
   switch (op) {
     case OP_SCALAR_LG2(0):
     case OP_SCALAR_LG2(2):
@@ -484,9 +427,7 @@
     char val_size = desctype_to_mapsize[val_field->descriptortype];
     UPB_ASSERT(key_field->offset == 0);
     UPB_ASSERT(val_field->offset == sizeof(upb_strview));
-    decode_donatemem(d);  /* We'll let map use the actual arena. */
-    map = _upb_map_new(d->arena, key_size, val_size);
-    decode_stealmem(d);
+    map = _upb_map_new(&d->arena, key_size, val_size);
     *map_p = map;
   }
 
@@ -496,15 +437,13 @@
   if (entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
       entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_GROUP) {
     /* Create proactively to handle the case where it doesn't appear. */
-    ent.v.val = upb_value_ptr(decode_newmsg(d, entry->submsgs[0]));
+    ent.v.val = upb_value_ptr(_upb_msg_new(entry->submsgs[0], &d->arena));
   }
 
   decode_tosubmsg(d, &ent.k, layout, field, val.str_val);
 
   /* Insert into map. */
-  decode_donatemem(d);  /* We'll let map use the actual arena. */
-  _upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, d->arena);
-  decode_stealmem(d);
+  _upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, &d->arena);
 }
 
 UPB_FORCEINLINE
@@ -660,7 +599,10 @@
       ptr = decode_group(d, ptr, NULL, NULL, field_number);
     }
     if (msg) {
-      decode_addunknown(d, msg, field_start, ptr - field_start);
+      if (!_upb_msg_addunknown(msg, field_start, ptr - field_start,
+                               &d->arena)) {
+        decode_err(d);
+      }
     }
   }
 
@@ -700,33 +642,32 @@
 
 bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l,
                 upb_arena *arena) {
-  bool ret;
+  bool ok;
   upb_decstate state;
 
-  state.limit = buf + size;
-  state.fastend = buf + size - 16;
-  state.fastlimit = state.fastend;
-  state.arena = arena;
-  state.depth = 64;
-  state.end_group = 0;
-
   if (size == 0) return true;
 
-  decode_stealmem(&state);
+  state.limit = buf + size;
+  state.depth = 64;
+  state.end_group = 0;
+  state.arena.head = arena->head;
+  state.arena.last_size = arena->last_size;
+  state.arena.parent = arena;
 
 #ifdef __APPLE__
-  if (_setjmp(state.err)) {
+  if (UPB_UNLIKELY(_setjmp(state.err))) {
 #else
-  if (setjmp(state.err)) {
+  if (UPB_UNLIKELY(setjmp(state.err))) {
 #endif
-    ret = false;
+    ok = false;
   } else {
     decode_msg(&state, buf, msg, l);
-    ret = state.end_group == 0;
+    ok = state.end_group == 0;
   }
 
-  decode_donatemem(&state);
-  return ret;
+  arena->head.ptr = state.arena.head.ptr;
+  arena->head.end = state.arena.head.end;
+  return ok;
 }
 
 #undef OP_SCALAR_LG2
diff --git a/upb/decode.h b/upb/decode.h
index 62a080f..a13e39b 100644
--- a/upb/decode.h
+++ b/upb/decode.h
@@ -18,119 +18,6 @@
 bool upb_decode(const char *buf, size_t size, upb_msg *msg,
                 const upb_msglayout *l, upb_arena *arena);
 
-/* Internal only: data pertaining to the parse. */
-typedef struct upb_decstate {
-  const char *limit;       /* End of delimited region or end of buffer. */
-  const char *fastlimit;   /* End of delimited region or end of buffer. */
-  const char *fastend;
-  char *arena_ptr;
-  char *arena_end;
-  upb_array *arr;
-  upb_arena *arena;
-  int depth;
-  uint32_t end_group; /* Set to field number of END_GROUP tag, if any. */
-  jmp_buf err;
-} upb_decstate;
-
-const char *fastdecode_dispatch(upb_decstate *d, const char *ptr, upb_msg *msg,
-                                const upb_msglayout *table, uint64_t hasbits);
-const char *fastdecode_generic(upb_decstate *d, const char *ptr, upb_msg *msg,
-                               const upb_msglayout *table, uint64_t hasbits,
-                               uint64_t data);
-const char *fastdecode_err(upb_decstate *d);
-
-void *decode_mallocfallback(upb_decstate *d, size_t size);
-
-UPB_INLINE bool decode_arenahas(upb_decstate *d, size_t bytes) {
-  return (size_t)(d->arena_end - d->arena_ptr) >= bytes;
-}
-
-UPB_INLINE void *decode_malloc(upb_decstate *d, size_t size) {
-  char *ptr = d->arena_ptr;
-  UPB_ASSERT((size & 7) == 0);
-  if (UPB_UNLIKELY(!decode_arenahas(d, size))) {
-    return decode_mallocfallback(d, size);
-  }
-  d->arena_ptr += size;
-  return ptr;
-}
-
-UPB_INLINE
-upb_msg *decode_newmsg_ceil(upb_decstate *d, const upb_msglayout *l,
-                            int msg_ceil_bytes) {
-  size_t size = l->size + sizeof(upb_msg_internal);
-  char *msg_data;
-  if (UPB_LIKELY(msg_ceil_bytes > 0 && decode_arenahas(d, msg_ceil_bytes))) {
-    UPB_ASSERT(size <= (size_t)msg_ceil_bytes);
-    msg_data = d->arena_ptr;
-    memset(msg_data, 0, msg_ceil_bytes);
-    d->arena_ptr += size;
-  } else {
-    msg_data = (char*)decode_malloc(d, size);
-    memset(msg_data, 0, size);
-  }
-  return msg_data + sizeof(upb_msg_internal);
-}
-
-UPB_INLINE
-upb_msg *decode_newmsg(upb_decstate *d, const upb_msglayout *l) {
-  return decode_newmsg_ceil(d, l, -1);
-}
-
-#define UPB_PARSE_PARAMS                                                      \
-  upb_decstate *d, const char *ptr, upb_msg *msg, const upb_msglayout *table, \
-      uint64_t hasbits, uint64_t data
-
-#define F(card, type, valbytes, tagbytes) \
-  const char *upb_p##card##type##valbytes##_##tagbytes##bt(UPB_PARSE_PARAMS);
-
-#define TYPES(card, tagbytes) \
-  F(card, b, 1, tagbytes)     \
-  F(card, v, 4, tagbytes)     \
-  F(card, v, 8, tagbytes)     \
-  F(card, z, 4, tagbytes)     \
-  F(card, z, 8, tagbytes)
-
-#define TAGBYTES(card) \
-  TYPES(card, 1)       \
-  TYPES(card, 2)
-
-TAGBYTES(s)
-TAGBYTES(o)
-/* TAGBYTES(r) */
-
-const char *upb_pss_1bt(UPB_PARSE_PARAMS);
-const char *upb_pss_2bt(UPB_PARSE_PARAMS);
-const char *upb_pos_1bt(UPB_PARSE_PARAMS);
-const char *upb_pos_2bt(UPB_PARSE_PARAMS);
-
-#undef F
-#undef TYPES
-#undef TAGBYTES
-
-#define F(card, tagbytes, size_ceil, ceil_arg) \
-  const char *upb_p##card##m_##tagbytes##bt_max##size_ceil##b(UPB_PARSE_PARAMS);
-
-#define SIZES(card, tagbytes) \
-  F(card, tagbytes, 64, 64) \
-  F(card, tagbytes, 128, 128) \
-  F(card, tagbytes, 192, 192) \
-  F(card, tagbytes, 256, 256) \
-  F(card, tagbytes, max, -1)
-
-#define TAGBYTES(card) \
-  SIZES(card, 1) \
-  SIZES(card, 2)
-
-TAGBYTES(s)
-TAGBYTES(o)
-TAGBYTES(r)
-
-#undef TAGBYTES
-#undef SIZES
-#undef F
-
-#undef UPB_PARSE_PARAMS
 
 #ifdef __cplusplus
 }  /* extern "C" */
diff --git a/upb/decode_fast.c b/upb/decode_fast.c
index 4af0d57..fd87b1d 100644
--- a/upb/decode_fast.c
+++ b/upb/decode_fast.c
@@ -1,6 +1,10 @@
 
-#include "upb/decode.h"
+#include "upb/decode_fast.h"
 
+#include "upb/decode.h"
+#include "upb/decode.int.h"
+
+/* Must be last. */
 #include "upb/port_def.inc"
 
 #define UPB_PARSE_PARAMS                                                      \
@@ -86,16 +90,16 @@
       if (UPB_LIKELY(!*arr_p)) {
         const size_t initial_len = 8;
         size_t need = (valbytes * initial_len) + sizeof(upb_array);
-        if (UPB_UNLIKELY((size_t)(d->arena_end - d->arena_ptr) < need)) {
+        if (UPB_UNLIKELY(!decode_arenahas(d, need))) {
           return NULL;
         }
-        arr = (void*)d->arena_ptr;
+        arr = (void*)d->arena.head.ptr;
         field = arr + 1;
         arr->data = _upb_array_tagptr(field, elem_size_lg2);
         *arr_p = arr;
         arr->size = initial_len;
         *end = (char*)field + (arr->size * valbytes);
-        d->arena_ptr += need;
+        d->arena.head.ptr += need;
       } else {
         arr = *arr_p;
         field = _upb_array_ptr(arr);
@@ -314,7 +318,7 @@
     *(uint32_t*)msg |= hasbits >> 16;
     hasbits = 0;
   }
-  
+
   const char *saved_limit = d->limit;
   const char *saved_fastlimit = d->fastlimit;
 
@@ -326,26 +330,26 @@
       size_t new_size = old_size * 2;
       size_t new_bytes = new_size * sizeof(upb_msg*);
       char *old_ptr = _upb_array_ptr(arr);
-      if (UPB_UNLIKELY((size_t)(d->arena_end - d->arena_ptr) < new_bytes)) {
+      if (UPB_UNLIKELY(!decode_arenahas(d, new_bytes))) {
         goto repeated_generic;
       }
-      memcpy(d->arena_ptr, old_ptr, old_bytes);
+      memcpy(d->arena.head.ptr, old_ptr, old_bytes);
       arr->size = new_size;
-      arr->data = _upb_array_tagptr(d->arena_ptr, 3);
-      submsg = (void*)(d->arena_ptr + (old_size * sizeof(upb_msg*)));
-      end = (void*)(d->arena_ptr + (new_size * sizeof(upb_msg*)));
-      d->arena_ptr += new_bytes;
+      arr->data = _upb_array_tagptr(d->arena.head.ptr, 3);
+      submsg = (void*)(d->arena.head.ptr + (old_size * sizeof(upb_msg*)));
+      end = (void*)(d->arena.head.ptr + (new_size * sizeof(upb_msg*)));
+      d->arena.head.ptr += new_bytes;
     }
   }
-  
+
   upb_msg* child = *submsg;
 
   if (card == CARD_r || UPB_LIKELY(!child)) {
     *submsg = child = decode_newmsg_ceil(d, subl, msg_ceil_bytes);
   }
-  
+
   ptr += tagbytes + 1;
-  
+
   ptr = fastdecode_lendelim_submsg(d, ptr, child, subl, 0, saved_limit);
 
   if (UPB_UNLIKELY(ptr != d->limit || d->end_group != 0)) {
@@ -383,7 +387,7 @@
   d->depth++;
 
   return fastdecode_dispatch(d, ptr, msg, table, hasbits);
-  
+
 repeated_generic:
   arr->len = submsg - (upb_msg**)_upb_array_ptr(arr);
   d->limit = saved_limit;
diff --git a/upb/def.c b/upb/def.c
index c5225a1..65c9e02 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -89,7 +89,9 @@
 struct upb_oneofdef {
   const upb_msgdef *parent;
   const char *full_name;
-  uint32_t index;
+  int field_count;
+  bool synthetic;
+  const upb_fielddef **fields;
   upb_strtable ntof;
   upb_inttable itof;
 };
@@ -293,37 +295,6 @@
   return true;
 }
 
-static bool check_oneofs(upb_msgdef *m, upb_status *s) {
-  int i;
-  int first_synthetic = -1;
-  upb_oneofdef *mutable_oneofs = (upb_oneofdef*)m->oneofs;
-
-  for (i = 0; i < m->oneof_count; i++) {
-    mutable_oneofs[i].index = i;
-
-    if (upb_oneofdef_issynthetic(&mutable_oneofs[i])) {
-      if (first_synthetic == -1) {
-        first_synthetic = i;
-      }
-    } else {
-      if (first_synthetic != -1) {
-        upb_status_seterrf(
-            s, "Synthetic oneofs must be after all other oneofs: %s",
-            upb_oneofdef_name(&mutable_oneofs[i]));
-        return false;
-      }
-    }
-  }
-
-  if (first_synthetic == -1) {
-    m->real_oneof_count = m->oneof_count;
-  } else {
-    m->real_oneof_count = first_synthetic;
-  }
-
-  return true;
-}
-
 static void assign_msg_wellknowntype(upb_msgdef *m) {
   const char *name = upb_msgdef_fullname(m);
   if (name == NULL) {
@@ -726,15 +697,32 @@
   return m->real_oneof_count;
 }
 
+int upb_msgdef_fieldcount(const upb_msgdef *m) {
+  return m->field_count;
+}
+
+int upb_msgdef_oneofcount(const upb_msgdef *m) {
+  return m->oneof_count;
+}
+
+int upb_msgdef_realoneofcount(const upb_msgdef *m) {
+  return m->real_oneof_count;
+}
+
 const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m) {
   return m->layout;
 }
 
-const upb_fielddef *_upb_msgdef_field(const upb_msgdef *m, int i) {
-  if (i >= m->field_count) return NULL;
+const upb_fielddef *upb_msgdef_field(const upb_msgdef *m, int i) {
+  UPB_ASSERT(i >= 0 && i < m->field_count);
   return &m->fields[i];
 }
 
+const upb_oneofdef *upb_msgdef_oneof(const upb_msgdef *m, int i) {
+  UPB_ASSERT(i >= 0 && i < m->oneof_count);
+  return &m->oneofs[i];
+}
+
 bool upb_msgdef_mapentry(const upb_msgdef *m) {
   return m->map_entry;
 }
@@ -822,22 +810,25 @@
   return o->parent;
 }
 
+int upb_oneofdef_fieldcount(const upb_oneofdef *o) {
+  return o->field_count;
+}
+
+const upb_fielddef *upb_oneofdef_field(const upb_oneofdef *o, int i) {
+  UPB_ASSERT(i < o->field_count);
+  return o->fields[i];
+}
+
 int upb_oneofdef_numfields(const upb_oneofdef *o) {
-  return (int)upb_strtable_count(&o->ntof);
+  return o->field_count;
 }
 
 uint32_t upb_oneofdef_index(const upb_oneofdef *o) {
-  return o->index;
+  return o - o->parent->oneofs;
 }
 
 bool upb_oneofdef_issynthetic(const upb_oneofdef *o) {
-  upb_inttable_iter iter;
-  const upb_fielddef *f;
-  upb_inttable_begin(&iter, &o->itof);
-  if (upb_oneofdef_numfields(o) != 1) return false;
-  f = upb_value_getptr(upb_inttable_iter_value(&iter));
-  UPB_ASSERT(f);
-  return f->proto3_optional_;
+  return o->synthetic;
 }
 
 const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o,
@@ -1159,6 +1150,46 @@
   }
 }
 
+static bool finalize_oneofs(symtab_addctx *ctx, upb_msgdef *m) {
+  int i;
+  int synthetic_count = 0;
+  upb_oneofdef *mutable_oneofs = (upb_oneofdef*)m->oneofs;
+
+  for (i = 0; i < m->oneof_count; i++) {
+    upb_oneofdef *o = &mutable_oneofs[i];
+
+    if (o->synthetic && o->field_count != 1) {
+      upb_status_seterrf(
+          ctx->status, "Synthetic oneofs must have one field, not %d: %s",
+          o->field_count, upb_oneofdef_name(o));
+      return false;
+    }
+
+    if (o->synthetic) {
+      synthetic_count++;
+    } else if (synthetic_count != 0) {
+      upb_status_seterrf(
+          ctx->status, "Synthetic oneofs must be after all other oneofs: %s",
+          upb_oneofdef_name(o));
+      return false;
+    }
+
+    o->fields = upb_malloc(ctx->alloc, sizeof(upb_fielddef*) * o->field_count);
+    o->field_count = 0;
+  }
+
+  for (i = 0; i < m->field_count; i++) {
+    const upb_fielddef *f = &m->fields[i];
+    upb_oneofdef *o = (upb_oneofdef*)f->oneof;
+    if (o) {
+      o->fields[o->field_count++] = f;
+    }
+  }
+
+  m->real_oneof_count = m->oneof_count - synthetic_count;
+  return true;
+}
+
 size_t getjsonname(const char *name, char *buf, size_t len) {
   size_t src, dst = 0;
   bool ucase_next = false;
@@ -1275,6 +1306,8 @@
   o = (upb_oneofdef*)&m->oneofs[m->oneof_count++];
   o->parent = m;
   o->full_name = makefullname(ctx, m->full_name, name);
+  o->field_count = 0;
+  o->synthetic = false;
 
   v = pack_def(o, UPB_DEFTYPE_ONEOF);
   CHK_OOM(symtab_add(ctx, o->full_name, v));
@@ -1554,10 +1587,20 @@
     oneof = (upb_oneofdef*)&m->oneofs[oneof_index];
     f->oneof = oneof;
 
+    oneof->field_count++;
+    if (f->proto3_optional_) {
+      oneof->synthetic = true;
+    }
     CHK(upb_inttable_insert2(&oneof->itof, f->number_, v, alloc));
     CHK(upb_strtable_insert3(&oneof->ntof, name.data, name.size, v, alloc));
   } else {
     f->oneof = NULL;
+    if (f->proto3_optional_) {
+      upb_status_seterrf(ctx->status,
+                         "field with proto3_optional was not in a oneof (%s)",
+                         f->full_name);
+      return false;
+    }
   }
 
   options = google_protobuf_FieldDescriptorProto_has_options(field_proto) ?
@@ -1697,7 +1740,7 @@
   }
 
   CHK(assign_msg_indices(m, ctx->status));
-  CHK(check_oneofs(m, ctx->status));
+  CHK(finalize_oneofs(ctx, m));
   assign_msg_wellknowntype(m);
   upb_inttable_compact2(&m->itof, ctx->alloc);
 
diff --git a/upb/def.h b/upb/def.h
index a7ce5d0..d88c287 100644
--- a/upb/def.h
+++ b/upb/def.h
@@ -117,9 +117,10 @@
 
 const char *upb_oneofdef_name(const upb_oneofdef *o);
 const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o);
-int upb_oneofdef_numfields(const upb_oneofdef *o);
 uint32_t upb_oneofdef_index(const upb_oneofdef *o);
 bool upb_oneofdef_issynthetic(const upb_oneofdef *o);
+int upb_oneofdef_fieldcount(const upb_oneofdef *o);
+const upb_fielddef *upb_oneofdef_field(const upb_oneofdef *o, int i);
 
 /* Oneof lookups:
  * - ntof:  look up a field by name.
@@ -133,11 +134,8 @@
 }
 const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num);
 
-/*  upb_oneof_iter i;
- *  for(upb_oneof_begin(&i, e); !upb_oneof_done(&i); upb_oneof_next(&i)) {
- *    // ...
- *  }
- */
+/* DEPRECATED, slated for removal. */
+int upb_oneofdef_numfields(const upb_oneofdef *o);
 void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o);
 void upb_oneof_next(upb_oneof_iter *iter);
 bool upb_oneof_done(upb_oneof_iter *iter);
@@ -145,6 +143,7 @@
 void upb_oneof_iter_setdone(upb_oneof_iter *iter);
 bool upb_oneof_iter_isequal(const upb_oneof_iter *iter1,
                             const upb_oneof_iter *iter2);
+/* END DEPRECATED */
 
 /* upb_msgdef *****************************************************************/
 
@@ -170,21 +169,21 @@
 const char *upb_msgdef_fullname(const upb_msgdef *m);
 const upb_filedef *upb_msgdef_file(const upb_msgdef *m);
 const char *upb_msgdef_name(const upb_msgdef *m);
-int upb_msgdef_numfields(const upb_msgdef *m);
-int upb_msgdef_numoneofs(const upb_msgdef *m);
-int upb_msgdef_numrealoneofs(const upb_msgdef *m);
 upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m);
 bool upb_msgdef_mapentry(const upb_msgdef *m);
 upb_wellknowntype_t upb_msgdef_wellknowntype(const upb_msgdef *m);
 bool upb_msgdef_iswrapper(const upb_msgdef *m);
 bool upb_msgdef_isnumberwrapper(const upb_msgdef *m);
+int upb_msgdef_fieldcount(const upb_msgdef *m);
+int upb_msgdef_oneofcount(const upb_msgdef *m);
+const upb_fielddef *upb_msgdef_field(const upb_msgdef *m, int i);
+const upb_oneofdef *upb_msgdef_oneof(const upb_msgdef *m, int i);
 const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i);
 const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name,
                                     size_t len);
 const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name,
                                     size_t len);
 const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m);
-const upb_fielddef *_upb_msgdef_field(const upb_msgdef *m, int i);
 
 UPB_INLINE const upb_oneofdef *upb_msgdef_ntooz(const upb_msgdef *m,
                                                const char *name) {
@@ -216,19 +215,10 @@
 const upb_fielddef *upb_msgdef_lookupjsonname(const upb_msgdef *m,
                                               const char *name, size_t len);
 
-/* Iteration over fields and oneofs.  For example:
- *
- * upb_msg_field_iter i;
- * for(upb_msg_field_begin(&i, m);
- *     !upb_msg_field_done(&i);
- *     upb_msg_field_next(&i)) {
- *   upb_fielddef *f = upb_msg_iter_field(&i);
- *   // ...
- * }
- *
- * For C we don't have separate iterators for const and non-const.
- * It is the caller's responsibility to cast the upb_fielddef* to
- * const if the upb_msgdef* is const. */
+/* DEPRECATED, slated for removal */
+int upb_msgdef_numfields(const upb_msgdef *m);
+int upb_msgdef_numoneofs(const upb_msgdef *m);
+int upb_msgdef_numrealoneofs(const upb_msgdef *m);
 void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m);
 void upb_msg_field_next(upb_msg_field_iter *iter);
 bool upb_msg_field_done(const upb_msg_field_iter *iter);
@@ -236,9 +226,6 @@
 void upb_msg_field_iter_setdone(upb_msg_field_iter *iter);
 bool upb_msg_field_iter_isequal(const upb_msg_field_iter * iter1,
                                 const upb_msg_field_iter * iter2);
-
-/* Similar to above, we also support iterating through the oneofs in a
- * msgdef. */
 void upb_msg_oneof_begin(upb_msg_oneof_iter * iter, const upb_msgdef *m);
 void upb_msg_oneof_next(upb_msg_oneof_iter * iter);
 bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter);
@@ -246,6 +233,7 @@
 void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter * iter);
 bool upb_msg_oneof_iter_isequal(const upb_msg_oneof_iter *iter1,
                                 const upb_msg_oneof_iter *iter2);
+/* END DEPRECATED */
 
 /* upb_enumdef ****************************************************************/
 
@@ -270,11 +258,6 @@
 }
 const char *upb_enumdef_iton(const upb_enumdef *e, int32_t num);
 
-/*  upb_enum_iter i;
- *  for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) {
- *    // ...
- *  }
- */
 void upb_enum_begin(upb_enum_iter *iter, const upb_enumdef *e);
 void upb_enum_next(upb_enum_iter *iter);
 bool upb_enum_done(upb_enum_iter *iter);
diff --git a/upb/def.hpp b/upb/def.hpp
index 62d06bb..e32da2a 100644
--- a/upb/def.hpp
+++ b/upb/def.hpp
@@ -129,17 +129,17 @@
   explicit OneofDefPtr(const upb_oneofdef* ptr) : ptr_(ptr) {}
 
   const upb_oneofdef* ptr() const { return ptr_; }
-  explicit operator bool() { return ptr_ != nullptr; }
+  explicit operator bool() const { return ptr_ != nullptr; }
 
-  // Returns the MessageDef that owns this OneofDef.
+  // Returns the MessageDef that contains this OneofDef.
   MessageDefPtr containing_type() const;
 
-  // Returns the name of this oneof. This is the name used to look up the oneof
-  // by name once added to a message def.
+  // Returns the name of this oneof.
   const char* name() const { return upb_oneofdef_name(ptr_); }
 
-  // Returns the number of fields currently defined in the oneof.
+  // Returns the number of fields in the oneof.
   int field_count() const { return upb_oneofdef_numfields(ptr_); }
+  FieldDefPtr field(int i) const { return FieldDefPtr(upb_oneofdef_field(ptr_, i)); }
 
   // Looks up by name.
   FieldDefPtr FindFieldByName(const char* name, size_t len) const {
@@ -159,40 +159,6 @@
     return FieldDefPtr(upb_oneofdef_itof(ptr_, num));
   }
 
-  class const_iterator
-      : public std::iterator<std::forward_iterator_tag, FieldDefPtr> {
-   public:
-    void operator++() { upb_oneof_next(&iter_); }
-
-    FieldDefPtr operator*() const {
-      return FieldDefPtr(upb_oneof_iter_field(&iter_));
-    }
-
-    bool operator!=(const const_iterator& other) const {
-      return !upb_oneof_iter_isequal(&iter_, &other.iter_);
-    }
-
-    bool operator==(const const_iterator& other) const {
-      return upb_oneof_iter_isequal(&iter_, &other.iter_);
-    }
-
-   private:
-    friend class OneofDefPtr;
-
-    const_iterator() {}
-    explicit const_iterator(OneofDefPtr o) { upb_oneof_begin(&iter_, o.ptr()); }
-    static const_iterator end() {
-      const_iterator iter;
-      upb_oneof_iter_setdone(&iter.iter_);
-      return iter;
-    }
-
-    upb_oneof_iter iter_;
-  };
-
-  const_iterator begin() const { return const_iterator(*this); }
-  const_iterator end() const { return const_iterator::end(); }
-
  private:
   const upb_oneofdef* ptr_;
 };
@@ -211,9 +177,11 @@
 
   // The number of fields that belong to the MessageDef.
   int field_count() const { return upb_msgdef_numfields(ptr_); }
+  FieldDefPtr field(int i) const { return FieldDefPtr(upb_msgdef_field(ptr_, i)); }
 
   // The number of oneofs that belong to the MessageDef.
   int oneof_count() const { return upb_msgdef_numoneofs(ptr_); }
+  OneofDefPtr oneof(int i) const { return OneofDefPtr(upb_msgdef_oneof(ptr_, i)); }
 
   upb_syntax_t syntax() const { return upb_msgdef_syntax(ptr_); }
 
@@ -258,112 +226,58 @@
   // Whether is a number wrapper.
   bool isnumberwrapper() const { return upb_msgdef_isnumberwrapper(ptr_); }
 
-  // Iteration over fields.  The order is undefined.
-  class const_field_iterator
-      : public std::iterator<std::forward_iterator_tag, FieldDefPtr> {
+ private:
+  class FieldIter {
    public:
-    void operator++() { upb_msg_field_next(&iter_); }
+    explicit FieldIter(const upb_msgdef *m, int i) : m_(m), i_(i) {}
+    void operator++() { i_++; }
 
-    FieldDefPtr operator*() const {
-      return FieldDefPtr(upb_msg_iter_field(&iter_));
-    }
-
-    bool operator!=(const const_field_iterator& other) const {
-      return !upb_msg_field_iter_isequal(&iter_, &other.iter_);
-    }
-
-    bool operator==(const const_field_iterator& other) const {
-      return upb_msg_field_iter_isequal(&iter_, &other.iter_);
-    }
+    FieldDefPtr operator*() { return FieldDefPtr(upb_msgdef_field(m_, i_)); }
+    bool operator!=(const FieldIter& other) { return i_ != other.i_; }
+    bool operator==(const FieldIter& other) { return i_ == other.i_; }
 
    private:
-    friend class MessageDefPtr;
-
-    explicit const_field_iterator() {}
-
-    explicit const_field_iterator(MessageDefPtr msg) {
-      upb_msg_field_begin(&iter_, msg.ptr());
-    }
-
-    static const_field_iterator end() {
-      const_field_iterator iter;
-      upb_msg_field_iter_setdone(&iter.iter_);
-      return iter;
-    }
-
-    upb_msg_field_iter iter_;
+    const upb_msgdef *m_;
+    int i_;
   };
 
-  // Iteration over oneofs. The order is undefined.
-  class const_oneof_iterator
-      : public std::iterator<std::forward_iterator_tag, OneofDefPtr> {
+  class FieldAccessor {
    public:
-    void operator++() { upb_msg_oneof_next(&iter_); }
-
-    OneofDefPtr operator*() const {
-      return OneofDefPtr(upb_msg_iter_oneof(&iter_));
-    }
-
-    bool operator!=(const const_oneof_iterator& other) const {
-      return !upb_msg_oneof_iter_isequal(&iter_, &other.iter_);
-    }
-
-    bool operator==(const const_oneof_iterator& other) const {
-      return upb_msg_oneof_iter_isequal(&iter_, &other.iter_);
-    }
-
-   private:
-    friend class MessageDefPtr;
-
-    const_oneof_iterator() {}
-
-    explicit const_oneof_iterator(MessageDefPtr msg) {
-      upb_msg_oneof_begin(&iter_, msg.ptr());
-    }
-
-    static const_oneof_iterator end() {
-      const_oneof_iterator iter;
-      upb_msg_oneof_iter_setdone(&iter.iter_);
-      return iter;
-    }
-
-    upb_msg_oneof_iter iter_;
-  };
-
-  class ConstFieldAccessor {
-   public:
-    explicit ConstFieldAccessor(const upb_msgdef* md) : md_(md) {}
-    const_field_iterator begin() { return MessageDefPtr(md_).field_begin(); }
-    const_field_iterator end() { return MessageDefPtr(md_).field_end(); }
+    explicit FieldAccessor(const upb_msgdef* md) : md_(md) {}
+    FieldIter begin() { return FieldIter(md_, 0); }
+    FieldIter end() { return FieldIter(md_, upb_msgdef_fieldcount(md_)); }
 
    private:
     const upb_msgdef* md_;
   };
 
-  class ConstOneofAccessor {
+  class OneofIter {
    public:
-    explicit ConstOneofAccessor(const upb_msgdef* md) : md_(md) {}
-    const_oneof_iterator begin() { return MessageDefPtr(md_).oneof_begin(); }
-    const_oneof_iterator end() { return MessageDefPtr(md_).oneof_end(); }
+    explicit OneofIter(const upb_msgdef *m, int i) : m_(m), i_(i) {}
+    void operator++() { i_++; }
+
+    OneofDefPtr operator*() { return OneofDefPtr(upb_msgdef_oneof(m_, i_)); }
+    bool operator!=(const OneofIter& other) { return i_ != other.i_; }
+    bool operator==(const OneofIter& other) { return i_ == other.i_; }
+
+   private:
+    const upb_msgdef *m_;
+    int i_;
+  };
+
+  class OneofAccessor {
+   public:
+    explicit OneofAccessor(const upb_msgdef* md) : md_(md) {}
+    OneofIter begin() { return OneofIter(md_, 0); }
+    OneofIter end() { return OneofIter(md_, upb_msgdef_oneofcount(md_)); }
 
    private:
     const upb_msgdef* md_;
   };
 
-  const_field_iterator field_begin() const {
-    return const_field_iterator(*this);
-  }
-
-  const_field_iterator field_end() const { return const_field_iterator::end(); }
-
-  const_oneof_iterator oneof_begin() const {
-    return const_oneof_iterator(*this);
-  }
-
-  const_oneof_iterator oneof_end() const { return const_oneof_iterator::end(); }
-
-  ConstFieldAccessor fields() const { return ConstFieldAccessor(ptr()); }
-  ConstOneofAccessor oneofs() const { return ConstOneofAccessor(ptr()); }
+ public:
+  FieldAccessor fields() const { return FieldAccessor(ptr()); }
+  OneofAccessor oneofs() const { return OneofAccessor(ptr()); }
 
  private:
   const upb_msgdef* ptr_;
diff --git a/upb/handlers.c b/upb/handlers.c
index b90b32c..4168a4f 100644
--- a/upb/handlers.c
+++ b/upb/handlers.c
@@ -359,7 +359,7 @@
 
 const upb_handlers *upb_handlercache_get(upb_handlercache *c,
                                          const upb_msgdef *md) {
-  upb_msg_field_iter i;
+  int i, n;
   upb_value v;
   upb_handlers *h;
 
@@ -377,10 +377,9 @@
 
   /* For each submessage field, get or create a handlers object and set it as
    * the subhandlers. */
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for (i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
 
     if (upb_fielddef_issubmsg(f)) {
       const upb_msgdef *subdef = upb_fielddef_msgsubdef(f);
diff --git a/upb/json/parser.rl b/upb/json/parser.rl
index f1ea076..d7dcc54 100644
--- a/upb/json/parser.rl
+++ b/upb/json/parser.rl
@@ -2858,7 +2858,7 @@
 
 static upb_json_parsermethod *parsermethod_new(upb_json_codecache *c,
                                                const upb_msgdef *md) {
-  upb_msg_field_iter i;
+  int i, n;
   upb_alloc *alloc = upb_arena_alloc(c->arena);
 
   upb_json_parsermethod *m = upb_malloc(alloc, sizeof(*m));
@@ -2873,10 +2873,9 @@
 
   /* Build name_table */
 
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
     upb_value v = upb_value_constptr(f);
     const char *name;
 
@@ -2965,7 +2964,7 @@
                                                     const upb_msgdef *md) {
   upb_json_parsermethod *m;
   upb_value v;
-  upb_msg_field_iter i;
+  int i, n;
   upb_alloc *alloc = upb_arena_alloc(c->arena);
 
   if (upb_inttable_lookupptr(&c->methods, md, &v)) {
@@ -2980,10 +2979,9 @@
 
   /* Populate parser methods for all submessages, so the name tables will
    * be available during parsing. */
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
 
     if (upb_fielddef_issubmsg(f)) {
       const upb_msgdef *subdef = upb_fielddef_msgsubdef(f);
diff --git a/upb/json/printer.c b/upb/json/printer.c
index 3a88219..b6d53a9 100644
--- a/upb/json/printer.c
+++ b/upb/json/printer.c
@@ -1124,16 +1124,16 @@
 
 void printer_sethandlers_value(const void *closure, upb_handlers *h) {
   const upb_msgdef *md = upb_handlers_msgdef(h);
-  upb_msg_field_iter i;
+  int i, n;
 
   upb_handlerattr empty_attr = UPB_HANDLERATTR_INIT;
 
   upb_handlers_setstartmsg(h, printer_startmsg_noframe, &empty_attr);
   upb_handlers_setendmsg(h, printer_endmsg_noframe, &empty_attr);
 
-  upb_msg_field_begin(&i, md);
-  for(; !upb_msg_field_done(&i); upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for (i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
 
     switch (upb_fielddef_type(f)) {
       case UPB_TYPE_ENUM:
@@ -1222,7 +1222,7 @@
   const upb_msgdef *md = upb_handlers_msgdef(h);
   bool is_mapentry = upb_msgdef_mapentry(md);
   upb_handlerattr empty_attr = UPB_HANDLERATTR_INIT;
-  upb_msg_field_iter i;
+  int i, n;
   const upb_json_printercache *cache = closure;
   const bool preserve_fieldnames = cache->preserve_fieldnames;
 
@@ -1287,9 +1287,9 @@
     }                                                                         \
     break;
 
-  upb_msg_field_begin(&i, md);
-  for(; !upb_msg_field_done(&i); upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for (i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
 
     upb_handlerattr name_attr = UPB_HANDLERATTR_INIT;
     name_attr.handler_data = newstrpc(h, f, preserve_fieldnames);
diff --git a/upb/json_encode.c b/upb/json_encode.c
index 6b6d99b..8338b4d 100644
--- a/upb/json_encode.c
+++ b/upb/json_encode.c
@@ -648,10 +648,10 @@
 
   if (e->options & UPB_JSONENC_EMITDEFAULTS) {
     /* Iterate over all fields. */
-    upb_msg_field_iter i;
-    for (upb_msg_field_begin(&i, m); !upb_msg_field_done(&i);
-         upb_msg_field_next(&i)) {
-      f = upb_msg_iter_field(&i);
+    int i = 0;
+    int n = upb_msgdef_fieldcount(m);
+    for (i = 0; i < n; i++) {
+      f = upb_msgdef_field(m, i);
       jsonenc_fieldval(e, f, upb_msg_get(msg, f), &first);
     }
   } else {
diff --git a/upb/msg.c b/upb/msg.c
index b9310c9..c878be9 100644
--- a/upb/msg.c
+++ b/upb/msg.c
@@ -7,48 +7,20 @@
 
 /** upb_msg *******************************************************************/
 
-const char _upb_fieldtype_to_sizelg2[12] = {
-  0,
-  0,  /* UPB_TYPE_BOOL */
-  2,  /* UPB_TYPE_FLOAT */
-  2,  /* UPB_TYPE_INT32 */
-  2,  /* UPB_TYPE_UINT32 */
-  2,  /* UPB_TYPE_ENUM */
-  UPB_SIZE(2, 3),  /* UPB_TYPE_MESSAGE */
-  3,  /* UPB_TYPE_DOUBLE */
-  3,  /* UPB_TYPE_INT64 */
-  3,  /* UPB_TYPE_UINT64 */
-  UPB_SIZE(3, 4),  /* UPB_TYPE_STRING */
-  UPB_SIZE(3, 4),  /* UPB_TYPE_BYTES */
-};
-
 static const size_t overhead = sizeof(upb_msg_internal);
 
-static size_t upb_msg_sizeof(const upb_msglayout *l) {
-  return l->size + overhead;
-}
-
 static const upb_msg_internal *upb_msg_getinternal_const(const upb_msg *msg) {
   ptrdiff_t size = sizeof(upb_msg_internal);
   return UPB_PTR_AT(msg, -size, upb_msg_internal);
 }
 
-void _upb_msg_clear(upb_msg *msg, const upb_msglayout *l) {
-  void *mem = UPB_PTR_AT(msg, -overhead, char);
-  memset(mem, 0, l->size + overhead);
+upb_msg *_upb_msg_new(const upb_msglayout *l, upb_arena *a) {
+  return _upb_msg_new_inl(l, a);
 }
 
-upb_msg *_upb_msg_new(const upb_msglayout *l, upb_arena *a) {
-  void *mem = upb_arena_malloc(a, upb_msg_sizeof(l));
-  upb_msg *msg;
-
-  if (!mem) {
-    return NULL;
-  }
-
-  msg = UPB_PTR_AT(mem, overhead, upb_msg);
-  _upb_msg_clear(msg, l);
-  return msg;
+void _upb_msg_clear(upb_msg *msg, const upb_msglayout *l) {
+  void *mem = UPB_PTR_AT(msg, -sizeof(upb_msg_internal), char);
+  memset(mem, 0, upb_msg_sizeof(l));
 }
 
 bool _upb_msg_addunknown(upb_msg *msg, const char *data, size_t len,
@@ -95,20 +67,6 @@
 
 /** upb_array *****************************************************************/
 
-upb_array *_upb_array_new(upb_arena *a, upb_fieldtype_t type) {
-  upb_array *arr = upb_arena_malloc(a, sizeof(upb_array));
-
-  if (!arr) {
-    return NULL;
-  }
-
-  arr->data = _upb_array_tagptr(NULL, _upb_fieldtype_to_sizelg2[type]);
-  arr->len = 0;
-  arr->size = 0;
-
-  return arr;
-}
-
 bool _upb_array_realloc(upb_array *arr, size_t min_size, upb_arena *arena) {
   size_t new_size = UPB_MAX(arr->size, 4);
   int elem_size_lg2 = arr->data & 7;
@@ -126,16 +84,16 @@
     return false;
   }
 
-  arr->data = _upb_array_tagptr(ptr, elem_size_lg2);
+  arr->data = _upb_tag_arrptr(ptr, elem_size_lg2);
   arr->size = new_size;
   return true;
 }
 
-static upb_array *getorcreate_array(upb_array **arr_ptr, upb_fieldtype_t type,
+static upb_array *getorcreate_array(upb_array **arr_ptr, int elem_size_lg2,
                                     upb_arena *arena) {
   upb_array *arr = *arr_ptr;
   if (!arr) {
-    arr = _upb_array_new(arena, type);
+    arr = _upb_array_new(arena, 4, elem_size_lg2);
     if (!arr) return NULL;
     *arr_ptr = arr;
   }
@@ -143,22 +101,22 @@
 }
 
 void *_upb_array_resize_fallback(upb_array **arr_ptr, size_t size,
-                                 upb_fieldtype_t type, upb_arena *arena) {
-  upb_array *arr = getorcreate_array(arr_ptr, type, arena);
-  return arr && _upb_array_resize(arr, size, arena) ? _upb_array_ptr(arr) : NULL;
+                                 int elem_size_lg2, upb_arena *arena) {
+  upb_array *arr = getorcreate_array(arr_ptr, elem_size_lg2, arena);
+  return arr && _upb_array_resize(arr, size, arena) ? _upb_array_ptr(arr)
+                                                    : NULL;
 }
 
 bool _upb_array_append_fallback(upb_array **arr_ptr, const void *value,
-                                upb_fieldtype_t type, upb_arena *arena) {
-  upb_array *arr = getorcreate_array(arr_ptr, type, arena);
+                                int elem_size_lg2, upb_arena *arena) {
+  upb_array *arr = getorcreate_array(arr_ptr, elem_size_lg2, arena);
   size_t elem = arr->len;
-  int lg2 = _upb_fieldtype_to_sizelg2[type];
   char *data;
 
   if (!arr || !_upb_array_resize(arr, elem + 1, arena)) return false;
 
   data = _upb_array_ptr(arr);
-  memcpy(data + (elem << lg2), value, 1 << lg2);
+  memcpy(data + (elem << elem_size_lg2), value, 1 << elem_size_lg2);
   return true;
 }
 
diff --git a/upb/msg.h b/upb/msg.h
index b132afe..1bc9342 100644
--- a/upb/msg.h
+++ b/upb/msg.h
@@ -85,7 +85,20 @@
 
 /* Maps upb_fieldtype_t -> memory size. */
 extern char _upb_fieldtype_to_size[12];
-extern const char _upb_fieldtype_to_sizelg2[12];
+
+UPB_INLINE size_t upb_msg_sizeof(const upb_msglayout *l) {
+  return l->size + sizeof(upb_msg_internal);
+}
+
+UPB_INLINE upb_msg *_upb_msg_new_inl(const upb_msglayout *l, upb_arena *a) {
+  size_t size = upb_msg_sizeof(l);
+  void *mem = upb_arena_malloc(a, size);
+  upb_msg *msg;
+  if (UPB_UNLIKELY(!mem)) return NULL;
+  msg = UPB_PTR_AT(mem, sizeof(upb_msg_internal), upb_msg);
+  memset(mem, 0, size);
+  return msg;
+}
 
 /* Creates a new messages with the given layout on the given arena. */
 upb_msg *_upb_msg_new(const upb_msglayout *l, upb_arena *a);
@@ -201,17 +214,30 @@
   return (void*)_upb_array_constptr(arr);
 }
 
-/* Creates a new array on the given arena. */
-upb_array *_upb_array_new(upb_arena *a, upb_fieldtype_t type);
+UPB_INLINE uintptr_t _upb_tag_arrptr(void* ptr, int elem_size_lg2) {
+  UPB_ASSERT(elem_size_lg2 <= 4);
+  return (uintptr_t)ptr | (unsigned)elem_size_lg2;
+}
+
+UPB_INLINE upb_array *_upb_array_new(upb_arena *a, size_t init_size,
+                                     int elem_size_lg2) {
+  const size_t bytes = sizeof(upb_array) + (init_size << elem_size_lg2);
+  upb_array *arr = (upb_array*)upb_arena_malloc(a, bytes);
+  if (!arr) return NULL;
+  arr->data = _upb_tag_arrptr(arr + 1, elem_size_lg2);
+  arr->len = 0;
+  arr->size = init_size;
+  return arr;
+}
 
 /* Resizes the capacity of the array to be at least min_size. */
 bool _upb_array_realloc(upb_array *arr, size_t min_size, upb_arena *arena);
 
 /* Fallback functions for when the accessors require a resize. */
 void *_upb_array_resize_fallback(upb_array **arr_ptr, size_t size,
-                                 upb_fieldtype_t type, upb_arena *arena);
+                                 int elem_size_lg2, upb_arena *arena);
 bool _upb_array_append_fallback(upb_array **arr_ptr, const void *value,
-                                upb_fieldtype_t type, upb_arena *arena);
+                                int elem_size_lg2, upb_arena *arena);
 
 UPB_INLINE bool _upb_array_reserve(upb_array *arr, size_t size,
                                    upb_arena *arena) {
@@ -251,28 +277,26 @@
 }
 
 UPB_INLINE void *_upb_array_resize_accessor(void *msg, size_t ofs, size_t size,
-                                            upb_fieldtype_t type,
+                                            int elem_size_lg2,
                                             upb_arena *arena) {
   upb_array **arr_ptr = PTR_AT(msg, ofs, upb_array*);
   upb_array *arr = *arr_ptr;
   if (!arr || arr->size < size) {
-    return _upb_array_resize_fallback(arr_ptr, size, type, arena);
+    return _upb_array_resize_fallback(arr_ptr, size, elem_size_lg2, arena);
   }
   arr->len = size;
   return _upb_array_ptr(arr);
 }
 
-
 UPB_INLINE bool _upb_array_append_accessor(void *msg, size_t ofs,
-                                           size_t elem_size,
-                                           upb_fieldtype_t type,
+                                           size_t elem_size, int elem_size_lg2,
                                            const void *value,
                                            upb_arena *arena) {
   upb_array **arr_ptr = PTR_AT(msg, ofs, upb_array*);
   upb_array *arr = *arr_ptr;
   void* ptr;
   if (!arr || arr->len == arr->size) {
-    return _upb_array_append_fallback(arr_ptr, value, type, arena);
+    return _upb_array_append_fallback(arr_ptr, value, elem_size_lg2, arena);
   }
   ptr = _upb_array_ptr(arr);
   memcpy(PTR_AT(ptr, arr->len * elem_size, char), value, elem_size);
diff --git a/upb/pb/compile_decoder.c b/upb/pb/compile_decoder.c
index 454397b..3c73f1a 100644
--- a/upb/pb/compile_decoder.c
+++ b/upb/pb/compile_decoder.c
@@ -701,7 +701,7 @@
   const upb_handlers *h;
   const upb_msgdef *md;
   uint32_t* start_pc;
-  upb_msg_field_iter i;
+  int i, n;
   upb_value val;
 
   UPB_ASSERT(method);
@@ -718,10 +718,9 @@
   putsel(c, OP_STARTMSG, UPB_STARTMSG_SELECTOR, h);
  label(c, LABEL_FIELD);
   start_pc = c->pc;
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
     upb_fieldtype_t type = upb_fielddef_type(f);
 
     if (type == UPB_TYPE_MESSAGE && !(haslazyhandlers(h, f) && c->lazy)) {
@@ -765,7 +764,7 @@
  * Generates a new method for every destination handlers reachable from "h". */
 static void find_methods(compiler *c, const upb_handlers *h) {
   upb_value v;
-  upb_msg_field_iter i;
+  int i, n;
   const upb_msgdef *md;
   upb_pbdecodermethod *method;
 
@@ -777,10 +776,9 @@
 
   /* Find submethods. */
   md = upb_handlers_msgdef(h);
-  for(upb_msg_field_begin(&i, md);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(md);
+  for (i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(md, i);
     const upb_handlers *sub_h;
     if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE &&
         (sub_h = upb_handlers_getsubhandlers(h, f)) != NULL) {
diff --git a/upb/pb/encoder.c b/upb/pb/encoder.c
index 779c214..0c47b0d 100644
--- a/upb/pb/encoder.c
+++ b/upb/pb/encoder.c
@@ -437,7 +437,7 @@
 #include <stdio.h>
 static void newhandlers_callback(const void *closure, upb_handlers *h) {
   const upb_msgdef *m;
-  upb_msg_field_iter i;
+  int i, n;
 
   UPB_UNUSED(closure);
 
@@ -446,10 +446,9 @@
   upb_handlers_setunknown(h, encode_unknown, NULL);
 
   m = upb_handlers_msgdef(h);
-  for(upb_msg_field_begin(&i, m);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    const upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(m);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(m, i);
     bool packed = upb_fielddef_isseq(f) && upb_fielddef_isprimitive(f) &&
                   upb_fielddef_packed(f);
     upb_handlerattr attr = UPB_HANDLERATTR_INIT;
diff --git a/upb/pb/textprinter.c b/upb/pb/textprinter.c
index 0760173..1d4acb1 100644
--- a/upb/pb/textprinter.c
+++ b/upb/pb/textprinter.c
@@ -252,16 +252,15 @@
 
 static void onmreg(const void *c, upb_handlers *h) {
   const upb_msgdef *m = upb_handlers_msgdef(h);
-  upb_msg_field_iter i;
+  int i, n;
   UPB_UNUSED(c);
 
   upb_handlers_setstartmsg(h, textprinter_startmsg, NULL);
   upb_handlers_setendmsg(h, textprinter_endmsg, NULL);
 
-  for(upb_msg_field_begin(&i, m);
-      !upb_msg_field_done(&i);
-      upb_msg_field_next(&i)) {
-    upb_fielddef *f = upb_msg_iter_field(&i);
+  n = upb_msgdef_fieldcount(m);
+  for(i = 0; i < n; i++) {
+    const upb_fielddef *f = upb_msgdef_field(m, i);
     upb_handlerattr attr = UPB_HANDLERATTR_INIT;
     attr.handler_data = f;
     switch (upb_fielddef_type(f)) {
diff --git a/upb/reflection.c b/upb/reflection.c
index 487241b..738bb18 100644
--- a/upb/reflection.c
+++ b/upb/reflection.c
@@ -48,6 +48,21 @@
   0,  /* UPB_TYPE_BYTES */
 };
 
+static const char _upb_fieldtype_to_sizelg2[12] = {
+  0,
+  0,  /* UPB_TYPE_BOOL */
+  2,  /* UPB_TYPE_FLOAT */
+  2,  /* UPB_TYPE_INT32 */
+  2,  /* UPB_TYPE_UINT32 */
+  2,  /* UPB_TYPE_ENUM */
+  UPB_SIZE(2, 3),  /* UPB_TYPE_MESSAGE */
+  3,  /* UPB_TYPE_DOUBLE */
+  3,  /* UPB_TYPE_INT64 */
+  3,  /* UPB_TYPE_UINT64 */
+  UPB_SIZE(3, 4),  /* UPB_TYPE_STRING */
+  UPB_SIZE(3, 4),  /* UPB_TYPE_BYTES */
+};
+
 /** upb_msg *******************************************************************/
 
 upb_msg *upb_msg_new(const upb_msgdef *m, upb_arena *a) {
@@ -207,11 +222,12 @@
 bool upb_msg_next(const upb_msg *msg, const upb_msgdef *m,
                   const upb_symtab *ext_pool, const upb_fielddef **out_f,
                   upb_msgval *out_val, size_t *iter) {
-  size_t i = *iter;
+  int i = *iter;
+  int n = upb_msgdef_fieldcount(m);
   const upb_msgval zero = {0};
-  const upb_fielddef *f;
   UPB_UNUSED(ext_pool);
-  while ((f = _upb_msgdef_field(m, (int)++i)) != NULL) {
+  while (++i < n) {
+    const upb_fielddef *f = upb_msgdef_field(m, i);
     upb_msgval val = _upb_msg_getraw(msg, f);
 
     /* Skip field if unset or empty. */
@@ -296,7 +312,7 @@
 /** upb_array *****************************************************************/
 
 upb_array *upb_array_new(upb_arena *a, upb_fieldtype_t type) {
-  return _upb_array_new(a, type);
+  return _upb_array_new(a, 4, _upb_fieldtype_to_sizelg2[type]);
 }
 
 size_t upb_array_size(const upb_array *arr) {
diff --git a/upb/upb.c b/upb/upb.c
index 3089c05..eb32ba2 100644
--- a/upb/upb.c
+++ b/upb/upb.c
@@ -1,5 +1,5 @@
 
-#include "upb/upb.h"
+#include "upb/upb.int.h"
 
 #include <errno.h>
 #include <stdarg.h>
@@ -73,37 +73,18 @@
 
 /* Be conservative and choose 16 in case anyone is using SSE. */
 
-typedef struct mem_block {
+struct mem_block {
   struct mem_block *next;
   uint32_t size;
   uint32_t cleanups;
   /* Data follows. */
-} mem_block;
+};
 
 typedef struct cleanup_ent {
   upb_cleanup_func *cleanup;
   void *ud;
 } cleanup_ent;
 
-struct upb_arena {
-  _upb_arena_head head;
-  uint32_t *cleanups;
-
-  /* Allocator to allocate arena blocks.  We are responsible for freeing these
-   * when we are destroyed. */
-  upb_alloc *block_alloc;
-  uint32_t last_size;
-
-  /* When multiple arenas are fused together, each arena points to a parent
-   * arena (root points to itself). The root tracks how many live arenas
-   * reference it. */
-  uint32_t refcount;  /* Only used when a->parent == a */
-  struct upb_arena *parent;
-
-  /* Linked list of blocks to free/cleanup. */
-  mem_block *freelist, *freelist_tail;
-};
-
 static const size_t memblock_reserve = UPB_ALIGN_UP(sizeof(mem_block), 16);
 
 static upb_arena *arena_findroot(upb_arena *a) {
@@ -117,9 +98,9 @@
   return a;
 }
 
-static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size) {
+static void upb_arena_addblock(upb_arena *a, upb_arena *root, void *ptr,
+                               size_t size) {
   mem_block *block = ptr;
-  upb_arena *root = arena_findroot(a);
 
   /* The block is for arena |a|, but should appear in the freelist of |root|. */
   block->next = root->freelist;
@@ -137,11 +118,12 @@
 }
 
 static bool upb_arena_allocblock(upb_arena *a, size_t size) {
+  upb_arena *root = arena_findroot(a);
   size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve;
-  mem_block *block = upb_malloc(a->block_alloc, block_size);
+  mem_block *block = upb_malloc(root->block_alloc, block_size);
 
   if (!block) return false;
-  upb_arena_addblock(a, block, block_size);
+  upb_arena_addblock(a, root, block, block_size);
   return true;
 }
 
@@ -184,7 +166,7 @@
   a->freelist = NULL;
   a->freelist_tail = NULL;
 
-  upb_arena_addblock(a, mem, n);
+  upb_arena_addblock(a, a, mem, n);
 
   return a;
 }
diff --git a/upb/upb.int.h b/upb/upb.int.h
new file mode 100644
index 0000000..b857560
--- /dev/null
+++ b/upb/upb.int.h
@@ -0,0 +1,29 @@
+
+#ifndef UPB_INT_H_
+#define UPB_INT_H_
+
+#include "upb/upb.h"
+
+struct mem_block;
+typedef struct mem_block mem_block;
+
+struct upb_arena {
+  _upb_arena_head head;
+  uint32_t *cleanups;
+
+  /* Allocator to allocate arena blocks.  We are responsible for freeing these
+   * when we are destroyed. */
+  upb_alloc *block_alloc;
+  uint32_t last_size;
+
+  /* When multiple arenas are fused together, each arena points to a parent
+   * arena (root points to itself). The root tracks how many live arenas
+   * reference it. */
+  uint32_t refcount;  /* Only used when a->parent == a */
+  struct upb_arena *parent;
+
+  /* Linked list of blocks to free/cleanup. */
+  mem_block *freelist, *freelist_tail;
+};
+
+#endif  /* UPB_INT_H_ */
diff --git a/upbc/generator.cc b/upbc/generator.cc
index 52cfb30..da0149c 100644
--- a/upbc/generator.cc
+++ b/upbc/generator.cc
@@ -231,28 +231,28 @@
   }
 }
 
-std::string UpbType(const protobuf::FieldDescriptor* field) {
+std::string SizeLg2(const protobuf::FieldDescriptor* field) {
   switch (field->cpp_type()) {
     case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
-      return "UPB_TYPE_MESSAGE";
+      return "UPB_SIZE(2, 3)";
     case protobuf::FieldDescriptor::CPPTYPE_ENUM:
-      return "UPB_TYPE_ENUM";
+      return std::to_string(2);
     case protobuf::FieldDescriptor::CPPTYPE_BOOL:
-      return "UPB_TYPE_BOOL";
+      return std::to_string(1);
     case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
-      return "UPB_TYPE_FLOAT";
+      return std::to_string(2);
     case protobuf::FieldDescriptor::CPPTYPE_INT32:
-      return "UPB_TYPE_INT32";
+      return std::to_string(2);
     case protobuf::FieldDescriptor::CPPTYPE_UINT32:
-      return "UPB_TYPE_UINT32";
+      return std::to_string(2);
     case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
-      return "UPB_TYPE_DOUBLE";
+      return std::to_string(3);
     case protobuf::FieldDescriptor::CPPTYPE_INT64:
-      return "UPB_TYPE_INT64";
+      return std::to_string(3);
     case protobuf::FieldDescriptor::CPPTYPE_UINT64:
-      return "UPB_TYPE_UINT64";
+      return std::to_string(3);
     case protobuf::FieldDescriptor::CPPTYPE_STRING:
-      return "UPB_TYPE_STRING";
+      return "UPB_SIZE(3, 4)";
     default:
       fprintf(stderr, "Unexpected type");
       abort();
@@ -509,7 +509,7 @@
           "}\n",
           CType(field), msgname, field->name(),
           GetSizeInit(layout.GetFieldOffset(field)),
-          UpbType(field));
+          SizeLg2(field));
       if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
         output(
             "UPB_INLINE struct $0* $1_add_$2($1 *msg, upb_arena *arena) {\n"
@@ -523,7 +523,7 @@
             MessageInit(field->message_type()),
             GetSizeInit(layout.GetFieldOffset(field)),
             GetSizeInit(MessageLayout::SizeOfUnwrapped(field).size),
-            UpbType(field));
+            SizeLg2(field));
       } else {
         output(
             "UPB_INLINE bool $1_add_$2($1 *msg, $0 val, upb_arena *arena) {\n"
@@ -533,7 +533,7 @@
             CType(field), msgname, field->name(),
             GetSizeInit(layout.GetFieldOffset(field)),
             GetSizeInit(MessageLayout::SizeOfUnwrapped(field).size),
-            UpbType(field));
+            SizeLg2(field));
       }
     } else {
       // Non-repeated field.
@@ -600,6 +600,7 @@
       "#define $0_UPB_H_\n\n"
       "#include \"upb/msg.h\"\n"
       "#include \"upb/decode.h\"\n"
+      "#include \"upb/decode_fast.h\"\n"
       "#include \"upb/encode.h\"\n\n",
       ToPreproc(file->name()));