From ce012b7b55f038394d142bf8a09827bdfdccf64b Mon Sep 17 00:00:00 2001
From: Joshua Haberman <jhaberman@gmail.com>
Date: Fri, 1 Oct 2021 16:34:42 -0700
Subject: [PATCH] Added support for extensions.

---
 BUILD                                  |  21 ++
 README.md                              |   6 +-
 cmake/google/protobuf/descriptor.upb.c | 457 +++++++++++++------------
 cmake/google/protobuf/descriptor.upb.h |   2 +
 tests/conformance_upb_failures.txt     |   1 -
 upb/decode.c                           |  96 ++++--
 upb/decode_internal.h                  |   1 +
 upb/def.c                              | 247 ++++++++++---
 upb/def.h                              |  20 +-
 upb/encode.c                           | 119 ++++---
 upb/json_decode.c                      |  23 +-
 upb/json_decode.h                      |   2 +-
 upb/json_encode.c                      |  16 +-
 upb/msg.c                              |  31 +-
 upb/msg_internal.h                     |  78 ++++-
 upb/reflection.c                       | 156 +++++----
 upb/reflection.h                       |   3 +-
 upbc/BUILD                             |   1 +
 upbc/common.cc                         |  25 +-
 upbc/common.h                          |   5 +-
 upbc/protoc-gen-upb.cc                 | 259 +++++++++++++-
 upbc/protoc-gen-upbdefs.cc             |  24 +-
 22 files changed, 1103 insertions(+), 490 deletions(-)

diff --git a/BUILD b/BUILD
index 1ddf4d3a0c..97928110b8 100644
--- a/BUILD
+++ b/BUILD
@@ -215,6 +215,27 @@ cc_library(
     ],
 )
 
+cc_test(
+    name = "msg_test",
+    srcs = ["upb/msg_test.cc"],
+    deps = [
+        "@com_google_googletest//:gtest_main",
+        ":msg_test_upb_proto_reflection",
+        ":json",
+    ],
+)
+
+proto_library(
+    name = "msg_test_proto",
+    srcs = ["upb/msg_test.proto"],
+    deps = ["@com_google_protobuf//:test_messages_proto3_proto"],
+)
+
+upb_proto_reflection_library(
+    name = "msg_test_upb_proto_reflection",
+    deps = [":msg_test_proto"],
+)
+
 # Internal C/C++ libraries #####################################################
 
 cc_library(
diff --git a/README.md b/README.md
index c59340c16f..92a081bbef 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,8 @@ Like the main protobuf implementation in C++, it supports:
 - reflection
 - binary & JSON wire formats
 - text format serialization
-- all standard features of protobufs (oneofs, maps, unknown fields, etc.)
+- all standard features of protobufs (oneofs, maps, unknown fields, extensions,
+  etc.)
 - full conformance with the protobuf conformance tests
 
 upb also supports some features that C++ does not:
@@ -36,9 +37,8 @@ upb also supports some features that C++ does not:
 - **fast reflection-based parsing:** messages loaded at runtime parse
   just as fast as compiled-in messages.
 
-However there are some features it does not support:
+However there are a few features it does not support:
 
-- proto2 extensions (coming soon!)
 - text format parsing
 - deep descriptor verification: upb's descriptor validation is not as exhaustive
   as `protoc`.
diff --git a/cmake/google/protobuf/descriptor.upb.c b/cmake/google/protobuf/descriptor.upb.c
index a6b9af9132..fdbf23b9d1 100644
--- a/cmake/google/protobuf/descriptor.upb.c
+++ b/cmake/google/protobuf/descriptor.upb.c
@@ -12,474 +12,511 @@
 
 #include "upb/port_def.inc"
 
-static const upb_msglayout *const google_protobuf_FileDescriptorSet_submsgs[1] = {
-  &google_protobuf_FileDescriptorProto_msginit,
+static const upb_msglayout_sub google_protobuf_FileDescriptorSet_submsgs[1] = {
+  {.submsg = &google_protobuf_FileDescriptorProto_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_FileDescriptorSet__fields[1] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_FileDescriptorSet_msginit = {
   &google_protobuf_FileDescriptorSet_submsgs[0],
   &google_protobuf_FileDescriptorSet__fields[0],
-  UPB_SIZE(8, 8), 1, false, 1, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
 };
 
-static const upb_msglayout *const google_protobuf_FileDescriptorProto_submsgs[6] = {
-  &google_protobuf_DescriptorProto_msginit,
-  &google_protobuf_EnumDescriptorProto_msginit,
-  &google_protobuf_FieldDescriptorProto_msginit,
-  &google_protobuf_FileOptions_msginit,
-  &google_protobuf_ServiceDescriptorProto_msginit,
-  &google_protobuf_SourceCodeInfo_msginit,
+static const upb_msglayout_sub google_protobuf_FileDescriptorProto_submsgs[6] = {
+  {.submsg = &google_protobuf_DescriptorProto_msginit},
+  {.submsg = &google_protobuf_EnumDescriptorProto_msginit},
+  {.submsg = &google_protobuf_FieldDescriptorProto_msginit},
+  {.submsg = &google_protobuf_FileOptions_msginit},
+  {.submsg = &google_protobuf_ServiceDescriptorProto_msginit},
+  {.submsg = &google_protobuf_SourceCodeInfo_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_FileDescriptorProto__fields[12] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(12, 24), 2, 0, 12, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(36, 72), 0, 0, 12, _UPB_MODE_ARRAY},
-  {4, UPB_SIZE(40, 80), 0, 0, 11, _UPB_MODE_ARRAY},
-  {5, UPB_SIZE(44, 88), 0, 1, 11, _UPB_MODE_ARRAY},
-  {6, UPB_SIZE(48, 96), 0, 4, 11, _UPB_MODE_ARRAY},
-  {7, UPB_SIZE(52, 104), 0, 2, 11, _UPB_MODE_ARRAY},
-  {8, UPB_SIZE(28, 56), 3, 3, 11, _UPB_MODE_SCALAR},
-  {9, UPB_SIZE(32, 64), 4, 5, 11, _UPB_MODE_SCALAR},
-  {10, UPB_SIZE(56, 112), 0, 0, 5, _UPB_MODE_ARRAY},
-  {11, UPB_SIZE(60, 120), 0, 0, 5, _UPB_MODE_ARRAY},
-  {12, UPB_SIZE(20, 40), 5, 0, 12, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(12, 24), 2, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {3, UPB_SIZE(36, 72), 0, 0, 12, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {4, UPB_SIZE(40, 80), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {5, UPB_SIZE(44, 88), 0, 1, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {6, UPB_SIZE(48, 96), 0, 4, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {7, UPB_SIZE(52, 104), 0, 2, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {8, UPB_SIZE(28, 56), 3, 3, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
+  {9, UPB_SIZE(32, 64), 4, 5, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
+  {10, UPB_SIZE(56, 112), 0, 0, 5, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {11, UPB_SIZE(60, 120), 0, 0, 5, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {12, UPB_SIZE(20, 40), 5, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
 };
 
 const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
   &google_protobuf_FileDescriptorProto_submsgs[0],
   &google_protobuf_FileDescriptorProto__fields[0],
-  UPB_SIZE(64, 128), 12, false, 12, 255,
+  UPB_SIZE(64, 128), 12, _UPB_MSGEXT_NONE, 12, 255,
 };
 
-static const upb_msglayout *const google_protobuf_DescriptorProto_submsgs[7] = {
-  &google_protobuf_DescriptorProto_msginit,
-  &google_protobuf_DescriptorProto_ExtensionRange_msginit,
-  &google_protobuf_DescriptorProto_ReservedRange_msginit,
-  &google_protobuf_EnumDescriptorProto_msginit,
-  &google_protobuf_FieldDescriptorProto_msginit,
-  &google_protobuf_MessageOptions_msginit,
-  &google_protobuf_OneofDescriptorProto_msginit,
+static const upb_msglayout_sub google_protobuf_DescriptorProto_submsgs[7] = {
+  {.submsg = &google_protobuf_DescriptorProto_msginit},
+  {.submsg = &google_protobuf_DescriptorProto_ExtensionRange_msginit},
+  {.submsg = &google_protobuf_DescriptorProto_ReservedRange_msginit},
+  {.submsg = &google_protobuf_EnumDescriptorProto_msginit},
+  {.submsg = &google_protobuf_FieldDescriptorProto_msginit},
+  {.submsg = &google_protobuf_MessageOptions_msginit},
+  {.submsg = &google_protobuf_OneofDescriptorProto_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto__fields[10] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(16, 32), 0, 4, 11, _UPB_MODE_ARRAY},
-  {3, UPB_SIZE(20, 40), 0, 0, 11, _UPB_MODE_ARRAY},
-  {4, UPB_SIZE(24, 48), 0, 3, 11, _UPB_MODE_ARRAY},
-  {5, UPB_SIZE(28, 56), 0, 1, 11, _UPB_MODE_ARRAY},
-  {6, UPB_SIZE(32, 64), 0, 4, 11, _UPB_MODE_ARRAY},
-  {7, UPB_SIZE(12, 24), 2, 5, 11, _UPB_MODE_SCALAR},
-  {8, UPB_SIZE(36, 72), 0, 6, 11, _UPB_MODE_ARRAY},
-  {9, UPB_SIZE(40, 80), 0, 2, 11, _UPB_MODE_ARRAY},
-  {10, UPB_SIZE(44, 88), 0, 0, 12, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(16, 32), 0, 4, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {3, UPB_SIZE(20, 40), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {4, UPB_SIZE(24, 48), 0, 3, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {5, UPB_SIZE(28, 56), 0, 1, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {6, UPB_SIZE(32, 64), 0, 4, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {7, UPB_SIZE(12, 24), 2, 5, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
+  {8, UPB_SIZE(36, 72), 0, 6, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {9, UPB_SIZE(40, 80), 0, 2, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {10, UPB_SIZE(44, 88), 0, 0, 12, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_DescriptorProto_msginit = {
   &google_protobuf_DescriptorProto_submsgs[0],
   &google_protobuf_DescriptorProto__fields[0],
-  UPB_SIZE(48, 96), 10, false, 10, 255,
+  UPB_SIZE(48, 96), 10, _UPB_MSGEXT_NONE, 10, 255,
 };
 
-static const upb_msglayout *const google_protobuf_DescriptorProto_ExtensionRange_submsgs[1] = {
-  &google_protobuf_ExtensionRangeOptions_msginit,
+static const upb_msglayout_sub google_protobuf_DescriptorProto_ExtensionRange_submsgs[1] = {
+  {.submsg = &google_protobuf_ExtensionRangeOptions_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto_ExtensionRange__fields[3] = {
-  {1, UPB_SIZE(4, 4), 1, 0, 5, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(8, 8), 2, 0, 5, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(12, 16), 3, 0, 11, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 4), 1, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {2, UPB_SIZE(8, 8), 2, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {3, UPB_SIZE(12, 16), 3, 0, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_DescriptorProto_ExtensionRange_msginit = {
   &google_protobuf_DescriptorProto_ExtensionRange_submsgs[0],
   &google_protobuf_DescriptorProto_ExtensionRange__fields[0],
-  UPB_SIZE(16, 24), 3, false, 3, 255,
+  UPB_SIZE(16, 24), 3, _UPB_MSGEXT_NONE, 3, 255,
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto_ReservedRange__fields[2] = {
-  {1, UPB_SIZE(4, 4), 1, 0, 5, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(8, 8), 2, 0, 5, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 4), 1, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {2, UPB_SIZE(8, 8), 2, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
 };
 
 const upb_msglayout google_protobuf_DescriptorProto_ReservedRange_msginit = {
   NULL,
   &google_protobuf_DescriptorProto_ReservedRange__fields[0],
-  UPB_SIZE(16, 16), 2, false, 2, 255,
+  UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
-static const upb_msglayout *const google_protobuf_ExtensionRangeOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_ExtensionRangeOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_ExtensionRangeOptions__fields[1] = {
-  {999, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY},
+  {999, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_ExtensionRangeOptions_msginit = {
   &google_protobuf_ExtensionRangeOptions_submsgs[0],
   &google_protobuf_ExtensionRangeOptions__fields[0],
-  UPB_SIZE(8, 8), 1, false, 0, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
-static const upb_msglayout *const google_protobuf_FieldDescriptorProto_submsgs[1] = {
-  &google_protobuf_FieldOptions_msginit,
+static const upb_msglayout_sub google_protobuf_FieldDescriptorProto_submsgs[1] = {
+  {.submsg = &google_protobuf_FieldOptions_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_FieldDescriptorProto__fields[11] = {
-  {1, UPB_SIZE(24, 24), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(32, 40), 2, 0, 12, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(12, 12), 3, 0, 5, _UPB_MODE_SCALAR},
-  {4, UPB_SIZE(4, 4), 4, 0, 14, _UPB_MODE_SCALAR},
-  {5, UPB_SIZE(8, 8), 5, 0, 14, _UPB_MODE_SCALAR},
-  {6, UPB_SIZE(40, 56), 6, 0, 12, _UPB_MODE_SCALAR},
-  {7, UPB_SIZE(48, 72), 7, 0, 12, _UPB_MODE_SCALAR},
-  {8, UPB_SIZE(64, 104), 8, 0, 11, _UPB_MODE_SCALAR},
-  {9, UPB_SIZE(16, 16), 9, 0, 5, _UPB_MODE_SCALAR},
-  {10, UPB_SIZE(56, 88), 10, 0, 12, _UPB_MODE_SCALAR},
-  {17, UPB_SIZE(20, 20), 11, 0, 8, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(24, 24), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(32, 40), 2, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {3, UPB_SIZE(12, 12), 3, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {4, UPB_SIZE(4, 4), 4, 0, 14, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {5, UPB_SIZE(8, 8), 5, 0, 14, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {6, UPB_SIZE(40, 56), 6, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {7, UPB_SIZE(48, 72), 7, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {8, UPB_SIZE(64, 104), 8, 0, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
+  {9, UPB_SIZE(16, 16), 9, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {10, UPB_SIZE(56, 88), 10, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {17, UPB_SIZE(20, 20), 11, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
 };
 
 const upb_msglayout google_protobuf_FieldDescriptorProto_msginit = {
   &google_protobuf_FieldDescriptorProto_submsgs[0],
   &google_protobuf_FieldDescriptorProto__fields[0],
-  UPB_SIZE(72, 112), 11, false, 10, 255,
+  UPB_SIZE(72, 112), 11, _UPB_MSGEXT_NONE, 10, 255,
 };
 
-static const upb_msglayout *const google_protobuf_OneofDescriptorProto_submsgs[1] = {
-  &google_protobuf_OneofOptions_msginit,
+static const upb_msglayout_sub google_protobuf_OneofDescriptorProto_submsgs[1] = {
+  {.submsg = &google_protobuf_OneofOptions_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_OneofDescriptorProto__fields[2] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(12, 24), 2, 0, 11, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(12, 24), 2, 0, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_OneofDescriptorProto_msginit = {
   &google_protobuf_OneofDescriptorProto_submsgs[0],
   &google_protobuf_OneofDescriptorProto__fields[0],
-  UPB_SIZE(16, 32), 2, false, 2, 255,
+  UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
-static const upb_msglayout *const google_protobuf_EnumDescriptorProto_submsgs[3] = {
-  &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit,
-  &google_protobuf_EnumOptions_msginit,
-  &google_protobuf_EnumValueDescriptorProto_msginit,
+static const upb_msglayout_sub google_protobuf_EnumDescriptorProto_submsgs[3] = {
+  {.submsg = &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit},
+  {.submsg = &google_protobuf_EnumOptions_msginit},
+  {.submsg = &google_protobuf_EnumValueDescriptorProto_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_EnumDescriptorProto__fields[5] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(16, 32), 0, 2, 11, _UPB_MODE_ARRAY},
-  {3, UPB_SIZE(12, 24), 2, 1, 11, _UPB_MODE_SCALAR},
-  {4, UPB_SIZE(20, 40), 0, 0, 11, _UPB_MODE_ARRAY},
-  {5, UPB_SIZE(24, 48), 0, 0, 12, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(16, 32), 0, 2, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {3, UPB_SIZE(12, 24), 2, 1, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
+  {4, UPB_SIZE(20, 40), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {5, UPB_SIZE(24, 48), 0, 0, 12, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
   &google_protobuf_EnumDescriptorProto_submsgs[0],
   &google_protobuf_EnumDescriptorProto__fields[0],
-  UPB_SIZE(32, 64), 5, false, 5, 255,
+  UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 5, 255,
 };
 
 static const upb_msglayout_field google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[2] = {
-  {1, UPB_SIZE(4, 4), 1, 0, 5, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(8, 8), 2, 0, 5, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 4), 1, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {2, UPB_SIZE(8, 8), 2, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
 };
 
 const upb_msglayout google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit = {
   NULL,
   &google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[0],
-  UPB_SIZE(16, 16), 2, false, 2, 255,
+  UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
-static const upb_msglayout *const google_protobuf_EnumValueDescriptorProto_submsgs[1] = {
-  &google_protobuf_EnumValueOptions_msginit,
+static const upb_msglayout_sub google_protobuf_EnumValueDescriptorProto_submsgs[1] = {
+  {.submsg = &google_protobuf_EnumValueOptions_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_EnumValueDescriptorProto__fields[3] = {
-  {1, UPB_SIZE(8, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(4, 4), 2, 0, 5, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(16, 24), 3, 0, 11, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(8, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(4, 4), 2, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {3, UPB_SIZE(16, 24), 3, 0, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_EnumValueDescriptorProto_msginit = {
   &google_protobuf_EnumValueDescriptorProto_submsgs[0],
   &google_protobuf_EnumValueDescriptorProto__fields[0],
-  UPB_SIZE(24, 32), 3, false, 3, 255,
+  UPB_SIZE(24, 32), 3, _UPB_MSGEXT_NONE, 3, 255,
 };
 
-static const upb_msglayout *const google_protobuf_ServiceDescriptorProto_submsgs[2] = {
-  &google_protobuf_MethodDescriptorProto_msginit,
-  &google_protobuf_ServiceOptions_msginit,
+static const upb_msglayout_sub google_protobuf_ServiceDescriptorProto_submsgs[2] = {
+  {.submsg = &google_protobuf_MethodDescriptorProto_msginit},
+  {.submsg = &google_protobuf_ServiceOptions_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_ServiceDescriptorProto__fields[3] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(16, 32), 0, 0, 11, _UPB_MODE_ARRAY},
-  {3, UPB_SIZE(12, 24), 2, 1, 11, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(16, 32), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {3, UPB_SIZE(12, 24), 2, 1, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_ServiceDescriptorProto_msginit = {
   &google_protobuf_ServiceDescriptorProto_submsgs[0],
   &google_protobuf_ServiceDescriptorProto__fields[0],
-  UPB_SIZE(24, 48), 3, false, 3, 255,
+  UPB_SIZE(24, 48), 3, _UPB_MSGEXT_NONE, 3, 255,
 };
 
-static const upb_msglayout *const google_protobuf_MethodDescriptorProto_submsgs[1] = {
-  &google_protobuf_MethodOptions_msginit,
+static const upb_msglayout_sub google_protobuf_MethodDescriptorProto_submsgs[1] = {
+  {.submsg = &google_protobuf_MethodOptions_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_MethodDescriptorProto__fields[6] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(12, 24), 2, 0, 12, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(20, 40), 3, 0, 12, _UPB_MODE_SCALAR},
-  {4, UPB_SIZE(28, 56), 4, 0, 11, _UPB_MODE_SCALAR},
-  {5, UPB_SIZE(1, 1), 5, 0, 8, _UPB_MODE_SCALAR},
-  {6, UPB_SIZE(2, 2), 6, 0, 8, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(12, 24), 2, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {3, UPB_SIZE(20, 40), 3, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {4, UPB_SIZE(28, 56), 4, 0, 11, _UPB_MODE_SCALAR | (_UPB_REP_PTR << 6)},
+  {5, UPB_SIZE(1, 1), 5, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {6, UPB_SIZE(2, 2), 6, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
 };
 
 const upb_msglayout google_protobuf_MethodDescriptorProto_msginit = {
   &google_protobuf_MethodDescriptorProto_submsgs[0],
   &google_protobuf_MethodDescriptorProto__fields[0],
-  UPB_SIZE(32, 64), 6, false, 6, 255,
+  UPB_SIZE(32, 64), 6, _UPB_MSGEXT_NONE, 6, 255,
 };
 
-static const upb_msglayout *const google_protobuf_FileOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_FileOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_FileOptions__fields[21] = {
-  {1, UPB_SIZE(20, 24), 1, 0, 12, _UPB_MODE_SCALAR},
-  {8, UPB_SIZE(28, 40), 2, 0, 12, _UPB_MODE_SCALAR},
-  {9, UPB_SIZE(4, 4), 3, 0, 14, _UPB_MODE_SCALAR},
-  {10, UPB_SIZE(8, 8), 4, 0, 8, _UPB_MODE_SCALAR},
-  {11, UPB_SIZE(36, 56), 5, 0, 12, _UPB_MODE_SCALAR},
-  {16, UPB_SIZE(9, 9), 6, 0, 8, _UPB_MODE_SCALAR},
-  {17, UPB_SIZE(10, 10), 7, 0, 8, _UPB_MODE_SCALAR},
-  {18, UPB_SIZE(11, 11), 8, 0, 8, _UPB_MODE_SCALAR},
-  {20, UPB_SIZE(12, 12), 9, 0, 8, _UPB_MODE_SCALAR},
-  {23, UPB_SIZE(13, 13), 10, 0, 8, _UPB_MODE_SCALAR},
-  {27, UPB_SIZE(14, 14), 11, 0, 8, _UPB_MODE_SCALAR},
-  {31, UPB_SIZE(15, 15), 12, 0, 8, _UPB_MODE_SCALAR},
-  {36, UPB_SIZE(44, 72), 13, 0, 12, _UPB_MODE_SCALAR},
-  {37, UPB_SIZE(52, 88), 14, 0, 12, _UPB_MODE_SCALAR},
-  {39, UPB_SIZE(60, 104), 15, 0, 12, _UPB_MODE_SCALAR},
-  {40, UPB_SIZE(68, 120), 16, 0, 12, _UPB_MODE_SCALAR},
-  {41, UPB_SIZE(76, 136), 17, 0, 12, _UPB_MODE_SCALAR},
-  {42, UPB_SIZE(16, 16), 18, 0, 8, _UPB_MODE_SCALAR},
-  {44, UPB_SIZE(84, 152), 19, 0, 12, _UPB_MODE_SCALAR},
-  {45, UPB_SIZE(92, 168), 20, 0, 12, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(100, 184), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(20, 24), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {8, UPB_SIZE(28, 40), 2, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {9, UPB_SIZE(4, 4), 3, 0, 14, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {10, UPB_SIZE(8, 8), 4, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {11, UPB_SIZE(36, 56), 5, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {16, UPB_SIZE(9, 9), 6, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {17, UPB_SIZE(10, 10), 7, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {18, UPB_SIZE(11, 11), 8, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {20, UPB_SIZE(12, 12), 9, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {23, UPB_SIZE(13, 13), 10, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {27, UPB_SIZE(14, 14), 11, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {31, UPB_SIZE(15, 15), 12, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {36, UPB_SIZE(44, 72), 13, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {37, UPB_SIZE(52, 88), 14, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {39, UPB_SIZE(60, 104), 15, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {40, UPB_SIZE(68, 120), 16, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {41, UPB_SIZE(76, 136), 17, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {42, UPB_SIZE(16, 16), 18, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {44, UPB_SIZE(84, 152), 19, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {45, UPB_SIZE(92, 168), 20, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {999, UPB_SIZE(100, 184), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_FileOptions_msginit = {
   &google_protobuf_FileOptions_submsgs[0],
   &google_protobuf_FileOptions__fields[0],
-  UPB_SIZE(104, 192), 21, false, 1, 255,
+  UPB_SIZE(104, 192), 21, _UPB_MSGEXT_EXTENDABLE, 1, 255,
 };
 
-static const upb_msglayout *const google_protobuf_MessageOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_MessageOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_MessageOptions__fields[5] = {
-  {1, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(2, 2), 2, 0, 8, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(3, 3), 3, 0, 8, _UPB_MODE_SCALAR},
-  {7, UPB_SIZE(4, 4), 4, 0, 8, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(8, 8), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {2, UPB_SIZE(2, 2), 2, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {3, UPB_SIZE(3, 3), 3, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {7, UPB_SIZE(4, 4), 4, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {999, UPB_SIZE(8, 8), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_MessageOptions_msginit = {
   &google_protobuf_MessageOptions_submsgs[0],
   &google_protobuf_MessageOptions__fields[0],
-  UPB_SIZE(16, 16), 5, false, 3, 255,
+  UPB_SIZE(16, 16), 5, _UPB_MSGEXT_EXTENDABLE, 3, 255,
 };
 
-static const upb_msglayout *const google_protobuf_FieldOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_FieldOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_FieldOptions__fields[7] = {
-  {1, UPB_SIZE(4, 4), 1, 0, 14, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(12, 12), 2, 0, 8, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(13, 13), 3, 0, 8, _UPB_MODE_SCALAR},
-  {5, UPB_SIZE(14, 14), 4, 0, 8, _UPB_MODE_SCALAR},
-  {6, UPB_SIZE(8, 8), 5, 0, 14, _UPB_MODE_SCALAR},
-  {10, UPB_SIZE(15, 15), 6, 0, 8, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(16, 16), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(4, 4), 1, 0, 14, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {2, UPB_SIZE(12, 12), 2, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {3, UPB_SIZE(13, 13), 3, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {5, UPB_SIZE(14, 14), 4, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {6, UPB_SIZE(8, 8), 5, 0, 14, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {10, UPB_SIZE(15, 15), 6, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {999, UPB_SIZE(16, 16), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_FieldOptions_msginit = {
   &google_protobuf_FieldOptions_submsgs[0],
   &google_protobuf_FieldOptions__fields[0],
-  UPB_SIZE(24, 24), 7, false, 3, 255,
+  UPB_SIZE(24, 24), 7, _UPB_MSGEXT_EXTENDABLE, 3, 255,
 };
 
-static const upb_msglayout *const google_protobuf_OneofOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_OneofOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_OneofOptions__fields[1] = {
-  {999, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY},
+  {999, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_OneofOptions_msginit = {
   &google_protobuf_OneofOptions_submsgs[0],
   &google_protobuf_OneofOptions__fields[0],
-  UPB_SIZE(8, 8), 1, false, 0, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
-static const upb_msglayout *const google_protobuf_EnumOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_EnumOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_EnumOptions__fields[3] = {
-  {2, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(2, 2), 2, 0, 8, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(4, 8), 0, 0, 11, _UPB_MODE_ARRAY},
+  {2, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {3, UPB_SIZE(2, 2), 2, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {999, UPB_SIZE(4, 8), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_EnumOptions_msginit = {
   &google_protobuf_EnumOptions_submsgs[0],
   &google_protobuf_EnumOptions__fields[0],
-  UPB_SIZE(8, 16), 3, false, 0, 255,
+  UPB_SIZE(8, 16), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
-static const upb_msglayout *const google_protobuf_EnumValueOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_EnumValueOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_EnumValueOptions__fields[2] = {
-  {1, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(4, 8), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {999, UPB_SIZE(4, 8), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_EnumValueOptions_msginit = {
   &google_protobuf_EnumValueOptions_submsgs[0],
   &google_protobuf_EnumValueOptions__fields[0],
-  UPB_SIZE(8, 16), 2, false, 1, 255,
+  UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 1, 255,
 };
 
-static const upb_msglayout *const google_protobuf_ServiceOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_ServiceOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_ServiceOptions__fields[2] = {
-  {33, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(4, 8), 0, 0, 11, _UPB_MODE_ARRAY},
+  {33, UPB_SIZE(1, 1), 1, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {999, UPB_SIZE(4, 8), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_ServiceOptions_msginit = {
   &google_protobuf_ServiceOptions_submsgs[0],
   &google_protobuf_ServiceOptions__fields[0],
-  UPB_SIZE(8, 16), 2, false, 0, 255,
+  UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
-static const upb_msglayout *const google_protobuf_MethodOptions_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_msginit,
+static const upb_msglayout_sub google_protobuf_MethodOptions_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_MethodOptions__fields[3] = {
-  {33, UPB_SIZE(8, 8), 1, 0, 8, _UPB_MODE_SCALAR},
-  {34, UPB_SIZE(4, 4), 2, 0, 14, _UPB_MODE_SCALAR},
-  {999, UPB_SIZE(12, 16), 0, 0, 11, _UPB_MODE_ARRAY},
+  {33, UPB_SIZE(8, 8), 1, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
+  {34, UPB_SIZE(4, 4), 2, 0, 14, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {999, UPB_SIZE(12, 16), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_MethodOptions_msginit = {
   &google_protobuf_MethodOptions_submsgs[0],
   &google_protobuf_MethodOptions__fields[0],
-  UPB_SIZE(16, 24), 3, false, 0, 255,
+  UPB_SIZE(16, 24), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
 };
 
-static const upb_msglayout *const google_protobuf_UninterpretedOption_submsgs[1] = {
-  &google_protobuf_UninterpretedOption_NamePart_msginit,
+static const upb_msglayout_sub google_protobuf_UninterpretedOption_submsgs[1] = {
+  {.submsg = &google_protobuf_UninterpretedOption_NamePart_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption__fields[7] = {
-  {2, UPB_SIZE(56, 80), 0, 0, 11, _UPB_MODE_ARRAY},
-  {3, UPB_SIZE(32, 32), 1, 0, 12, _UPB_MODE_SCALAR},
-  {4, UPB_SIZE(8, 8), 2, 0, 4, _UPB_MODE_SCALAR},
-  {5, UPB_SIZE(16, 16), 3, 0, 3, _UPB_MODE_SCALAR},
-  {6, UPB_SIZE(24, 24), 4, 0, 1, _UPB_MODE_SCALAR},
-  {7, UPB_SIZE(40, 48), 5, 0, 12, _UPB_MODE_SCALAR},
-  {8, UPB_SIZE(48, 64), 6, 0, 12, _UPB_MODE_SCALAR},
+  {2, UPB_SIZE(56, 80), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
+  {3, UPB_SIZE(32, 32), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {4, UPB_SIZE(8, 8), 2, 0, 4, _UPB_MODE_SCALAR | (_UPB_REP_8BYTE << 6)},
+  {5, UPB_SIZE(16, 16), 3, 0, 3, _UPB_MODE_SCALAR | (_UPB_REP_8BYTE << 6)},
+  {6, UPB_SIZE(24, 24), 4, 0, 1, _UPB_MODE_SCALAR | (_UPB_REP_8BYTE << 6)},
+  {7, UPB_SIZE(40, 48), 5, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {8, UPB_SIZE(48, 64), 6, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
 };
 
 const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
   &google_protobuf_UninterpretedOption_submsgs[0],
   &google_protobuf_UninterpretedOption__fields[0],
-  UPB_SIZE(64, 96), 7, false, 0, 255,
+  UPB_SIZE(64, 96), 7, _UPB_MSGEXT_NONE, 0, 255,
 };
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption_NamePart__fields[2] = {
-  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {2, UPB_SIZE(1, 1), 2, 0, 8, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {2, UPB_SIZE(1, 1), 2, 0, 8, _UPB_MODE_SCALAR | (_UPB_REP_1BYTE << 6)},
 };
 
 const upb_msglayout google_protobuf_UninterpretedOption_NamePart_msginit = {
   NULL,
   &google_protobuf_UninterpretedOption_NamePart__fields[0],
-  UPB_SIZE(16, 32), 2, false, 2, 255,
+  UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
 };
 
-static const upb_msglayout *const google_protobuf_SourceCodeInfo_submsgs[1] = {
-  &google_protobuf_SourceCodeInfo_Location_msginit,
+static const upb_msglayout_sub google_protobuf_SourceCodeInfo_submsgs[1] = {
+  {.submsg = &google_protobuf_SourceCodeInfo_Location_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_SourceCodeInfo__fields[1] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_SourceCodeInfo_msginit = {
   &google_protobuf_SourceCodeInfo_submsgs[0],
   &google_protobuf_SourceCodeInfo__fields[0],
-  UPB_SIZE(8, 8), 1, false, 1, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
 };
 
 static const upb_msglayout_field google_protobuf_SourceCodeInfo_Location__fields[5] = {
-  {1, UPB_SIZE(20, 40), 0, 0, 5, _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED},
-  {2, UPB_SIZE(24, 48), 0, 0, 5, _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED},
-  {3, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR},
-  {4, UPB_SIZE(12, 24), 2, 0, 12, _UPB_MODE_SCALAR},
-  {6, UPB_SIZE(28, 56), 0, 0, 12, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(20, 40), 0, 0, 5, _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED | (_UPB_REP_PTR << 6)},
+  {2, UPB_SIZE(24, 48), 0, 0, 5, _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED | (_UPB_REP_PTR << 6)},
+  {3, UPB_SIZE(4, 8), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {4, UPB_SIZE(12, 24), 2, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {6, UPB_SIZE(28, 56), 0, 0, 12, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
   NULL,
   &google_protobuf_SourceCodeInfo_Location__fields[0],
-  UPB_SIZE(32, 64), 5, false, 4, 255,
+  UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 4, 255,
 };
 
-static const upb_msglayout *const google_protobuf_GeneratedCodeInfo_submsgs[1] = {
-  &google_protobuf_GeneratedCodeInfo_Annotation_msginit,
+static const upb_msglayout_sub google_protobuf_GeneratedCodeInfo_submsgs[1] = {
+  {.submsg = &google_protobuf_GeneratedCodeInfo_Annotation_msginit},
 };
 
 static const upb_msglayout_field google_protobuf_GeneratedCodeInfo__fields[1] = {
-  {1, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY},
+  {1, UPB_SIZE(0, 0), 0, 0, 11, _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6)},
 };
 
 const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit = {
   &google_protobuf_GeneratedCodeInfo_submsgs[0],
   &google_protobuf_GeneratedCodeInfo__fields[0],
-  UPB_SIZE(8, 8), 1, false, 1, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
 };
 
 static const upb_msglayout_field google_protobuf_GeneratedCodeInfo_Annotation__fields[4] = {
-  {1, UPB_SIZE(20, 32), 0, 0, 5, _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED},
-  {2, UPB_SIZE(12, 16), 1, 0, 12, _UPB_MODE_SCALAR},
-  {3, UPB_SIZE(4, 4), 2, 0, 5, _UPB_MODE_SCALAR},
-  {4, UPB_SIZE(8, 8), 3, 0, 5, _UPB_MODE_SCALAR},
+  {1, UPB_SIZE(20, 32), 0, 0, 5, _UPB_MODE_ARRAY | _UPB_MODE_IS_PACKED | (_UPB_REP_PTR << 6)},
+  {2, UPB_SIZE(12, 16), 1, 0, 12, _UPB_MODE_SCALAR | (_UPB_REP_STRVIEW << 6)},
+  {3, UPB_SIZE(4, 4), 2, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
+  {4, UPB_SIZE(8, 8), 3, 0, 5, _UPB_MODE_SCALAR | (_UPB_REP_4BYTE << 6)},
 };
 
 const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit = {
   NULL,
   &google_protobuf_GeneratedCodeInfo_Annotation__fields[0],
-  UPB_SIZE(24, 48), 4, false, 4, 255,
+  UPB_SIZE(24, 48), 4, _UPB_MSGEXT_NONE, 4, 255,
+};
+
+static const upb_msglayout *messages_layout[27] = {
+  &google_protobuf_FileDescriptorSet_msginit,
+  &google_protobuf_FileDescriptorProto_msginit,
+  &google_protobuf_DescriptorProto_msginit,
+  &google_protobuf_DescriptorProto_ExtensionRange_msginit,
+  &google_protobuf_DescriptorProto_ReservedRange_msginit,
+  &google_protobuf_ExtensionRangeOptions_msginit,
+  &google_protobuf_FieldDescriptorProto_msginit,
+  &google_protobuf_OneofDescriptorProto_msginit,
+  &google_protobuf_EnumDescriptorProto_msginit,
+  &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit,
+  &google_protobuf_EnumValueDescriptorProto_msginit,
+  &google_protobuf_ServiceDescriptorProto_msginit,
+  &google_protobuf_MethodDescriptorProto_msginit,
+  &google_protobuf_FileOptions_msginit,
+  &google_protobuf_MessageOptions_msginit,
+  &google_protobuf_FieldOptions_msginit,
+  &google_protobuf_OneofOptions_msginit,
+  &google_protobuf_EnumOptions_msginit,
+  &google_protobuf_EnumValueOptions_msginit,
+  &google_protobuf_ServiceOptions_msginit,
+  &google_protobuf_MethodOptions_msginit,
+  &google_protobuf_UninterpretedOption_msginit,
+  &google_protobuf_UninterpretedOption_NamePart_msginit,
+  &google_protobuf_SourceCodeInfo_msginit,
+  &google_protobuf_SourceCodeInfo_Location_msginit,
+  &google_protobuf_GeneratedCodeInfo_msginit,
+  &google_protobuf_GeneratedCodeInfo_Annotation_msginit,
+};
+
+const upb_msglayout_file google_protobuf_descriptor_proto_upb_file_layout = {
+  messages_layout,
+  NULL,
+  27,
+  0,
 };
 
 #include "upb/port_undef.inc"
diff --git a/cmake/google/protobuf/descriptor.upb.h b/cmake/google/protobuf/descriptor.upb.h
index 4767b3af68..2416bc1060 100644
--- a/cmake/google/protobuf/descriptor.upb.h
+++ b/cmake/google/protobuf/descriptor.upb.h
@@ -2038,6 +2038,8 @@ UPB_INLINE void google_protobuf_GeneratedCodeInfo_Annotation_set_end(google_prot
   *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
 }
 
+extern const upb_msglayout_file google_protobuf_descriptor_proto_upb_file_layout;
+
 #ifdef __cplusplus
 }  /* extern "C" */
 #endif
diff --git a/tests/conformance_upb_failures.txt b/tests/conformance_upb_failures.txt
index d9290df987..e69de29bb2 100644
--- a/tests/conformance_upb_failures.txt
+++ b/tests/conformance_upb_failures.txt
@@ -1 +0,0 @@
-Recommended.Proto2.JsonInput.FieldNameExtension.Validator
diff --git a/upb/decode.c b/upb/decode.c
index 2739be86e7..f7992c7165 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -298,7 +298,8 @@ static void decode_munge(int type, wireval *val) {
   }
 }
 
-static const upb_msglayout_field *upb_find_field(const upb_msglayout *l,
+static const upb_msglayout_field *upb_find_field(upb_decstate *d,
+                                                 const upb_msglayout *l,
                                                  uint32_t field_number,
                                                  int *last_field_index) {
   static upb_msglayout_field none = {0, 0, 0, 0, 0, 0};
@@ -307,23 +308,32 @@ static const upb_msglayout_field *upb_find_field(const upb_msglayout *l,
 
   size_t idx = ((size_t)field_number) - 1;  // 0 wraps to SIZE_MAX
   if (idx < l->dense_below) {
+    /* Fastest case: index into dense fields. */
     goto found;
   }
 
-  /* Resume scanning from last_field_index since fields are usually in order. */
-  int last = *last_field_index;
-  for (idx = last; idx < l->field_count; idx++) {
-    if (l->fields[idx].number == field_number) {
-      goto found;
+  if (l->dense_below < l->field_count) {
+    /* Linear search non-dense fields. Resume scanning from last_field_index
+     * since fields are usually in order. */
+    int last = *last_field_index;
+    for (idx = last; idx < l->field_count; idx++) {
+      if (l->fields[idx].number == field_number) {
+        goto found;
+      }
     }
-  }
 
-  for (idx = 0; idx < last; idx++) {
-    if (l->fields[idx].number == field_number) {
-      goto found;
+    for (idx = 0; idx < last; idx++) {
+      if (l->fields[idx].number == field_number) {
+        goto found;
+      }
     }
   }
 
+  if (l->ext == _UPB_MSGEXT_EXTENDABLE && d->extreg) {
+    const upb_msglayout_ext *ext = _upb_extreg_get(d->extreg, l, field_number);
+    if (ext) return &ext->field;
+  }
+
   return &none; /* Unknown field. */
 
  found:
@@ -332,10 +342,9 @@ static const upb_msglayout_field *upb_find_field(const upb_msglayout *l,
   return &l->fields[idx];
 }
 
-static upb_msg *decode_newsubmsg(upb_decstate *d,
-                                 upb_msglayout const *const *submsgs,
+static upb_msg *decode_newsubmsg(upb_decstate *d, const upb_msglayout_sub *subs,
                                  const upb_msglayout_field *field) {
-  const upb_msglayout *subl = submsgs[field->submsg_index];
+  const upb_msglayout *subl = subs[field->submsg_index].submsg;
   return _upb_msg_new_inl(subl, &d->arena);
 }
 
@@ -365,10 +374,10 @@ static const char *decode_readstr(upb_decstate *d, const char *ptr, int size,
 
 UPB_FORCEINLINE
 static const char *decode_tosubmsg(upb_decstate *d, const char *ptr,
-                                   upb_msg *submsg, 
-                                   upb_msglayout const *const *submsgs,
+                                   upb_msg *submsg,
+                                   const upb_msglayout_sub *subs,
                                    const upb_msglayout_field *field, int size) {
-  const upb_msglayout *subl = submsgs[field->submsg_index];
+  const upb_msglayout *subl = subs[field->submsg_index].submsg;
   int saved_delta = decode_pushlimit(d, ptr, size);
   if (--d->depth < 0) decode_err(d);
   if (!decode_isdone(d, &ptr)) {
@@ -398,15 +407,15 @@ static const char *decode_group(upb_decstate *d, const char *ptr,
 UPB_FORCEINLINE
 static const char *decode_togroup(upb_decstate *d, const char *ptr,
                                   upb_msg *submsg,
-                                  upb_msglayout const *const *submsgs,
+                                  const upb_msglayout_sub *subs,
                                   const upb_msglayout_field *field) {
-  const upb_msglayout *subl = submsgs[field->submsg_index];
+  const upb_msglayout *subl = subs[field->submsg_index].submsg;
   return decode_group(d, ptr, submsg, subl, field->number);
 }
 
 static const char *decode_toarray(upb_decstate *d, const char *ptr,
                                   upb_msg *msg,
-                                  upb_msglayout const *const *submsgs,
+                                  const upb_msglayout_sub *subs,
                                   const upb_msglayout_field *field, wireval *val,
                                   int op) {
   upb_array **arrp = UPB_PTR_AT(msg, field->offset, void);
@@ -442,14 +451,14 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
     }
     case OP_SUBMSG: {
       /* Append submessage / group. */
-      upb_msg *submsg = decode_newsubmsg(d, submsgs, field);
+      upb_msg *submsg = decode_newsubmsg(d, subs, field);
       *UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(void *), upb_msg *) =
           submsg;
       arr->len++;
       if (UPB_UNLIKELY(field->descriptortype == UPB_DTYPE_GROUP)) {
-        return decode_togroup(d, ptr, submsg, submsgs, field);
+        return decode_togroup(d, ptr, submsg, subs, field);
       } else {
-        return decode_tosubmsg(d, ptr, submsg, submsgs, field, val->size);
+        return decode_tosubmsg(d, ptr, submsg, subs, field, val->size);
       }
     }
     case OP_FIXPCK_LG2(2):
@@ -495,12 +504,13 @@ static const char *decode_toarray(upb_decstate *d, const char *ptr,
 }
 
 static const char *decode_tomap(upb_decstate *d, const char *ptr, upb_msg *msg,
-                                upb_msglayout const *const *submsgs,
-                                const upb_msglayout_field *field, wireval *val) {
+                                const upb_msglayout_sub *subs,
+                                const upb_msglayout_field *field,
+                                wireval *val) {
   upb_map **map_p = UPB_PTR_AT(msg, field->offset, upb_map *);
   upb_map *map = *map_p;
   upb_map_entry ent;
-  const upb_msglayout *entry = submsgs[field->submsg_index];
+  const upb_msglayout *entry = subs[field->submsg_index].submsg;
 
   if (!map) {
     /* Lazily create map. */
@@ -520,16 +530,16 @@ static const char *decode_tomap(upb_decstate *d, const char *ptr, upb_msg *msg,
   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(_upb_msg_new(entry->submsgs[0], &d->arena));
+    ent.v.val = upb_value_ptr(_upb_msg_new(entry->subs[0].submsg, &d->arena));
   }
 
-  ptr = decode_tosubmsg(d, ptr, &ent.k, submsgs, field, val->size);
+  ptr = decode_tosubmsg(d, ptr, &ent.k, subs, field, val->size);
   _upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, &d->arena);
   return ptr;
 }
 
 static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg,
-                                upb_msglayout const *const *submsgs,
+                                const upb_msglayout_sub *subs,
                                 const upb_msglayout_field *field, wireval *val,
                                 int op) {
   void *mem = UPB_PTR_AT(msg, field->offset, void);
@@ -553,13 +563,13 @@ static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg,
       upb_msg **submsgp = mem;
       upb_msg *submsg = *submsgp;
       if (!submsg) {
-        submsg = decode_newsubmsg(d, submsgs, field);
+        submsg = decode_newsubmsg(d, subs, field);
         *submsgp = submsg;
       }
       if (UPB_UNLIKELY(type == UPB_DTYPE_GROUP)) {
-        ptr = decode_togroup(d, ptr, submsg, submsgs, field);
+        ptr = decode_togroup(d, ptr, submsg, subs, field);
       } else {
-        ptr = decode_tosubmsg(d, ptr, submsg, submsgs, field, val->size);
+        ptr = decode_tosubmsg(d, ptr, submsg, subs, field, val->size);
       }
       break;
     }
@@ -616,7 +626,7 @@ static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
     field_number = tag >> 3;
     wire_type = tag & 7;
 
-    field = upb_find_field(layout, field_number, &last_field_index);
+    field = upb_find_field(d, layout, field_number, &last_field_index);
 
     switch (wire_type) {
       case UPB_WIRE_TYPE_VARINT:
@@ -664,16 +674,29 @@ static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
     }
 
     if (op >= 0) {
+      /* Known field, possibly an extension. */
+      upb_msg *field_msg = msg;
+      const upb_msglayout_sub *subs = layout->subs;
+      uint8_t mode = field->mode;
+
+      if (UPB_UNLIKELY(mode & _UPB_MODE_IS_EXTENSION)) {
+        const upb_msglayout_ext *ext_layout = (const upb_msglayout_ext*)field;
+        upb_msg_ext *ext = _upb_msg_getorcreateext(msg, ext_layout, &d->arena);
+        if (UPB_UNLIKELY(!ext)) decode_err(d);
+        field_msg = &ext->data;
+        subs = &ext->ext->sub;
+      }
+
       /* Parse, using op for dispatch. */
-      switch (_upb_getmode(field)) {
+      switch (mode & 3) {
         case _UPB_MODE_ARRAY:
-          ptr = decode_toarray(d, ptr, msg, layout->submsgs, field, &val, op);
+          ptr = decode_toarray(d, ptr, field_msg, subs, field, &val, op);
           break;
         case _UPB_MODE_MAP:
-          ptr = decode_tomap(d, ptr, msg, layout->submsgs, field, &val);
+          ptr = decode_tomap(d, ptr, field_msg, subs, field, &val);
           break;
         case _UPB_MODE_SCALAR:
-          ptr = decode_tomsg(d, ptr, msg, layout->submsgs, field, &val, op);
+          ptr = decode_tomsg(d, ptr, field_msg, subs, field, &val, op);
           break;
         default:
           UPB_UNREACHABLE();
@@ -743,6 +766,7 @@ bool _upb_decode(const char *buf, size_t size, void *msg,
     state.alias = options & UPB_DECODE_ALIAS;
   }
 
+  state.extreg = extreg;
   state.limit_ptr = state.end;
   state.unknown_msg = NULL;
   state.depth = depth ? depth : 64;
diff --git a/upb/decode_internal.h b/upb/decode_internal.h
index 0492363725..6d03084b37 100644
--- a/upb/decode_internal.h
+++ b/upb/decode_internal.h
@@ -48,6 +48,7 @@ typedef struct upb_decstate {
   const char *limit_ptr;   /* = end + UPB_MIN(limit, 0) */
   upb_msg *unknown_msg;    /* If non-NULL, add unknown data at buffer flip. */
   const char *unknown;     /* Start of unknown data. */
+  const upb_extreg *extreg;  /* For looking up extensions during the parse. */
   int limit;               /* Submessage limit relative to end. */
   int depth;
   uint32_t end_group;   /* field number of END_GROUP tag, else DECODE_NOGROUP */
diff --git a/upb/def.c b/upb/def.c
index de41dcdb17..d1f3466978 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -74,6 +74,12 @@ struct upb_fielddef {
   upb_label_t label_;
 };
 
+struct upb_extrange {
+  const google_protobuf_ExtensionRangeOptions *opts;
+  int32_t start;
+  int32_t end;
+};
+
 struct upb_msgdef {
   const upb_msglayout *layout;
   const upb_filedef *file;
@@ -83,17 +89,17 @@ struct upb_msgdef {
   upb_inttable itof;
   upb_strtable ntof;
 
+  const upb_extrange *ext_ranges;
   const upb_fielddef *fields;
   const upb_oneofdef *oneofs;
   int field_count;
   int oneof_count;
   int real_oneof_count;
+  int ext_range_count;
 
   /* Is this a map-entry message? */
   bool map_entry;
   upb_wellknowntype_t well_known_type;
-
-  /* TODO(haberman): proper extension ranges (there can be multiple). */
 };
 
 struct upb_enumdef {
@@ -132,6 +138,7 @@ struct upb_filedef {
   const upb_msgdef *msgs;
   const upb_enumdef *enums;
   const upb_fielddef *exts;
+  const upb_msglayout_ext **ext_layouts;
   const upb_symtab *symtab;
 
   int dep_count;
@@ -145,6 +152,8 @@ struct upb_symtab {
   upb_arena *arena;
   upb_strtable syms;  /* full_name -> packed def ptr */
   upb_strtable files;  /* file_name -> upb_filedef* */
+  upb_inttable exts;   /* upb_msglayout_ext* -> upb_fielddef* */
+  upb_extreg *extreg;
   size_t bytes_loaded;
 };
 
@@ -518,9 +527,15 @@ const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f) {
 }
 
 const upb_msglayout_field *upb_fielddef_layout(const upb_fielddef *f) {
+  UPB_ASSERT(!upb_fielddef_isextension(f));
   return &f->msgdef->layout->fields[f->layout_index];
 }
 
+const upb_msglayout_ext *_upb_fielddef_extlayout(const upb_fielddef *f) {
+  UPB_ASSERT(upb_fielddef_isextension(f));
+  return f->file->ext_layouts[f->layout_index];
+}
+
 bool upb_fielddef_issubmsg(const upb_fielddef *f) {
   return upb_fielddef_type(f) == UPB_TYPE_MESSAGE;
 }
@@ -651,6 +666,10 @@ int upb_msgdef_numrealoneofs(const upb_msgdef *m) {
   return m->real_oneof_count;
 }
 
+int upb_msgdef_extrangecount(const upb_msgdef *m) {
+  return m->ext_range_count;
+}
+
 int upb_msgdef_fieldcount(const upb_msgdef *m) {
   return m->field_count;
 }
@@ -667,6 +686,11 @@ const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m) {
   return m->layout;
 }
 
+const upb_extrange *upb_msgdef_extrange(const upb_msgdef *m, int i) {
+  UPB_ASSERT(i >= 0 && i < m->ext_range_count);
+  return &m->ext_ranges[i];
+}
+
 const upb_fielddef *upb_msgdef_field(const upb_msgdef *m, int i) {
   UPB_ASSERT(i >= 0 && i < m->field_count);
   return &m->fields[i];
@@ -884,12 +908,19 @@ upb_symtab *upb_symtab_new(void) {
   s->bytes_loaded = 0;
 
   if (!upb_strtable_init(&s->syms, 32, s->arena) ||
-      !upb_strtable_init(&s->files, 4, s->arena)) {
-    upb_arena_free(s->arena);
-    upb_gfree(s);
-    s = NULL;
+      !upb_strtable_init(&s->files, 4, s->arena) ||
+      !upb_inttable_init(&s->exts, s->arena)) {
+    goto err;
   }
+
+  s->extreg = upb_extreg_new(s->arena);
+  if (!s->extreg) goto err;
   return s;
+
+err:
+  upb_arena_free(s->arena);
+  upb_gfree(s);
+  return NULL;
 }
 
 const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym) {
@@ -898,11 +929,16 @@ const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym) {
       unpack_def(v, UPB_DEFTYPE_MSG) : NULL;
 }
 
+static const void *symtab_lookup2(const upb_symtab *s, const char *sym,
+                                  size_t size, upb_deftype_t type) {
+  upb_value v;
+  return upb_strtable_lookup2(&s->syms, sym, size, &v) ? unpack_def(v, type)
+                                                       : NULL;
+}
+
 const upb_msgdef *upb_symtab_lookupmsg2(const upb_symtab *s, const char *sym,
                                         size_t len) {
-  upb_value v;
-  return upb_strtable_lookup2(&s->syms, sym, len, &v) ?
-      unpack_def(v, UPB_DEFTYPE_MSG) : NULL;
+  return symtab_lookup2(s, sym, len, UPB_DEFTYPE_MSG);
 }
 
 const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym) {
@@ -925,6 +961,11 @@ const upb_fielddef *upb_symtab_lookupext(const upb_symtab *s, const char *sym) {
       unpack_def(v, UPB_DEFTYPE_FIELD) : NULL;
 }
 
+const upb_fielddef *upb_symtab_lookupext2(const upb_symtab *s, const char *sym,
+                                          size_t size) {
+  return symtab_lookup2(s, sym, size, UPB_DEFTYPE_FIELD);
+}
+
 const upb_filedef *upb_symtab_lookupfile(const upb_symtab *s, const char *name) {
   upb_value v;
   return upb_strtable_lookup(&s->files, name, &v) ? upb_value_getconstptr(v)
@@ -955,7 +996,10 @@ typedef struct {
   upb_symtab *symtab;
   upb_filedef *file;              /* File we are building. */
   upb_arena *arena;               /* Allocate defs here. */
-  const upb_msglayout **layouts;  /* NULL if we should build layouts. */
+  const upb_msglayout_file *layout;  /* NULL if we should build layouts. */
+  int enum_count;                 /* Count of enums built so far. */
+  int msg_count;                  /* Count of messages built so far. */
+  int ext_count;                  /* Count of extensions built so far. */
   upb_status *status;             /* Record errors here. */
   jmp_buf err;                    /* longjmp() on error. */
 } symtab_addctx;
@@ -1095,16 +1139,42 @@ static void fill_fieldlayout(upb_msglayout_field *field, const upb_fielddef *f)
   }
 
   if (upb_fielddef_ismap(f)) {
-    field->mode = _UPB_MODE_MAP;
+    field->mode = _UPB_MODE_MAP | (_UPB_REP_PTR << 6);
   } else if (upb_fielddef_isseq(f)) {
-    field->mode = _UPB_MODE_ARRAY;
+    field->mode = _UPB_MODE_ARRAY | (_UPB_REP_PTR << 6);
   } else {
-    field->mode = _UPB_MODE_SCALAR;
+    /* Maps descriptor type -> elem_size_lg2.  */
+    static const uint8_t sizes[] = {
+        -1,               /* invalid descriptor type */
+        _UPB_REP_8BYTE,  /* DOUBLE */
+        _UPB_REP_4BYTE,   /* FLOAT */
+        _UPB_REP_8BYTE,   /* INT64 */
+        _UPB_REP_8BYTE,  /* UINT64 */
+        _UPB_REP_4BYTE,   /* INT32 */
+        _UPB_REP_8BYTE,  /* FIXED64 */
+        _UPB_REP_4BYTE,  /* FIXED32 */
+        _UPB_REP_1BYTE,    /* BOOL */
+        _UPB_REP_STRVIEW,  /* STRING */
+        _UPB_REP_PTR,  /* GROUP */
+        _UPB_REP_PTR,  /* MESSAGE */
+        _UPB_REP_STRVIEW,  /* BYTES */
+        _UPB_REP_4BYTE,  /* UINT32 */
+        _UPB_REP_4BYTE,    /* ENUM */
+        _UPB_REP_4BYTE,   /* SFIXED32 */
+        _UPB_REP_8BYTE,   /* SFIXED64 */
+        _UPB_REP_4BYTE,   /* SINT32 */
+        _UPB_REP_8BYTE,   /* SINT64 */
+    };
+    field->mode = _UPB_MODE_SCALAR | (sizes[field->descriptortype] << 6);
   }
 
   if (upb_fielddef_packed(f)) {
     field->mode |= _UPB_MODE_IS_PACKED;
   }
+
+  if (upb_fielddef_isextension(f)) {
+    field->mode |= _UPB_MODE_IS_EXTENSION;
+  }
 }
 
 /* This function is the dynamic equivalent of message_layout.{cc,h} in upbc.
@@ -1115,8 +1185,8 @@ static void make_layout(symtab_addctx *ctx, const upb_msgdef *m) {
   upb_msg_oneof_iter oit;
   size_t hasbit;
   size_t field_count = upb_msgdef_numfields(m);
-  size_t submsg_count = 0;
-  const upb_msglayout **submsgs;
+  size_t sublayout_count = 0;
+  upb_msglayout_sub *subs;
   upb_msglayout_field *fields;
 
   memset(l, 0, sizeof(*l) + sizeof(_upb_fasttable_entry));
@@ -1124,18 +1194,24 @@ static void make_layout(symtab_addctx *ctx, const upb_msgdef *m) {
   /* Count sub-messages. */
   for (size_t i = 0; i < field_count; i++) {
     if (upb_fielddef_issubmsg(&m->fields[i])) {
-      submsg_count++;
+      sublayout_count++;
     }
   }
 
   fields = symtab_alloc(ctx, field_count * sizeof(*fields));
-  submsgs = symtab_alloc(ctx, submsg_count * sizeof(*submsgs));
+  subs = symtab_alloc(ctx, sublayout_count * sizeof(*subs));
 
   l->field_count = upb_msgdef_numfields(m);
   l->fields = fields;
-  l->submsgs = submsgs;
+  l->subs = subs;
   l->table_mask = 0;
 
+  if (upb_msgdef_extrangecount(m) > 0) {
+    l->ext = _UPB_MSGEXT_EXTENDABLE;
+  } else {
+    l->ext = _UPB_MSGEXT_NONE;
+  }
+
   /* TODO(haberman): initialize fast tables so that reflection-based parsing
    * can get the same speeds as linked-in types. */
   l->fasttable[0].field_parser = &fastdecode_generic;
@@ -1159,7 +1235,7 @@ static void make_layout(symtab_addctx *ctx, const upb_msgdef *m) {
     fields[1].submsg_index = 0;
 
     if (upb_fielddef_type(val) == UPB_TYPE_MESSAGE) {
-      submsgs[0] = upb_fielddef_msgsubdef(val)->layout;
+      subs[0].submsg = upb_fielddef_msgsubdef(val)->layout;
     }
 
     l->field_count = 2;
@@ -1178,7 +1254,7 @@ static void make_layout(symtab_addctx *ctx, const upb_msgdef *m) {
    */
 
   /* Allocate hasbits and set basic field attributes. */
-  submsg_count = 0;
+  sublayout_count = 0;
   for (upb_msg_field_begin(&it, m), hasbit = 0;
        !upb_msg_field_done(&it);
        upb_msg_field_next(&it)) {
@@ -1189,8 +1265,8 @@ static void make_layout(symtab_addctx *ctx, const upb_msgdef *m) {
 
     if (upb_fielddef_issubmsg(f)) {
       const upb_msgdef *subm = upb_fielddef_msgsubdef(f);
-      field->submsg_index = submsg_count++;
-      submsgs[field->submsg_index] = subm->layout;
+      field->submsg_index = sublayout_count++;
+      subs[field->submsg_index].submsg = subm->layout;
     }
 
     if (upb_fielddef_haspresence(f) && !upb_fielddef_realcontainingoneof(f)) {
@@ -1656,12 +1732,11 @@ static void create_fielddef(
       upb_strtable_insert(&m->ntof, json_name, json_size, json_v, ctx->arena);
     }
 
-    if (ctx->layouts) {
+    if (ctx->layout) {
       const upb_msglayout_field *fields = m->layout->fields;
       int count = m->layout->field_count;
       bool found = false;
-      int i;
-      for (i = 0; i < count; i++) {
+      for (int i = 0; i < count; i++) {
         if (fields[i].number == field_number) {
           f->layout_index = i;
           found = true;
@@ -1672,9 +1747,15 @@ static void create_fielddef(
     }
   } else {
     /* extension field. */
-    f = (upb_fielddef*)&ctx->file->exts[ctx->file->ext_count++];
+    uint16_t layout_index = ctx->ext_count++;
+    f = (upb_fielddef*)&ctx->file->exts[layout_index];
+    f->layout_index = layout_index;
     f->is_extension_ = true;
     symtab_add(ctx, full_name, pack_def(f, UPB_DEFTYPE_FIELD));
+    if (ctx->layout) {
+      UPB_ASSERT(ctx->file->ext_layouts[f->layout_index]->field.number ==
+                 field_number);
+    }
   }
 
   f->full_name = full_name;
@@ -1763,7 +1844,7 @@ static void create_enumdef(
   name = google_protobuf_EnumDescriptorProto_name(enum_proto);
   check_ident(ctx, name, false);
 
-  e = (upb_enumdef*)&ctx->file->enums[ctx->file->enum_count++];
+  e = (upb_enumdef*)&ctx->file->enums[ctx->enum_count++];
   e->full_name = makefullname(ctx, prefix, name);
   symtab_add(ctx, e->full_name, pack_def(e, UPB_DEFTYPE_ENUM));
 
@@ -1816,18 +1897,23 @@ static void create_msgdef(symtab_addctx *ctx, const char *prefix,
   const google_protobuf_FieldDescriptorProto *const *fields;
   const google_protobuf_EnumDescriptorProto *const *enums;
   const google_protobuf_DescriptorProto *const *msgs;
-  size_t i, n_oneof, n_field, n;
+  const google_protobuf_DescriptorProto_ExtensionRange *const *ext_ranges;
+  size_t i, n_oneof, n_field, n_ext_range, n;
   upb_strview name;
 
   name = google_protobuf_DescriptorProto_name(msg_proto);
   check_ident(ctx, name, false);
 
-  m = (upb_msgdef*)&ctx->file->msgs[ctx->file->msg_count++];
+  int msg_index = ctx->msg_count;
+  m = (upb_msgdef*)&ctx->file->msgs[msg_index];
   m->full_name = makefullname(ctx, prefix, name);
+  ctx->msg_count++;
   symtab_add(ctx, m->full_name, pack_def(m, UPB_DEFTYPE_MSG));
 
   oneofs = google_protobuf_DescriptorProto_oneof_decl(msg_proto, &n_oneof);
   fields = google_protobuf_DescriptorProto_field(msg_proto, &n_field);
+  ext_ranges =
+      google_protobuf_DescriptorProto_extension_range(msg_proto, &n_ext_range);
 
   CHK_OOM(upb_inttable_init(&m->itof, ctx->arena));
   CHK_OOM(upb_strtable_init(&m->ntof, n_oneof + n_field, ctx->arena));
@@ -1841,9 +1927,10 @@ static void create_msgdef(symtab_addctx *ctx, const char *prefix,
     m->map_entry = google_protobuf_MessageOptions_map_entry(options);
   }
 
-  if (ctx->layouts) {
-    m->layout = *ctx->layouts;
-    ctx->layouts++;
+  if (ctx->layout) {
+    /* create_fielddef() below depends on this being set. */
+    m->layout = ctx->layout->msgs[msg_index];
+    UPB_ASSERT(n_field == m->layout->field_count);
   } else {
     /* Allocate now (to allow cross-linking), populate later. */
     m->layout = symtab_alloc(
@@ -1862,6 +1949,15 @@ static void create_msgdef(symtab_addctx *ctx, const char *prefix,
     create_fielddef(ctx, m->full_name, m, fields[i]);
   }
 
+  m->ext_range_count = n_ext_range;
+  m->ext_ranges = symtab_alloc(ctx, sizeof(*m->ext_ranges) * n_ext_range);
+  for (i = 0; i < n_ext_range; i++) {
+    const google_protobuf_DescriptorProto_ExtensionRange *r = ext_ranges[i];
+    upb_extrange *r_def = (upb_extrange*)&m->ext_ranges[i];
+    r_def->start = google_protobuf_DescriptorProto_ExtensionRange_start(r);
+    r_def->end = google_protobuf_DescriptorProto_ExtensionRange_end(r);
+  }
+
   finalize_oneofs(ctx, m);
   assign_msg_wellknowntype(m);
   upb_inttable_compact(&m->itof, ctx->arena);
@@ -1873,6 +1969,11 @@ static void create_msgdef(symtab_addctx *ctx, const char *prefix,
     create_enumdef(ctx, m->full_name, enums[i]);
   }
 
+  fields = google_protobuf_DescriptorProto_extension(msg_proto, &n);
+  for (i = 0; i < n; i++) {
+    create_fielddef(ctx, m->full_name, NULL, fields[i]);
+  }
+
   msgs = google_protobuf_DescriptorProto_nested_type(msg_proto, &n);
   for (i = 0; i < n; i++) {
     create_msgdef(ctx, m->full_name, msgs[i]);
@@ -1929,6 +2030,22 @@ static void resolve_fielddef(symtab_addctx *ctx, const char *prefix,
 
     name = google_protobuf_FieldDescriptorProto_extendee(field_proto);
     f->msgdef = symtab_resolve(ctx, f, prefix, name, UPB_DEFTYPE_MSG);
+
+    const upb_msglayout_ext *ext = ctx->file->ext_layouts[f->layout_index];
+    if (ctx->layout) {
+      UPB_ASSERT(upb_fielddef_number(f) == ext->field.number);
+    } else {
+      upb_msglayout_ext *mut_ext = (upb_msglayout_ext*)ext;
+      fill_fieldlayout(&mut_ext->field, f);
+      mut_ext->field.presence = 0;
+      mut_ext->field.offset = 0;
+      mut_ext->field.submsg_index = 0;
+      mut_ext->extendee = f->msgdef->layout;
+      mut_ext->sub.submsg = f->sub.msgdef->layout;
+    }
+
+    CHK_OOM(upb_inttable_insert(&ctx->symtab->exts, (uintptr_t)ext,
+                                upb_value_constptr(f), ctx->arena));
   }
 
   if ((upb_fielddef_issubmsg(f) || f->type_ == UPB_DESCRIPTOR_TYPE_ENUM) &&
@@ -1987,10 +2104,26 @@ static void build_filedef(
   file->enums = symtab_alloc(ctx, sizeof(*file->enums) * file->enum_count);
   file->exts = symtab_alloc(ctx, sizeof(*file->exts) * file->ext_count);
 
-  /* In the second pass we increment these as defs are added. */
-  file->msg_count = 0;
-  file->enum_count = 0;
-  file->ext_count = 0;
+  ctx->msg_count = 0;
+  ctx->enum_count = 0;
+  ctx->ext_count = 0;
+
+  if (ctx->layout) {
+    /* We are using the ext layouts that were passed in. */
+    file->ext_layouts = ctx->layout->exts;
+    if (ctx->layout->ext_count != file->ext_count) {
+      symtab_errf(ctx, "Extension count did not match layout (%d vs %d)",
+                  ctx->layout->ext_count, file->ext_count);
+    }
+  } else {
+    /* We are building ext layouts from scratch. */
+    file->ext_layouts =
+        symtab_alloc(ctx, sizeof(*file->ext_layouts) * file->ext_count);
+    upb_msglayout_ext *ext = symtab_alloc(ctx, sizeof(*ext) * file->ext_count);
+    for (int i = 0; i < file->ext_count; i++) {
+      file->ext_layouts[i] = &ext[i];
+    }
+  }
 
   if (!google_protobuf_FileDescriptorProto_has_name(file_proto)) {
     symtab_errf(ctx, "File has no name");
@@ -2071,11 +2204,12 @@ static void build_filedef(
 
   /* Create extensions. */
   exts = google_protobuf_FileDescriptorProto_extension(file_proto, &n);
-  file->exts = symtab_alloc(ctx, sizeof(*file->exts) * n);
   for (i = 0; i < n; i++) {
     create_fielddef(ctx, file->package, NULL, exts[i]);
   }
 
+  UPB_ASSERT(ctx->ext_count == file->ext_count);
+
   /* Now that all names are in the table, build layouts and resolve refs. */
   for (i = 0; i < (size_t)file->ext_count; i++) {
     resolve_fielddef(ctx, file->package, (upb_fielddef*)&file->exts[i]);
@@ -2089,25 +2223,28 @@ static void build_filedef(
     }
   }
 
-  if (!ctx->layouts) {
+  if (!ctx->layout) {
     for (i = 0; i < (size_t)file->msg_count; i++) {
       const upb_msgdef *m = &file->msgs[i];
       make_layout(ctx, m);
     }
   }
+
+  CHK_OOM(
+      _upb_extreg_add(ctx->symtab->extreg, file->ext_layouts, file->ext_count));
 }
 
-static void remove_filedef(upb_symtab *s, upb_filedef *file) {
+static void remove_filedef(symtab_addctx *ctx, upb_symtab *s, upb_filedef *file) {
   int i;
-  for (i = 0; i < file->msg_count; i++) {
+  for (i = 0; i < ctx->msg_count; i++) {
     const char *name = file->msgs[i].full_name;
     upb_strtable_remove(&s->syms, name, strlen(name), NULL);
   }
-  for (i = 0; i < file->enum_count; i++) {
+  for (i = 0; i < ctx->enum_count; i++) {
     const char *name = file->enums[i].full_name;
     upb_strtable_remove(&s->syms, name, strlen(name), NULL);
   }
-  for (i = 0; i < file->ext_count; i++) {
+  for (i = 0; i < ctx->ext_count; i++) {
     const char *name = file->exts[i].full_name;
     upb_strtable_remove(&s->syms, name, strlen(name), NULL);
   }
@@ -2115,7 +2252,7 @@ static void remove_filedef(upb_symtab *s, upb_filedef *file) {
 
 static const upb_filedef *_upb_symtab_addfile(
     upb_symtab *s, const google_protobuf_FileDescriptorProto *file_proto,
-    const upb_msglayout **layouts, upb_status *status) {
+    const upb_msglayout_file *layout, upb_status *status) {
   symtab_addctx ctx;
   upb_strview name = google_protobuf_FileDescriptorProto_name(file_proto);
 
@@ -2126,7 +2263,7 @@ static const upb_filedef *_upb_symtab_addfile(
   }
 
   ctx.symtab = s;
-  ctx.layouts = layouts;
+  ctx.layout = layout;
   ctx.status = status;
   ctx.file = NULL;
   ctx.arena = upb_arena_new();
@@ -2139,7 +2276,7 @@ static const upb_filedef *_upb_symtab_addfile(
   if (UPB_UNLIKELY(UPB_SETJMP(ctx.err))) {
     UPB_ASSERT(!upb_ok(status));
     if (ctx.file) {
-      remove_filedef(s, ctx.file);
+      remove_filedef(&ctx, s, ctx.file);
       ctx.file = NULL;
     }
   } else {
@@ -2198,14 +2335,16 @@ bool _upb_symtab_loaddefinit(upb_symtab *s, const upb_def_init *init) {
     goto err;
   }
 
-  if (!_upb_symtab_addfile(s, file, init->layouts, &status)) goto err;
+  if (!_upb_symtab_addfile(s, file, init->layout, &status)) goto err;
 
   upb_arena_free(arena);
   return true;
 
 err:
-  fprintf(stderr, "Error loading compiled-in descriptor: %s\n",
-          upb_status_errmsg(&status));
+  fprintf(stderr,
+          "Error loading compiled-in descriptor for file '%s' (this should "
+          "never happen): %s\n",
+          init->filename, upb_status_errmsg(&status));
   upb_arena_free(arena);
   return false;
 }
@@ -2218,4 +2357,16 @@ upb_arena *_upb_symtab_arena(const upb_symtab *s) {
   return s->arena;
 }
 
+const upb_fielddef *_upb_symtab_lookupextfield(const upb_symtab *s,
+                                               const upb_msglayout_ext *ext) {
+  upb_value v;
+  bool ok = upb_inttable_lookup(&s->exts, (uintptr_t)ext, &v);
+  UPB_ASSERT(ok);
+  return upb_value_getconstptr(v);
+}
+
+const upb_extreg *upb_symtab_extreg(const upb_symtab *s) {
+  return s->extreg;
+}
+
 #undef CHK_OOM
diff --git a/upb/def.h b/upb/def.h
index 563c946b0c..b548e2b360 100644
--- a/upb/def.h
+++ b/upb/def.h
@@ -56,6 +56,8 @@ struct upb_enumdef;
 typedef struct upb_enumdef upb_enumdef;
 struct upb_enumvaldef;
 typedef struct upb_enumvaldef upb_enumvaldef;
+struct upb_extrange;
+typedef struct upb_extrange upb_extrange;
 struct upb_fielddef;
 typedef struct upb_fielddef upb_fielddef;
 struct upb_filedef;
@@ -137,6 +139,7 @@ bool upb_fielddef_haspresence(const upb_fielddef *f);
 const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f);
 const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f);
 const upb_msglayout_field *upb_fielddef_layout(const upb_fielddef *f);
+const upb_msglayout_ext *_upb_fielddef_extlayout(const upb_fielddef *f);
 
 /* upb_oneofdef ***************************************************************/
 
@@ -201,8 +204,10 @@ 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_extrangecount(const upb_msgdef *m);
 int upb_msgdef_fieldcount(const upb_msgdef *m);
 int upb_msgdef_oneofcount(const upb_msgdef *m);
+const upb_extrange *upb_msgdef_extrange(const upb_msgdef *m, int i);
 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);
@@ -258,6 +263,14 @@ bool upb_msg_oneof_iter_isequal(const upb_msg_oneof_iter *iter1,
                                 const upb_msg_oneof_iter *iter2);
 /* END DEPRECATED */
 
+/* upb_extrange ***************************************************************/
+
+const google_protobuf_ExtensionRangeOptions *upb_extrange_options(
+    const upb_extrange *r);
+bool upb_extrange_hasoptions(const upb_extrange *r);
+int32_t upb_extrange_start(const upb_extrange *r);
+int32_t upb_extrange_end(const upb_extrange *r);
+
 /* upb_enumdef ****************************************************************/
 
 typedef upb_strtable_iter upb_enum_iter;
@@ -321,6 +334,8 @@ const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym);
 const upb_enumvaldef *upb_symtab_lookupenumval(const upb_symtab *s,
                                                const char *sym);
 const upb_fielddef *upb_symtab_lookupext(const upb_symtab *s, const char *sym);
+const upb_fielddef *upb_symtab_lookupext2(const upb_symtab *s, const char *sym,
+                                         size_t len);
 const upb_filedef *upb_symtab_lookupfile(const upb_symtab *s, const char *name);
 const upb_filedef *upb_symtab_lookupfile2(
     const upb_symtab *s, const char *name, size_t len);
@@ -330,11 +345,14 @@ const upb_filedef *upb_symtab_addfile(
     upb_status *status);
 size_t _upb_symtab_bytesloaded(const upb_symtab *s);
 upb_arena *_upb_symtab_arena(const upb_symtab *s);
+const upb_fielddef *_upb_symtab_lookupextfield(const upb_symtab *s,
+                                               const upb_msglayout_ext *ext);
+const upb_extreg *upb_symtab_extreg(const upb_symtab *s);
 
 /* For generated code only: loads a generated descriptor. */
 typedef struct upb_def_init {
   struct upb_def_init **deps;     /* Dependencies of this file. */
-  const upb_msglayout **layouts;  /* Pre-order layouts of all messages. */
+  const upb_msglayout_file *layout;
   const char *filename;
   upb_strview descriptor;         /* Serialized descriptor. */
 } upb_def_init;
diff --git a/upb/encode.c b/upb/encode.c
index 6c5bfaf6ff..25f918afba 100644
--- a/upb/encode.c
+++ b/upb/encode.c
@@ -187,17 +187,14 @@ static void encode_message(upb_encstate *e, const upb_msg *msg,
                            const upb_msglayout *m, size_t *size);
 
 static void encode_scalar(upb_encstate *e, const void *_field_mem,
-                          const upb_msglayout *m, const upb_msglayout_field *f,
-                          bool skip_zero_value) {
+                          const upb_msglayout_sub *subs,
+                          const upb_msglayout_field *f) {
   const char *field_mem = _field_mem;
   int wire_type;
 
 #define CASE(ctype, type, wtype, encodeval) \
   {                                         \
     ctype val = *(ctype *)field_mem;        \
-    if (skip_zero_value && val == 0) {      \
-      return;                               \
-    }                                       \
     encode_##type(e, encodeval);            \
     wire_type = wtype;                      \
     break;                                  \
@@ -231,9 +228,6 @@ static void encode_scalar(upb_encstate *e, const void *_field_mem,
     case UPB_DESCRIPTOR_TYPE_STRING:
     case UPB_DESCRIPTOR_TYPE_BYTES: {
       upb_strview view = *(upb_strview*)field_mem;
-      if (skip_zero_value && view.size == 0) {
-        return;
-      }
       encode_bytes(e, view.data, view.size);
       encode_varint(e, view.size);
       wire_type = UPB_WIRE_TYPE_DELIMITED;
@@ -242,7 +236,7 @@ static void encode_scalar(upb_encstate *e, const void *_field_mem,
     case UPB_DESCRIPTOR_TYPE_GROUP: {
       size_t size;
       void *submsg = *(void **)field_mem;
-      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      const upb_msglayout *subm = subs[f->submsg_index].submsg;
       if (submsg == NULL) {
         return;
       }
@@ -256,7 +250,7 @@ static void encode_scalar(upb_encstate *e, const void *_field_mem,
     case UPB_DESCRIPTOR_TYPE_MESSAGE: {
       size_t size;
       void *submsg = *(void **)field_mem;
-      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      const upb_msglayout *subm = subs[f->submsg_index].submsg;
       if (submsg == NULL) {
         return;
       }
@@ -276,7 +270,8 @@ static void encode_scalar(upb_encstate *e, const void *_field_mem,
 }
 
 static void encode_array(upb_encstate *e, const upb_msg *msg,
-                         const upb_msglayout *m, const upb_msglayout_field *f) {
+                         const upb_msglayout_sub *subs,
+                         const upb_msglayout_field *f) {
   const upb_array *arr = *UPB_PTR_AT(msg, f->offset, upb_array*);
   bool packed = f->mode & _UPB_MODE_IS_PACKED;
   size_t pre_len = e->limit - e->ptr;
@@ -344,7 +339,7 @@ static void encode_array(upb_encstate *e, const upb_msg *msg,
     case UPB_DESCRIPTOR_TYPE_GROUP: {
       const void *const*start = _upb_array_constptr(arr);
       const void *const*ptr = start + arr->len;
-      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      const upb_msglayout *subm = subs[f->submsg_index].submsg;
       if (--e->depth == 0) encode_err(e);
       do {
         size_t size;
@@ -359,7 +354,7 @@ static void encode_array(upb_encstate *e, const upb_msg *msg,
     case UPB_DESCRIPTOR_TYPE_MESSAGE: {
       const void *const*start = _upb_array_constptr(arr);
       const void *const*ptr = start + arr->len;
-      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      const upb_msglayout *subm = subs[f->submsg_index].submsg;
       if (--e->depth == 0) encode_err(e);
       do {
         size_t size;
@@ -387,17 +382,18 @@ static void encode_mapentry(upb_encstate *e, uint32_t number,
   const upb_msglayout_field *val_field = &layout->fields[1];
   size_t pre_len = e->limit - e->ptr;
   size_t size;
-  encode_scalar(e, &ent->v, layout, val_field, false);
-  encode_scalar(e, &ent->k, layout, key_field, false);
+  encode_scalar(e, &ent->v, layout->subs, val_field);
+  encode_scalar(e, &ent->k, layout->subs, key_field);
   size = (e->limit - e->ptr) - pre_len;
   encode_varint(e, size);
   encode_tag(e, number, UPB_WIRE_TYPE_DELIMITED);
 }
 
 static void encode_map(upb_encstate *e, const upb_msg *msg,
-                       const upb_msglayout *m, const upb_msglayout_field *f) {
+                       const upb_msglayout_sub *subs,
+                       const upb_msglayout_field *f) {
   const upb_map *map = *UPB_PTR_AT(msg, f->offset, const upb_map*);
-  const upb_msglayout *layout = m->submsgs[f->submsg_index];
+  const upb_msglayout *layout = subs[f->submsg_index].submsg;
   UPB_ASSERT(layout->field_count == 2);
 
   if (map == NULL) return;
@@ -425,28 +421,65 @@ static void encode_map(upb_encstate *e, const upb_msg *msg,
   }
 }
 
-static void encode_scalarfield(upb_encstate *e, const char *msg,
-                               const upb_msglayout *m,
-                               const upb_msglayout_field *f) {
-  bool skip_empty = false;
+static bool encode_shouldencode(upb_encstate *e, const upb_msg *msg,
+                                const upb_msglayout_sub *subs,
+                                const upb_msglayout_field *f) {
   if (f->presence == 0) {
-    /* Proto3 presence. */
-    skip_empty = true;
+    /* Proto3 presence or map/array. */
+    const void *mem = UPB_PTR_AT(msg, f->offset, void);
+    switch (f->mode >> 6) {
+      case _UPB_REP_1BYTE: {
+        char ch;
+        memcpy(&ch, mem, 1);
+        return ch != 0;
+      }
+      case _UPB_REP_4BYTE: {
+        uint32_t u32;
+        memcpy(&u32, mem, 4);
+        return u32 != 0;
+      }
+      case _UPB_REP_8BYTE: {
+        uint64_t u64;
+        memcpy(&u64, mem, 8);
+        return u64 != 0;
+      }
+      case _UPB_REP_STRVIEW: {
+        const upb_strview *str = (const upb_strview*)mem;
+        return str->size != 0;
+      }
+      default:
+        UPB_UNREACHABLE();
+    }
   } else if (f->presence > 0) {
     /* Proto2 presence: hasbit. */
-    if (!_upb_hasbit_field(msg, f)) return;
+    return _upb_hasbit_field(msg, f);
   } else {
     /* Field is in a oneof. */
-    if (_upb_getoneofcase_field(msg, f) != f->number) return;
+    return _upb_getoneofcase_field(msg, f) == f->number;
+  }
+}
+
+static void encode_field(upb_encstate *e, const upb_msg *msg,
+                         const upb_msglayout_sub *subs,
+                         const upb_msglayout_field *field) {
+  switch (_upb_getmode(field)) {
+    case _UPB_MODE_ARRAY:
+      encode_array(e, msg, subs, field);
+      break;
+    case _UPB_MODE_MAP:
+      encode_map(e, msg, subs, field);
+      break;
+    case _UPB_MODE_SCALAR:
+      encode_scalar(e, UPB_PTR_AT(msg, field->offset, void), subs, field);
+      break;
+    default:
+      UPB_UNREACHABLE();
   }
-  encode_scalar(e, msg + f->offset, m, f, skip_empty);
 }
 
 static void encode_message(upb_encstate *e, const upb_msg *msg,
                            const upb_msglayout *m, size_t *size) {
   size_t pre_len = e->limit - e->ptr;
-  const upb_msglayout_field *f = &m->fields[m->field_count];
-  const upb_msglayout_field *first = &m->fields[0];
 
   if ((e->options & UPB_ENCODE_SKIPUNKNOWN) == 0) {
     size_t unknown_size;
@@ -457,20 +490,26 @@ static void encode_message(upb_encstate *e, const upb_msg *msg,
     }
   }
 
+  if (m->ext != _UPB_MSGEXT_NONE) {
+    /* Encode all extensions together. Unlike C++, we do not attempt to keep
+     * these in field number order relative to normal fields or even to each
+     * other. */
+    size_t ext_count;
+    const upb_msg_ext *ext = _upb_msg_getexts(msg, &ext_count);
+    const upb_msg_ext *end = ext + ext_count;
+    if (ext_count) {
+      for (; ext != end; ext++) {
+        encode_field(e, &ext->data, &ext->ext->sub, &ext->ext->field);
+      }
+    }
+  }
+
+  const upb_msglayout_field *f = &m->fields[m->field_count];
+  const upb_msglayout_field *first = &m->fields[0];
   while (f != first) {
     f--;
-    switch (_upb_getmode(f)) {
-      case _UPB_MODE_ARRAY:
-        encode_array(e, msg, m, f);
-        break;
-      case _UPB_MODE_MAP:
-        encode_map(e, msg, m, f);
-        break;
-      case _UPB_MODE_SCALAR:
-        encode_scalarfield(e, msg, m, f);
-        break;
-      default:
-        UPB_UNREACHABLE();
+    if (encode_shouldencode(e, msg, m->subs, f)) {
+      encode_field(e, msg, m->subs, f);
     }
   }
 
diff --git a/upb/json_decode.c b/upb/json_decode.c
index f7d1c5b2c4..cc8139e455 100644
--- a/upb/json_decode.c
+++ b/upb/json_decode.c
@@ -45,7 +45,7 @@
 typedef struct {
   const char *ptr, *end;
   upb_arena *arena;  /* TODO: should we have a tmp arena for tmp data? */
-  const upb_symtab *any_pool;
+  const upb_symtab *symtab;
   int depth;
   upb_status *status;
   jmp_buf err;
@@ -931,7 +931,20 @@ static void jsondec_field(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
 
   name = jsondec_string(d);
   jsondec_entrysep(d);
-  f = upb_msgdef_lookupjsonname(m, name.data, name.size);
+
+  if (name.size >= 2 && name.data[0] == '[' &&
+     name.data[name.size - 1] == ']') {
+   f = upb_symtab_lookupext2(d->symtab, name.data + 1, name.size - 2);
+   if (f && upb_fielddef_containingtype(f) != m) {
+     jsondec_errf(
+         d, "Extension %s extends message %s, but was seen in message %s",
+         upb_fielddef_fullname(f),
+         upb_msgdef_fullname(upb_fielddef_containingtype(f)),
+         upb_msgdef_fullname(m));
+   }
+ } else {
+   f = upb_msgdef_lookupjsonname(m, name.data, name.size);
+ }
 
   if (!f) {
     if ((d->options & UPB_JSONDEC_IGNOREUNKNOWN) == 0) {
@@ -1335,7 +1348,7 @@ static const upb_msgdef *jsondec_typeurl(jsondec *d, upb_msg *msg,
   }
 
   ptr++;
-  type_m = upb_symtab_lookupmsg2(d->any_pool, ptr, end - ptr);
+  type_m = upb_symtab_lookupmsg2(d->symtab, ptr, end - ptr);
 
   if (!type_m) {
     jsondec_err(d, "Type was not found");
@@ -1453,7 +1466,7 @@ static void jsondec_wellknown(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
 }
 
 bool upb_json_decode(const char *buf, size_t size, upb_msg *msg,
-                     const upb_msgdef *m, const upb_symtab *any_pool,
+                     const upb_msgdef *m, const upb_symtab *symtab,
                      int options, upb_arena *arena, upb_status *status) {
   jsondec d;
 
@@ -1462,7 +1475,7 @@ bool upb_json_decode(const char *buf, size_t size, upb_msg *msg,
   d.ptr = buf;
   d.end = buf + size;
   d.arena = arena;
-  d.any_pool = any_pool;
+  d.symtab = symtab;
   d.status = status;
   d.options = options;
   d.depth = 64;
diff --git a/upb/json_decode.h b/upb/json_decode.h
index 9ace310454..ea0d2d7c16 100644
--- a/upb/json_decode.h
+++ b/upb/json_decode.h
@@ -39,7 +39,7 @@ enum {
 };
 
 bool upb_json_decode(const char *buf, size_t size, upb_msg *msg,
-                     const upb_msgdef *m, const upb_symtab *any_pool,
+                     const upb_msgdef *m, const upb_symtab *symtab,
                      int options, upb_arena *arena, upb_status *status);
 
 #ifdef __cplusplus
diff --git a/upb/json_encode.c b/upb/json_encode.c
index b7d6fa9de8..bdcc9760ab 100644
--- a/upb/json_encode.c
+++ b/upb/json_encode.c
@@ -670,15 +670,19 @@ static void jsonenc_fieldval(jsonenc *e, const upb_fielddef *f,
                              upb_msgval val, bool *first) {
   const char *name;
 
-  if (e->options & UPB_JSONENC_PROTONAMES) {
-    name = upb_fielddef_name(f);
+  jsonenc_putsep(e, ",", first);
+
+  if (upb_fielddef_isextension(f)) {
+    jsonenc_printf(e, "\"[%s]\":", upb_fielddef_fullname(f));
   } else {
-    name = upb_fielddef_jsonname(f);
+    if (e->options & UPB_JSONENC_PROTONAMES) {
+      name = upb_fielddef_name(f);
+    } else {
+      name = upb_fielddef_jsonname(f);
+    }
+    jsonenc_printf(e, "\"%s\":", name);
   }
 
-  jsonenc_putsep(e, ",", first);
-  jsonenc_printf(e, "\"%s\":", name);
-
   if (upb_fielddef_ismap(f)) {
     jsonenc_map(e, val.map_val, f);
   } else if (upb_fielddef_isseq(f)) {
diff --git a/upb/msg.c b/upb/msg.c
index ad2d547e21..62580baa89 100644
--- a/upb/msg.c
+++ b/upb/msg.c
@@ -137,6 +137,18 @@ const upb_msg_ext *_upb_msg_getext(const upb_msg *msg,
   return NULL;
 }
 
+void _upb_msg_clearext(upb_msg *msg, const upb_msglayout_ext *ext_l) {
+  upb_msg_internal *in = upb_msg_getinternal(msg);
+  if (!in->internal) return;
+  const upb_msg_ext *base =
+      UPB_PTR_AT(in->internal, in->internal->ext_begin, void);
+  upb_msg_ext *ext = (void*)_upb_msg_getext(msg, ext_l);
+  if (ext) {
+    *ext = *base;
+    in->internal->ext_begin += sizeof(upb_msg_ext);
+  }
+}
+
 upb_msg_ext *_upb_msg_getorcreateext(upb_msg *msg, const upb_msglayout_ext *e,
                                      upb_arena *arena) {
   upb_msg_ext *ext = (upb_msg_ext*)_upb_msg_getext(msg, e);
@@ -361,14 +373,15 @@ upb_extreg *upb_extreg_new(upb_arena *arena) {
   return r;
 }
 
-bool _upb_extreg_add(upb_extreg *r, const upb_msglayout_ext *e, size_t count) {
+bool _upb_extreg_add(upb_extreg *r, const upb_msglayout_ext **e, size_t count) {
   char buf[EXTREG_KEY_SIZE];
-  const upb_msglayout_ext *start = e;
-  const upb_msglayout_ext *end = e + count;
+  const upb_msglayout_ext **start = e;
+  const upb_msglayout_ext **end = e + count;
   for (; e < end; e++) {
-    extreg_key(buf, e->extendee, e->field.number);
+    const upb_msglayout_ext *ext = *e;
+    extreg_key(buf, ext->extendee, ext->field.number);
     if (!upb_strtable_insert(&r->exts, buf, EXTREG_KEY_SIZE,
-                             upb_value_constptr(e), r->arena)) {
+                             upb_value_constptr(ext), r->arena)) {
       goto failure;
     }
   }
@@ -377,15 +390,15 @@ bool _upb_extreg_add(upb_extreg *r, const upb_msglayout_ext *e, size_t count) {
 failure:
   /* Back out the entries previously added. */
   for (end = e, e = start; e < end; e++) {
-    extreg_key(buf, e->extendee, e->field.number);
+    const upb_msglayout_ext *ext = *e;
+    extreg_key(buf, ext->extendee, ext->field.number);
     upb_strtable_remove(&r->exts, buf, EXTREG_KEY_SIZE, NULL);
   }
   return false;
 }
 
-const upb_msglayout_field *_upb_extreg_get(const upb_extreg *r,
-                                           const upb_msglayout *l,
-                                           uint32_t num) {
+const upb_msglayout_ext *_upb_extreg_get(const upb_extreg *r,
+                                         const upb_msglayout *l, uint32_t num) {
   char buf[EXTREG_KEY_SIZE];
   upb_value v;
   extreg_key(buf, l, num);
diff --git a/upb/msg_internal.h b/upb/msg_internal.h
index 24517002ea..7b9267e01e 100644
--- a/upb/msg_internal.h
+++ b/upb/msg_internal.h
@@ -1,3 +1,30 @@
+/*
+ * Copyright (c) 2009-2021, Google LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Google LLC nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 /*
 ** Our memory representation for parsing tables and messages themselves.
 ** Functions in this file are used by generated code and possibly reflection.
@@ -42,7 +69,7 @@ typedef struct {
   int16_t presence;       /* If >0, hasbit_index.  If <0, ~oneof_index. */
   uint16_t submsg_index;  /* undefined if descriptortype != MESSAGE or GROUP. */
   uint8_t descriptortype;
-  int8_t mode;            /* upb_fieldmode, with flags from upb_labelflags */
+  uint8_t mode;            /* upb_fieldmode | upb_labelflags | (upb_rep << 6) */
 } upb_msglayout_field;
 
 typedef enum {
@@ -54,6 +81,22 @@ typedef enum {
 /* Extra flags on the mode field. */
 enum upb_labelflags {
   _UPB_MODE_IS_PACKED = 4,
+  _UPB_MODE_IS_EXTENSION = 8,
+};
+
+/* Representation in the message.  Derivable from descriptortype and mode, but
+ * fast access helps the serializer. */
+enum upb_rep {
+  _UPB_REP_1BYTE = 0,
+  _UPB_REP_4BYTE = 1,
+  _UPB_REP_8BYTE = 2,
+  _UPB_REP_STRVIEW = 3,
+
+#if UINTPTR_MAX == 0xffffffff
+  _UPB_REP_PTR = _UPB_REP_4BYTE,
+#else
+  _UPB_REP_PTR = _UPB_REP_8BYTE,
+#endif
 };
 
 UPB_INLINE upb_fieldmode _upb_getmode(const upb_msglayout_field *field) {
@@ -82,14 +125,25 @@ typedef struct {
   _upb_field_parser *field_parser;
 } _upb_fasttable_entry;
 
+typedef union {
+  const struct upb_msglayout *submsg;
+  // TODO: const upb_enumlayout *subenum;
+} upb_msglayout_sub;
+
+typedef enum {
+  _UPB_MSGEXT_NONE = 0,        // Non-extendable message.
+  _UPB_MSGEXT_EXTENDABLE = 1,  // Normal extendable message.
+  // TODO: MessageSet
+} upb_msgext_mode;
+
 struct upb_msglayout {
-  const struct upb_msglayout *const* submsgs;
+  const upb_msglayout_sub *subs;
   const upb_msglayout_field *fields;
   /* Must be aligned to sizeof(void*).  Doesn't include internal members like
    * unknown fields, extension dict, pointer to msglayout, etc. */
   uint16_t size;
   uint16_t field_count;
-  bool extendable;
+  uint8_t ext;  // upb_msgext_mode, declared as uint8_t so sizeof(ext) == 1
   uint8_t dense_below;
   uint8_t table_mask;
   /* To constant-initialize the tables of variable length, we need a flexible
@@ -100,22 +154,28 @@ struct upb_msglayout {
 typedef struct {
   upb_msglayout_field field;
   const upb_msglayout *extendee;
-  const upb_msglayout *submsg;   /* NULL for non-submessage fields. */
+  upb_msglayout_sub sub;   /* NULL unless submessage or proto2 enum */
 } upb_msglayout_ext;
 
+typedef struct {
+  const upb_msglayout **msgs;
+  const upb_msglayout_ext **exts;
+  int msg_count;
+  int ext_count;
+} upb_msglayout_file;
+
 /** upb_extreg ****************************************************************/
 
 /* Adds the given extension info for message type |l| and field number |num|
  * into the registry. Returns false if this message type and field number were
  * already in the map, or if memory allocation fails. */
-bool _upb_extreg_add(upb_extreg *r, const upb_msglayout_ext *e, size_t count);
+bool _upb_extreg_add(upb_extreg *r, const upb_msglayout_ext **e, size_t count);
 
 /* Looks up the extension (if any) defined for message type |l| and field
  * number |num|.  If an extension was found, copies the field info into |*ext|
  * and returns true. Otherwise returns false. */
-const upb_msglayout_field *_upb_extreg_get(const upb_extreg *r,
-                                           const upb_msglayout *l,
-                                           uint32_t num);
+const upb_msglayout_ext *_upb_extreg_get(const upb_extreg *r,
+                                         const upb_msglayout *l, uint32_t num);
 
 /** upb_msg *******************************************************************/
 
@@ -216,6 +276,8 @@ const upb_msg_ext *_upb_msg_getexts(const upb_msg *msg, size_t *count);
 const upb_msg_ext *_upb_msg_getext(const upb_msg *msg,
                                    const upb_msglayout_ext *ext);
 
+void _upb_msg_clearext(upb_msg *msg, const upb_msglayout_ext *ext);
+
 /** Hasbit access *************************************************************/
 
 UPB_INLINE bool _upb_hasbit(const upb_msg *msg, size_t idx) {
diff --git a/upb/reflection.c b/upb/reflection.c
index b562190e30..989ab3af1a 100644
--- a/upb/reflection.c
+++ b/upb/reflection.c
@@ -108,15 +108,20 @@ static upb_msgval _upb_msg_getraw(const upb_msg *msg, const upb_fielddef *f) {
 }
 
 bool upb_msg_has(const upb_msg *msg, const upb_fielddef *f) {
-  const upb_msglayout_field *field = upb_fielddef_layout(f);
-  if (in_oneof(field)) {
-    return _upb_getoneofcase_field(msg, field) == field->number;
-  } else if (field->presence > 0) {
-    return _upb_hasbit_field(msg, field);
+  if (upb_fielddef_isextension(f)) {
+    const upb_msglayout_ext *ext = _upb_fielddef_extlayout(f);
+    return _upb_msg_getext(msg, ext) != NULL;
   } else {
-    UPB_ASSERT(field->descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
-               field->descriptortype == UPB_DESCRIPTOR_TYPE_GROUP);
-    return _upb_msg_getraw(msg, f).msg_val != NULL;
+    const upb_msglayout_field *field = upb_fielddef_layout(f);
+    if (in_oneof(field)) {
+      return _upb_getoneofcase_field(msg, field) == field->number;
+    } else if (field->presence > 0) {
+      return _upb_hasbit_field(msg, field);
+    } else {
+      UPB_ASSERT(field->descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
+                 field->descriptortype == UPB_DESCRIPTOR_TYPE_GROUP);
+      return _upb_msg_getraw(msg, f).msg_val != NULL;
+    }
   }
 }
 
@@ -136,73 +141,92 @@ const upb_fielddef *upb_msg_whichoneof(const upb_msg *msg,
 }
 
 upb_msgval upb_msg_get(const upb_msg *msg, const upb_fielddef *f) {
-  if (!upb_fielddef_haspresence(f) || upb_msg_has(msg, f)) {
+  if (upb_fielddef_isextension(f)) {
+    const upb_msg_ext *ext = _upb_msg_getext(msg, _upb_fielddef_extlayout(f));
+    if (ext) {
+      upb_msgval val;
+      memcpy(&val, &ext->data, sizeof(val));
+      return val;
+    } else if (upb_fielddef_isseq(f)) {
+      return (upb_msgval){.array_val = NULL};
+    }
+  } else if (!upb_fielddef_haspresence(f) || upb_msg_has(msg, f)) {
     return _upb_msg_getraw(msg, f);
-  } else {
-    return upb_fielddef_default(f);
   }
+  return upb_fielddef_default(f);
 }
 
 upb_mutmsgval upb_msg_mutable(upb_msg *msg, const upb_fielddef *f,
                               upb_arena *a) {
-  const upb_msglayout_field *field = upb_fielddef_layout(f);
-  upb_mutmsgval ret;
-  char *mem = UPB_PTR_AT(msg, field->offset, char);
-  bool wrong_oneof =
-      in_oneof(field) && _upb_getoneofcase_field(msg, field) != field->number;
+  assert(upb_fielddef_issubmsg(f) || upb_fielddef_isseq(f));
+  if (upb_fielddef_haspresence(f) && !upb_msg_has(msg, f)) {
+    // We need to skip the upb_msg_get() call in this case.
+    goto make;
+  }
 
-  memcpy(&ret, mem, sizeof(void*));
+  upb_msgval val = upb_msg_get(msg, f);
+  if (val.array_val) {
+    return (upb_mutmsgval){.array = (upb_array*)val.array_val};
+  }
 
-  if (a && (!ret.msg || wrong_oneof)) {
-    if (upb_fielddef_ismap(f)) {
-      const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
-      const upb_fielddef *key = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
-      const upb_fielddef *value = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
-      ret.map = upb_map_new(a, upb_fielddef_type(key), upb_fielddef_type(value));
-    } else if (upb_fielddef_isseq(f)) {
-      ret.array = upb_array_new(a, upb_fielddef_type(f));
-    } else {
-      UPB_ASSERT(upb_fielddef_issubmsg(f));
-      ret.msg = upb_msg_new(upb_fielddef_msgsubdef(f), a);
-    }
+  upb_mutmsgval ret;
+make:
+  if (!a) return (upb_mutmsgval){.array = NULL};
+  if (upb_fielddef_ismap(f)) {
+    const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+    const upb_fielddef *key = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
+    const upb_fielddef *value = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
+    ret.map = upb_map_new(a, upb_fielddef_type(key), upb_fielddef_type(value));
+  } else if (upb_fielddef_isseq(f)) {
+    ret.array = upb_array_new(a, upb_fielddef_type(f));
+  } else {
+    UPB_ASSERT(upb_fielddef_issubmsg(f));
+    ret.msg = upb_msg_new(upb_fielddef_msgsubdef(f), a);
+  }
 
-    memcpy(mem, &ret, sizeof(void*));
+  val.array_val = ret.array;
+  upb_msg_set(msg, f, val, a);
 
-    if (wrong_oneof) {
-      *_upb_oneofcase_field(msg, field) = field->number;
-    } else if (field->presence > 0) {
-      _upb_sethas_field(msg, field);
-    }
-  }
   return ret;
 }
 
-void upb_msg_set(upb_msg *msg, const upb_fielddef *f, upb_msgval val,
+bool upb_msg_set(upb_msg *msg, const upb_fielddef *f, upb_msgval val,
                  upb_arena *a) {
-  const upb_msglayout_field *field = upb_fielddef_layout(f);
-  char *mem = UPB_PTR_AT(msg, field->offset, char);
-  UPB_UNUSED(a);  /* We reserve the right to make set insert into a map. */
-  memcpy(mem, &val, get_field_size(field));
-  if (field->presence > 0) {
-    _upb_sethas_field(msg, field);
-  } else if (in_oneof(field)) {
-    *_upb_oneofcase_field(msg, field) = field->number;
+  if (upb_fielddef_isextension(f)) {
+    upb_msg_ext *ext =
+        _upb_msg_getorcreateext(msg, _upb_fielddef_extlayout(f), a);
+    if (!ext) return false;
+    memcpy(&ext->data, &val, sizeof(val));
+  } else {
+    const upb_msglayout_field *field = upb_fielddef_layout(f);
+    char *mem = UPB_PTR_AT(msg, field->offset, char);
+    memcpy(mem, &val, get_field_size(field));
+    if (field->presence > 0) {
+      _upb_sethas_field(msg, field);
+    } else if (in_oneof(field)) {
+      *_upb_oneofcase_field(msg, field) = field->number;
+    }
   }
+  return true;
 }
 
 void upb_msg_clearfield(upb_msg *msg, const upb_fielddef *f) {
-  const upb_msglayout_field *field = upb_fielddef_layout(f);
-  char *mem = UPB_PTR_AT(msg, field->offset, char);
-
-  if (field->presence > 0) {
-    _upb_clearhas_field(msg, field);
-  } else if (in_oneof(field)) {
-    uint32_t *oneof_case = _upb_oneofcase_field(msg, field);
-    if (*oneof_case != field->number) return;
-    *oneof_case = 0;
-  }
+  if (upb_fielddef_isextension(f)) {
+    _upb_msg_clearext(msg, _upb_fielddef_extlayout(f));
+  } else {
+    const upb_msglayout_field *field = upb_fielddef_layout(f);
+    char *mem = UPB_PTR_AT(msg, field->offset, char);
+
+    if (field->presence > 0) {
+      _upb_clearhas_field(msg, field);
+    } else if (in_oneof(field)) {
+      uint32_t *oneof_case = _upb_oneofcase_field(msg, field);
+      if (*oneof_case != field->number) return;
+      *oneof_case = 0;
+    }
 
-  memset(mem, 0, get_field_size(field));
+    memset(mem, 0, get_field_size(field));
+  }
 }
 
 void upb_msg_clear(upb_msg *msg, const upb_msgdef *m) {
@@ -212,10 +236,12 @@ void upb_msg_clear(upb_msg *msg, const upb_msgdef *m) {
 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) {
-  int i = *iter;
-  int n = upb_msgdef_fieldcount(m);
+  size_t i = *iter;
+  size_t n = upb_msgdef_fieldcount(m);
   const upb_msgval zero = {0};
   UPB_UNUSED(ext_pool);
+
+  /* Iterate over normal fields, returning the first one that is set. */
   while (++i < n) {
     const upb_fielddef *f = upb_msgdef_field(m, i);
     upb_msgval val = _upb_msg_getraw(msg, f);
@@ -245,6 +271,20 @@ bool upb_msg_next(const upb_msg *msg, const upb_msgdef *m,
     *iter = i;
     return true;
   }
+
+  if (ext_pool) {
+    /* Return any extensions that are set. */
+    size_t count;
+    const upb_msg_ext *ext = _upb_msg_getexts(msg, &count);
+    if (i - n < count) {
+      ext += count - 1 - (i - n);
+      memcpy(out_val, &ext->data, sizeof(*out_val));
+      *out_f = _upb_symtab_lookupextfield(ext_pool, ext->ext);
+      *iter = i;
+      return true;
+    }
+  }
+
   *iter = i;
   return false;
 }
diff --git a/upb/reflection.h b/upb/reflection.h
index f0eb0d4fb9..5b8f5c2ce7 100644
--- a/upb/reflection.h
+++ b/upb/reflection.h
@@ -82,9 +82,10 @@ const upb_fielddef *upb_msg_whichoneof(const upb_msg *msg,
 
 /* Sets the given field to the given value.  For a msg/array/map/string, the
  * value must be in the same arena.  */
-void upb_msg_set(upb_msg *msg, const upb_fielddef *f, upb_msgval val,
+bool upb_msg_set(upb_msg *msg, const upb_fielddef *f, upb_msgval val,
                  upb_arena *a);
 
+
 /* Clears any field presence and sets the value back to its default. */
 void upb_msg_clearfield(upb_msg *msg, const upb_fielddef *f);
 
diff --git a/upbc/BUILD b/upbc/BUILD
index 29bb3983c2..8fd69901cb 100644
--- a/upbc/BUILD
+++ b/upbc/BUILD
@@ -54,6 +54,7 @@ cc_binary(
         ":common",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/container:flat_hash_set",
         "@com_google_absl//absl/strings",
         "@com_google_protobuf//:protobuf",
         "@com_google_protobuf//:protoc_lib",
diff --git a/upbc/common.cc b/upbc/common.cc
index d2df929c9d..0f2d643d12 100644
--- a/upbc/common.cc
+++ b/upbc/common.cc
@@ -31,14 +31,6 @@ namespace {
 
 namespace protobuf = ::google::protobuf;
 
-void AddMessages(const protobuf::Descriptor* message,
-                 std::vector<const protobuf::Descriptor*>* messages) {
-  messages->push_back(message);
-  for (int i = 0; i < message->nested_type_count(); i++) {
-    AddMessages(message->nested_type(i), messages);
-  }
-}
-
 }  // namespace
 
 std::string StripExtension(absl::string_view fname) {
@@ -69,21 +61,16 @@ void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) {
       file->name());
 }
 
-std::vector<const protobuf::Descriptor*> SortedMessages(
-    const protobuf::FileDescriptor* file) {
-  std::vector<const protobuf::Descriptor*> messages;
-  for (int i = 0; i < file->message_type_count(); i++) {
-    AddMessages(file->message_type(i), &messages);
-  }
-  return messages;
-}
-
 std::string MessageName(const protobuf::Descriptor* descriptor) {
   return ToCIdent(descriptor->full_name());
 }
 
-std::string MessageInit(const protobuf::Descriptor* descriptor) {
-  return MessageName(descriptor) + "_msginit";
+std::string FileLayoutName(const google::protobuf::FileDescriptor* file) {
+  return ToCIdent(file->name()) + "_upb_file_layout";
+}
+
+std::string HeaderFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.h";
 }
 
 }  // namespace upbc
diff --git a/upbc/common.h b/upbc/common.h
index 1867ae0331..917d850ff2 100644
--- a/upbc/common.h
+++ b/upbc/common.h
@@ -82,10 +82,9 @@ std::string ToCIdent(absl::string_view str);
 std::string ToPreproc(absl::string_view str);
 void EmitFileWarning(const google::protobuf::FileDescriptor* file,
                      Output& output);
-std::vector<const google::protobuf::Descriptor*> SortedMessages(
-    const google::protobuf::FileDescriptor* file);
-std::string MessageInit(const google::protobuf::Descriptor* descriptor);
 std::string MessageName(const google::protobuf::Descriptor* descriptor);
+std::string FileLayoutName(const google::protobuf::FileDescriptor* file);
+std::string HeaderFilename(const google::protobuf::FileDescriptor* file);
 
 }  // namespace upbc
 
diff --git a/upbc/protoc-gen-upb.cc b/upbc/protoc-gen-upb.cc
index a68a691bd8..2de85b5f4c 100644
--- a/upbc/protoc-gen-upb.cc
+++ b/upbc/protoc-gen-upb.cc
@@ -26,6 +26,7 @@
 #include <memory>
 
 #include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/strings/ascii.h"
 #include "absl/strings/substitute.h"
 #include "google/protobuf/compiler/code_generator.h"
@@ -42,14 +43,31 @@ namespace {
 namespace protoc = ::google::protobuf::compiler;
 namespace protobuf = ::google::protobuf;
 
-std::string HeaderFilename(std::string proto_filename) {
-  return StripExtension(proto_filename) + ".upb.h";
+std::string SourceFilename(const google::protobuf::FileDescriptor* file) {
+  return StripExtension(file->name()) + ".upb.c";
 }
 
-std::string SourceFilename(std::string proto_filename) {
-  return StripExtension(proto_filename) + ".upb.c";
+std::string MessageInit(const protobuf::Descriptor* descriptor) {
+  return MessageName(descriptor) + "_msginit";
 }
 
+std::string ExtensionIdentBase(const protobuf::FieldDescriptor* ext) {
+  assert(ext->is_extension());
+  std::string ext_scope;
+  if (ext->extension_scope()) {
+    return MessageName(ext->extension_scope());
+  } else {
+    return ToCIdent(ext->file()->package());
+  }
+}
+
+std::string ExtensionLayout(const google::protobuf::FieldDescriptor* ext) {
+  return absl::StrCat(ExtensionIdentBase(ext), "_", ext->name(), "_ext");
+}
+
+const char *kMessagesInit = "messages_layout";
+const char *kExtensionsInit = "extensions_layout";
+
 void AddEnums(const protobuf::Descriptor* message,
               std::vector<const protobuf::EnumDescriptor*>* enums) {
   for (int i = 0; i < message->enum_type_count(); i++) {
@@ -79,6 +97,56 @@ std::vector<const protobuf::EnumDescriptor*> SortedEnums(
   return enums;
 }
 
+void AddMessages(const protobuf::Descriptor* message,
+                 std::vector<const protobuf::Descriptor*>* messages) {
+  messages->push_back(message);
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    AddMessages(message->nested_type(i), messages);
+  }
+}
+
+// Ordering must match upb/def.c!
+//
+// The ordering is significant because each upb_msgdef* will point at the
+// corresponding upb_msglayout and we just iterate through the list without
+// any search or lookup.
+std::vector<const protobuf::Descriptor*> SortedMessages(
+    const protobuf::FileDescriptor* file) {
+  std::vector<const protobuf::Descriptor*> messages;
+  for (int i = 0; i < file->message_type_count(); i++) {
+    AddMessages(file->message_type(i), &messages);
+  }
+  return messages;
+}
+
+void AddExtensionsFromMessage(
+    const protobuf::Descriptor* message,
+    std::vector<const protobuf::FieldDescriptor*>* exts) {
+  for (int i = 0; i < message->extension_count(); i++) {
+    exts->push_back(message->extension(i));
+  }
+  for (int i = 0; i < message->nested_type_count(); i++) {
+    AddExtensionsFromMessage(message->nested_type(i), exts);
+  }
+}
+
+// Ordering must match upb/def.c!
+//
+// The ordering is significant because each upb_fielddef* will point at the
+// corresponding upb_msglayout_ext and we just iterate through the list without
+// any search or lookup.
+std::vector<const protobuf::FieldDescriptor*> SortedExtensions(
+    const protobuf::FileDescriptor* file) {
+  std::vector<const protobuf::FieldDescriptor*> ret;
+  for (int i = 0; i < file->message_type_count(); i++) {
+    AddExtensionsFromMessage(file->message_type(i), &ret);
+  }
+  for (int i = 0; i < file->extension_count(); i++) {
+    ret.push_back(file->extension(i));
+  }
+  return ret;
+}
+
 std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
     const protobuf::Descriptor* message) {
   std::vector<const protobuf::FieldDescriptor*> fields;
@@ -180,6 +248,29 @@ std::string SizeLg2(const protobuf::FieldDescriptor* field) {
   }
 }
 
+std::string SizeRep(const protobuf::FieldDescriptor* field) {
+  switch (field->cpp_type()) {
+    case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
+      return "_UPB_REP_PTR";
+    case protobuf::FieldDescriptor::CPPTYPE_ENUM:
+    case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
+    case protobuf::FieldDescriptor::CPPTYPE_INT32:
+    case protobuf::FieldDescriptor::CPPTYPE_UINT32:
+      return "_UPB_REP_4BYTE";
+    case protobuf::FieldDescriptor::CPPTYPE_BOOL:
+      return "_UPB_REP_1BYTE";
+    case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
+    case protobuf::FieldDescriptor::CPPTYPE_INT64:
+    case protobuf::FieldDescriptor::CPPTYPE_UINT64:
+      return "_UPB_REP_8BYTE";
+    case protobuf::FieldDescriptor::CPPTYPE_STRING:
+      return "_UPB_REP_STRVIEW";
+    default:
+      fprintf(stderr, "Unexpected type");
+      abort();
+  }
+}
+
 std::string FieldDefault(const protobuf::FieldDescriptor* field) {
   switch (field->cpp_type()) {
     case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
@@ -239,6 +330,34 @@ void DumpEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
   }
 }
 
+void GenerateExtensionInHeader(const protobuf::FieldDescriptor* ext,
+                               Output& output) {
+  output(
+      "UPB_INLINE bool $0_has_$1(const struct $2 *msg) { "
+      "return _upb_msg_getext(msg, &$3) != NULL; }\n",
+      ExtensionIdentBase(ext), ext->name(), MessageName(ext->containing_type()),
+      ExtensionLayout(ext));
+
+  if (ext->is_repeated()) {
+  } else if (ext->message_type()) {
+    output(
+        "UPB_INLINE $0 $1_$2(const struct $3 *msg) { "
+        "const upb_msg_ext *ext = _upb_msg_getext(msg, &$4); "
+        "UPB_ASSERT(ext); return *UPB_PTR_AT(&ext->data, 0, $0); }\n",
+        CTypeConst(ext), ExtensionIdentBase(ext), ext->name(),
+        MessageName(ext->containing_type()), ExtensionLayout(ext),
+        FieldDefault(ext));
+  } else {
+    output(
+        "UPB_INLINE $0 $1_$2(const struct $3 *msg) { "
+        "const upb_msg_ext *ext = _upb_msg_getext(msg, &$4); "
+        "return ext ? *UPB_PTR_AT(&ext->data, 0, $0) : $5; }\n",
+        CTypeConst(ext), ExtensionIdentBase(ext), ext->name(),
+        MessageName(ext->containing_type()), ExtensionLayout(ext),
+        FieldDefault(ext));
+  }
+}
+
 void GenerateMessageInHeader(const protobuf::Descriptor* message, Output& output) {
   MessageLayout layout(message);
 
@@ -524,11 +643,10 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
       ToPreproc(file->name()));
 
   for (int i = 0; i < file->public_dependency_count(); i++) {
-    const auto& name = file->public_dependency(i)->name();
     if (i == 0) {
       output("/* Public Imports. */\n");
     }
-    output("#include \"$0\"\n", HeaderFilename(name));
+    output("#include \"$0\"\n", HeaderFilename(file));
     if (i == file->public_dependency_count() - 1) {
       output("\n");
     }
@@ -544,6 +662,8 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
 
   const std::vector<const protobuf::Descriptor*> this_file_messages =
       SortedMessages(file);
+  const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
+      SortedExtensions(file);
 
   // Forward-declare types defined in this file.
   for (auto message : this_file_messages) {
@@ -555,6 +675,9 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
   for (auto message : this_file_messages) {
     output("extern const upb_msglayout $0;\n", MessageInit(message));
   }
+  for (auto ext : this_file_exts) {
+    output("extern const upb_msglayout_ext $0;\n", ExtensionLayout(ext));
+  }
 
   // Forward-declare types not in this file, but used as submessages.
   // Order by full name for consistent ordering.
@@ -570,6 +693,12 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
       }
     }
   }
+  for (auto ext : this_file_exts) {
+    if (ext->file() != ext->containing_type()->file()) {
+      forward_messages[ext->containing_type()->full_name()] =
+          ext->containing_type();
+    }
+  }
   for (const auto& pair : forward_messages) {
     output("struct $0;\n", MessageName(pair.second));
   }
@@ -596,6 +725,12 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
     GenerateMessageInHeader(message, output);
   }
 
+  for (auto ext : this_file_exts) {
+    GenerateExtensionInHeader(ext, output);
+  }
+
+  output("extern const upb_msglayout_file $0;\n\n", FileLayoutName(file));
+
   output(
       "#ifdef __cplusplus\n"
       "}  /* extern \"C\" */\n"
@@ -853,20 +988,28 @@ void WriteField(const protobuf::FieldDescriptor* field,
                 absl::string_view offset, absl::string_view presence,
                 int submsg_index, Output& output) {
   std::string mode;
+  std::string rep;
   if (field->is_map()) {
     mode = "_UPB_MODE_MAP";
+    rep = "_UPB_REP_PTR";
   } else if (field->is_repeated()) {
     mode = "_UPB_MODE_ARRAY";
+    rep = "_UPB_REP_PTR";
   } else {
     mode = "_UPB_MODE_SCALAR";
+    rep = SizeRep(field);
   }
 
   if (field->is_packed()) {
     absl::StrAppend(&mode, " | _UPB_MODE_IS_PACKED");
   }
 
-  output("{$0, $1, $2, $3, $4, $5}", field->number(), offset, presence,
-         submsg_index, TableDescriptorType(field), mode);
+  if (field->is_extension()) {
+    absl::StrAppend(&mode, " | _UPB_MODE_IS_EXTENSION");
+  }
+
+  output("{$0, $1, $2, $3, $4, $5 | ($6 << 6)}", field->number(), offset,
+         presence, submsg_index, TableDescriptorType(field), mode, rep);
 }
 
 // Writes a single field into a .upb.c source file.
@@ -913,11 +1056,11 @@ void WriteMessage(const protobuf::Descriptor* message, Output& output,
     // "submsgs" array for every strongly-connected component.
     std::string submsgs_array_name = msg_name + "_submsgs";
     submsgs_array_ref = "&" + submsgs_array_name + "[0]";
-    output("static const upb_msglayout *const $0[$1] = {\n",
+    output("static const upb_msglayout_sub $0[$1] = {\n",
            submsgs_array_name, submsg_array.submsgs().size());
 
     for (auto submsg : submsg_array.submsgs()) {
-      output("  &$0,\n", MessageInit(submsg));
+      output("  {.submsg = &$0},\n", MessageInit(submsg));
     }
 
     output("};\n\n");
@@ -960,12 +1103,19 @@ void WriteMessage(const protobuf::Descriptor* message, Output& output,
     table_mask = (table.size() - 1) << 3;
   }
 
+  std::string msgext = "_UPB_MSGEXT_NONE";
+
+  if (message->extension_range_count()) {
+    msgext = "_UPB_MSGEXT_EXTENDABLE";
+  }
+
   output("const upb_msglayout $0 = {\n", MessageInit(message));
   output("  $0,\n", submsgs_array_ref);
   output("  $0,\n", fields_array_ref);
-  output("  $0, $1, $2, $3, $4,\n", GetSizeInit(layout.message_size()),
+  output("  $0, $1, $2, $3, $4,\n",
+         GetSizeInit(layout.message_size()),
          field_number_order.size(),
-         "false",  // TODO: extendable
+         msgext,
          dense_below,
          table_mask
   );
@@ -980,11 +1130,74 @@ void WriteMessage(const protobuf::Descriptor* message, Output& output,
   output("};\n\n");
 }
 
-void WriteMessages(const protobuf::FileDescriptor* file, Output& output,
+void WriteExtension(const protobuf::FieldDescriptor* ext, Output& output) {
+  output("const upb_msglayout_ext $0 = {\n  ", ExtensionLayout(ext));
+  WriteField(ext, "0", "0", 0, output);
+  output(",\n");
+  output("  &$0,\n", MessageInit(ext->containing_type()));
+  if (ext->message_type()) {
+    output("  {.submsg = &$0},\n", MessageInit(ext->message_type()));
+  } else {
+    output("  {.submsg = NULL},\n");
+  }
+  output("\n};\n");
+}
+
+int WriteMessages(const protobuf::FileDescriptor* file, Output& output,
                    bool fasttable_enabled) {
-  for (auto* message : SortedMessages(file)) {
+  std::vector<const protobuf::Descriptor*> file_messages =
+      SortedMessages(file);
+
+  if (file_messages.empty()) return 0;
+
+  for (auto message : file_messages) {
     WriteMessage(message, output, fasttable_enabled);
   }
+
+  output("static const upb_msglayout *$0[$1] = {\n", kMessagesInit,
+         file_messages.size());
+  for (auto message : file_messages) {
+    output("  &$0,\n", MessageInit(message));
+  }
+  output("};\n");
+  output("\n");
+  return file_messages.size();
+}
+
+int WriteExtensions(const protobuf::FileDescriptor* file, Output& output) {
+  auto exts = SortedExtensions(file);
+  absl::flat_hash_set<const protobuf::Descriptor*> forward_decls;
+
+  if (exts.empty()) return 0;
+
+  for (auto ext : exts) {
+    forward_decls.insert(ext->containing_type());
+    if (ext->message_type()) {
+      forward_decls.insert(ext->message_type());
+    }
+  }
+
+  for (auto ext : exts) {
+    WriteExtension(ext, output);
+  }
+
+  for (auto decl : forward_decls) {
+    output("extern const upb_msglayout $0;\n", MessageInit(decl));
+  }
+
+  output(
+      "\n"
+      "static const upb_msglayout_ext *$0[$1] = {\n",
+      kExtensionsInit, exts.size());
+
+  for (auto ext : exts) {
+    output("  &$0,\n", ExtensionLayout(ext));
+  }
+
+  output(
+      "};\n"
+      "\n");
+  return exts.size();
 }
 
 // Writes a .upb.c source file.
@@ -996,10 +1209,10 @@ void WriteSource(const protobuf::FileDescriptor* file, Output& output,
       "#include <stddef.h>\n"
       "#include \"upb/msg_internal.h\"\n"
       "#include \"$0\"\n",
-      HeaderFilename(file->name()));
+      HeaderFilename(file));
 
   for (int i = 0; i < file->dependency_count(); i++) {
-    output("#include \"$0\"\n", HeaderFilename(file->dependency(i)->name()));
+    output("#include \"$0\"\n", HeaderFilename(file->dependency(i)));
   }
 
   output(
@@ -1007,7 +1220,15 @@ void WriteSource(const protobuf::FileDescriptor* file, Output& output,
       "#include \"upb/port_def.inc\"\n"
       "\n");
 
-  WriteMessages(file, output, fasttable_enabled);
+  int msg_count = WriteMessages(file, output, fasttable_enabled);
+  int ext_count = WriteExtensions(file, output);
+
+  output("const upb_msglayout_file $0 = {\n", FileLayoutName(file));
+  output("  $0,\n", msg_count ? kMessagesInit : "NULL");
+  output("  $0,\n", ext_count ? kExtensionsInit : "NULL");
+  output("  $0,\n", msg_count);
+  output("  $0,\n", ext_count);
+  output("};\n\n");
 
   output("#include \"upb/port_undef.inc\"\n");
   output("\n");
@@ -1040,10 +1261,10 @@ bool Generator::Generate(const protobuf::FileDescriptor* file,
     }
   }
 
-  Output h_output(context->Open(HeaderFilename(file->name())));
+  Output h_output(context->Open(HeaderFilename(file)));
   WriteHeader(file, h_output);
 
-  Output c_output(context->Open(SourceFilename(file->name())));
+  Output c_output(context->Open(SourceFilename(file)));
   WriteSource(file, c_output, fasttable_enabled);
 
   return true;
diff --git a/upbc/protoc-gen-upbdefs.cc b/upbc/protoc-gen-upbdefs.cc
index 4ec4163f60..39191a8ab9 100644
--- a/upbc/protoc-gen-upbdefs.cc
+++ b/upbc/protoc-gen-upbdefs.cc
@@ -104,29 +104,13 @@ void WriteDefSource(const protobuf::FileDescriptor* file, Output& output) {
 
   output("#include \"upb/def.h\"\n");
   output("#include \"$0\"\n", DefHeaderFilename(file->name()));
+  output("#include \"$0\"\n", HeaderFilename(file));
   output("\n");
 
   for (int i = 0; i < file->dependency_count(); i++) {
     output("extern upb_def_init $0;\n", DefInitSymbol(file->dependency(i)));
   }
 
-  std::vector<const protobuf::Descriptor*> file_messages =
-      SortedMessages(file);
-
-  for (auto message : file_messages) {
-    output("extern const upb_msglayout $0;\n", MessageInit(message));
-  }
-  output("\n");
-
-  if (!file_messages.empty()) {
-    output("static const upb_msglayout *layouts[$0] = {\n", file_messages.size());
-    for (auto message : file_messages) {
-      output("  &$0,\n", MessageInit(message));
-    }
-    output("};\n");
-    output("\n");
-  }
-
   protobuf::FileDescriptorProto file_proto;
   file->CopyTo(&file_proto);
   std::string file_data;
@@ -156,11 +140,7 @@ void WriteDefSource(const protobuf::FileDescriptor* file, Output& output) {
 
   output("upb_def_init $0 = {\n", DefInitSymbol(file));
   output("  deps,\n");
-  if (file_messages.empty()) {
-    output("  NULL,\n");
-  } else {
-    output("  layouts,\n");
-  }
+  output("  &$0,\n", FileLayoutName(file));
   output("  \"$0\",\n", file->name());
   output("  UPB_STRVIEW_INIT(descriptor, $0)\n", file_data.size());
   output("};\n");