match file names to type names

Lots of changes but it's all just moving things around.
Backward-compatible stub #include's have been provided for now.

upb_Arena/upb_Status have been split out from upb/upb.?
upb_Array/upb_Map/upb_MessageValue have been split out from upb/collections.?
upb_ExtensionRegistry has been split out from upb/msg.?
upb/decode_internal.h is now upb/internal/decode.h
upb/mini_table_accessors_internal.h is now upb/internal/mini_table_accessors.h
upb/table_internal.h is now upb/internal/table.h
upb/upb_internal.h is now upb/internal/upb.h

PiperOrigin-RevId: 456297617
diff --git a/BUILD b/BUILD
index 29a9962..95701d1 100644
--- a/BUILD
+++ b/BUILD
@@ -102,26 +102,32 @@
 cc_library(
     name = "upb",
     srcs = [
+        "upb/arena.c",
         "upb/decode.c",
-        "upb/decode_internal.h",
         "upb/encode.c",
+        "upb/internal/decode.h",
+        "upb/internal/table.h",
+        "upb/internal/upb.h",
         "upb/msg.c",
         "upb/msg_internal.h",
+        "upb/status.c",
         "upb/table.c",
-        "upb/table_internal.h",
         "upb/upb.c",
-        "upb/upb_internal.h",
     ],
     hdrs = [
+        "upb/arena.h",
         "upb/decode.h",
         "upb/encode.h",
+        "upb/extension_registry.h",
         "upb/msg.h",
+        "upb/status.h",
         "upb/upb.h",
         "upb/upb.hpp",
     ],
     copts = UPB_DEFAULT_COPTS,
     visibility = ["//visibility:public"],
     deps = [
+        ":extension_registry",
         ":fastdecode",
         ":port",
         "//third_party/utf8_range",
@@ -129,6 +135,25 @@
 )
 
 cc_library(
+    name = "extension_registry",
+    srcs = [
+        "upb/extension_registry.c",
+        "upb/msg.h",
+        "upb/msg_internal.h",
+        "upb/upb.h",
+    ],
+    hdrs = [
+        "upb/extension_registry.h",
+    ],
+    copts = UPB_DEFAULT_COPTS,
+    visibility = ["//visibility:public"],
+    deps = [
+        ":port",
+        ":table",
+    ],
+)
+
+cc_library(
     name = "mini_descriptor",
     srcs = [
         "upb/mini_descriptor.c",
@@ -150,8 +175,11 @@
 
 cc_library(
     name = "mini_table_internal",
-    hdrs = ["upb/msg_internal.h"],
+    hdrs = [
+        "upb/msg_internal.h",
+    ],
     deps = [
+        ":extension_registry",
         ":port",
         ":table",
         ":upb",
@@ -170,6 +198,7 @@
     copts = UPB_DEFAULT_COPTS,
     visibility = ["//visibility:public"],
     deps = [
+        ":extension_registry",
         ":mini_table_internal",
         ":port",
         ":upb",
@@ -179,8 +208,8 @@
 cc_library(
     name = "mini_table_accessors",
     srcs = [
+        "upb/internal/mini_table_accessors.h",
         "upb/mini_table_accessors.c",
-        "upb/mini_table_accessors_internal.h",
     ],
     hdrs = [
         "upb/mini_table_accessors.h",
@@ -198,10 +227,15 @@
 
 cc_test(
     name = "mini_table_test",
-    srcs = ["upb/mini_table_test.cc"],
+    srcs = [
+        "upb/internal/table.h",
+        "upb/mini_table_test.cc",
+    ],
     deps = [
+        ":extension_registry",
         ":mini_table",
         ":mini_table_internal",
+        ":port",
         ":upb",
         "@com_google_absl//absl/container:flat_hash_set",
         "@com_google_googletest//:gtest_main",
@@ -233,13 +267,14 @@
         "upb/decode.h",
         "upb/decode_fast.c",
         "upb/decode_fast.h",
-        "upb/decode_internal.h",
+        "upb/internal/decode.h",
+        "upb/internal/upb.h",
         "upb/msg.h",
         "upb/msg_internal.h",
-        "upb/upb_internal.h",
     ],
     copts = UPB_DEFAULT_COPTS,
     deps = [
+        ":extension_registry",
         ":port",
         ":table",
         "//third_party/utf8_range",
@@ -258,6 +293,7 @@
         "upb/decode.h",
         "upb/decode_fast.h",
         "upb/encode.h",
+        "upb/extension_registry.h",
         "upb/msg.h",
         "upb/msg_internal.h",
         "upb/port_def.inc",
@@ -302,10 +338,14 @@
 cc_library(
     name = "collections",
     srcs = [
-        "upb/collections.c",
+        "upb/array.c",
+        "upb/map.c",
     ],
     hdrs = [
+        "upb/array.h",
         "upb/collections.h",
+        "upb/map.h",
+        "upb/message_value.h",
     ],
     copts = UPB_DEFAULT_COPTS,
     visibility = ["//visibility:public"],
@@ -346,8 +386,8 @@
 cc_library(
     name = "textformat",
     srcs = [
+        "upb/internal/upb.h",
         "upb/text_encode.c",
-        "upb/upb_internal.h",
     ],
     hdrs = [
         "upb/text_encode.h",
@@ -364,9 +404,9 @@
 cc_library(
     name = "json",
     srcs = [
+        "upb/internal/upb.h",
         "upb/json_decode.c",
         "upb/json_encode.c",
-        "upb/upb_internal.h",
     ],
     hdrs = [
         "upb/json_decode.h",
@@ -667,6 +707,9 @@
 cc_library(
     name = "table",
     hdrs = [
+        "upb/arena.h",
+        "upb/internal/table.h",
+        "upb/status.h",
         "upb/table_internal.h",
         "upb/upb.h",
     ],
@@ -692,6 +735,7 @@
     libs = [
         ":collections",
         ":descriptor_upb_proto",
+        ":extension_registry",
         ":fastdecode",
         ":mini_table",
         ":port",
@@ -718,6 +762,7 @@
         ":collections",
         ":descriptor_upb_proto",
         ":descriptor_upb_proto_reflection",
+        ":extension_registry",
         ":fastdecode",
         ":json",
         ":mini_table",
@@ -745,6 +790,7 @@
     libs = [
         ":collections",
         ":descriptor_upb_proto",
+        ":extension_registry",
         ":fastdecode",
         ":json",
         ":mini_table",
diff --git a/python/map.c b/python/map.c
index a0ba2fe..5e0f6ab 100644
--- a/python/map.c
+++ b/python/map.c
@@ -30,7 +30,7 @@
 #include "python/convert.h"
 #include "python/message.h"
 #include "python/protobuf.h"
-#include "upb/collections.h"
+#include "upb/map.h"
 
 // -----------------------------------------------------------------------------
 // MapContainer
diff --git a/python/protobuf.h b/python/protobuf.h
index be9eb81..2169a60 100644
--- a/python/protobuf.h
+++ b/python/protobuf.h
@@ -32,7 +32,6 @@
 
 #include "python/descriptor.h"
 #include "python/python_api.h"
-#include "upb/table_internal.h"
 
 // begin:github_only
 #define PYUPB_PROTOBUF_PUBLIC_PACKAGE "google.protobuf"
diff --git a/upb/arena.c b/upb/arena.c
new file mode 100644
index 0000000..3c87269
--- /dev/null
+++ b/upb/arena.c
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+#include "upb/arena.h"
+
+#include <stdlib.h>
+
+#include "upb/internal/upb.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+/* upb_alloc ******************************************************************/
+
+static void* upb_global_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize,
+                                  size_t size) {
+  UPB_UNUSED(alloc);
+  UPB_UNUSED(oldsize);
+  if (size == 0) {
+    free(ptr);
+    return NULL;
+  } else {
+    return realloc(ptr, size);
+  }
+}
+
+static uint32_t* upb_cleanup_pointer(uintptr_t cleanup_metadata) {
+  return (uint32_t*)(cleanup_metadata & ~0x1);
+}
+
+static bool upb_cleanup_has_initial_block(uintptr_t cleanup_metadata) {
+  return cleanup_metadata & 0x1;
+}
+
+static uintptr_t upb_cleanup_metadata(uint32_t* cleanup,
+                                      bool has_initial_block) {
+  return (uintptr_t)cleanup | has_initial_block;
+}
+
+upb_alloc upb_alloc_global = {&upb_global_allocfunc};
+
+/* upb_Arena ******************************************************************/
+
+struct mem_block {
+  struct mem_block* next;
+  uint32_t size;
+  uint32_t cleanups;
+  /* Data follows. */
+};
+
+typedef struct cleanup_ent {
+  upb_CleanupFunc* cleanup;
+  void* ud;
+} cleanup_ent;
+
+static const size_t memblock_reserve =
+    UPB_ALIGN_UP(sizeof(mem_block), UPB_MALLOC_ALIGN);
+
+static upb_Arena* arena_findroot(upb_Arena* a) {
+  /* Path splitting keeps time complexity down, see:
+   *   https://en.wikipedia.org/wiki/Disjoint-set_data_structure */
+  while (a->parent != a) {
+    upb_Arena* next = a->parent;
+    a->parent = next->parent;
+    a = next;
+  }
+  return a;
+}
+
+static void upb_Arena_addblock(upb_Arena* a, upb_Arena* root, void* ptr,
+                               size_t size) {
+  mem_block* block = ptr;
+
+  /* The block is for arena |a|, but should appear in the freelist of |root|. */
+  block->next = root->freelist;
+  block->size = (uint32_t)size;
+  block->cleanups = 0;
+  root->freelist = block;
+  a->last_size = block->size;
+  if (!root->freelist_tail) root->freelist_tail = block;
+
+  a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char);
+  a->head.end = UPB_PTR_AT(block, size, char);
+  a->cleanup_metadata = upb_cleanup_metadata(
+      &block->cleanups, upb_cleanup_has_initial_block(a->cleanup_metadata));
+
+  UPB_POISON_MEMORY_REGION(a->head.ptr, a->head.end - a->head.ptr);
+}
+
+static bool upb_Arena_Allocblock(upb_Arena* a, size_t size) {
+  upb_Arena* root = arena_findroot(a);
+  size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve;
+  mem_block* block = upb_malloc(root->block_alloc, block_size);
+
+  if (!block) return false;
+  upb_Arena_addblock(a, root, block, block_size);
+  return true;
+}
+
+void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size) {
+  if (!upb_Arena_Allocblock(a, size)) return NULL; /* Out of memory. */
+  UPB_ASSERT(_upb_ArenaHas(a) >= size);
+  return upb_Arena_Malloc(a, size);
+}
+
+static void* upb_Arena_doalloc(upb_alloc* alloc, void* ptr, size_t oldsize,
+                               size_t size) {
+  upb_Arena* a = (upb_Arena*)alloc; /* upb_alloc is initial member. */
+  return upb_Arena_Realloc(a, ptr, oldsize, size);
+}
+
+/* Public Arena API ***********************************************************/
+
+upb_Arena* arena_initslow(void* mem, size_t n, upb_alloc* alloc) {
+  const size_t first_block_overhead = sizeof(upb_Arena) + memblock_reserve;
+  upb_Arena* a;
+
+  /* We need to malloc the initial block. */
+  n = first_block_overhead + 256;
+  if (!alloc || !(mem = upb_malloc(alloc, n))) {
+    return NULL;
+  }
+
+  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena);
+  n -= sizeof(*a);
+
+  a->head.alloc.func = &upb_Arena_doalloc;
+  a->block_alloc = alloc;
+  a->parent = a;
+  a->refcount = 1;
+  a->freelist = NULL;
+  a->freelist_tail = NULL;
+  a->cleanup_metadata = upb_cleanup_metadata(NULL, false);
+
+  upb_Arena_addblock(a, a, mem, n);
+
+  return a;
+}
+
+upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc) {
+  upb_Arena* a;
+
+  if (n) {
+    /* Align initial pointer up so that we return properly-aligned pointers. */
+    void* aligned = (void*)UPB_ALIGN_UP((uintptr_t)mem, UPB_MALLOC_ALIGN);
+    size_t delta = (uintptr_t)aligned - (uintptr_t)mem;
+    n = delta <= n ? n - delta : 0;
+    mem = aligned;
+  }
+
+  /* Round block size down to alignof(*a) since we will allocate the arena
+   * itself at the end. */
+  n = UPB_ALIGN_DOWN(n, UPB_ALIGN_OF(upb_Arena));
+
+  if (UPB_UNLIKELY(n < sizeof(upb_Arena))) {
+    return arena_initslow(mem, n, alloc);
+  }
+
+  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena);
+
+  a->head.alloc.func = &upb_Arena_doalloc;
+  a->block_alloc = alloc;
+  a->parent = a;
+  a->refcount = 1;
+  a->last_size = UPB_MAX(128, n);
+  a->head.ptr = mem;
+  a->head.end = UPB_PTR_AT(mem, n - sizeof(*a), char);
+  a->freelist = NULL;
+  a->cleanup_metadata = upb_cleanup_metadata(NULL, true);
+
+  return a;
+}
+
+static void arena_dofree(upb_Arena* a) {
+  mem_block* block = a->freelist;
+  UPB_ASSERT(a->parent == a);
+  UPB_ASSERT(a->refcount == 0);
+
+  while (block) {
+    /* Load first since we are deleting block. */
+    mem_block* next = block->next;
+
+    if (block->cleanups > 0) {
+      cleanup_ent* end = UPB_PTR_AT(block, block->size, void);
+      cleanup_ent* ptr = end - block->cleanups;
+
+      for (; ptr < end; ptr++) {
+        ptr->cleanup(ptr->ud);
+      }
+    }
+
+    upb_free(a->block_alloc, block);
+    block = next;
+  }
+}
+
+void upb_Arena_Free(upb_Arena* a) {
+  a = arena_findroot(a);
+  if (--a->refcount == 0) arena_dofree(a);
+}
+
+bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func) {
+  cleanup_ent* ent;
+  uint32_t* cleanups = upb_cleanup_pointer(a->cleanup_metadata);
+
+  if (!cleanups || _upb_ArenaHas(a) < sizeof(cleanup_ent)) {
+    if (!upb_Arena_Allocblock(a, 128)) return false; /* Out of memory. */
+    UPB_ASSERT(_upb_ArenaHas(a) >= sizeof(cleanup_ent));
+    cleanups = upb_cleanup_pointer(a->cleanup_metadata);
+  }
+
+  a->head.end -= sizeof(cleanup_ent);
+  ent = (cleanup_ent*)a->head.end;
+  (*cleanups)++;
+  UPB_UNPOISON_MEMORY_REGION(ent, sizeof(cleanup_ent));
+
+  ent->cleanup = func;
+  ent->ud = ud;
+
+  return true;
+}
+
+bool upb_Arena_Fuse(upb_Arena* a1, upb_Arena* a2) {
+  upb_Arena* r1 = arena_findroot(a1);
+  upb_Arena* r2 = arena_findroot(a2);
+
+  if (r1 == r2) return true; /* Already fused. */
+
+  /* Do not fuse initial blocks since we cannot lifetime extend them. */
+  if (upb_cleanup_has_initial_block(r1->cleanup_metadata)) return false;
+  if (upb_cleanup_has_initial_block(r2->cleanup_metadata)) return false;
+
+  /* Only allow fuse with a common allocator */
+  if (r1->block_alloc != r2->block_alloc) return false;
+
+  /* We want to join the smaller tree to the larger tree.
+   * So swap first if they are backwards. */
+  if (r1->refcount < r2->refcount) {
+    upb_Arena* tmp = r1;
+    r1 = r2;
+    r2 = tmp;
+  }
+
+  /* r1 takes over r2's freelist and refcount. */
+  r1->refcount += r2->refcount;
+  if (r2->freelist_tail) {
+    UPB_ASSERT(r2->freelist_tail->next == NULL);
+    r2->freelist_tail->next = r1->freelist;
+    r1->freelist = r2->freelist;
+  }
+  r2->parent = r1;
+  return true;
+}
diff --git a/upb/arena.h b/upb/arena.h
new file mode 100644
index 0000000..0c4fd1b
--- /dev/null
+++ b/upb/arena.h
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_ARENA_H_
+#define UPB_ARENA_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** upb_alloc *****************************************************************/
+
+/* A upb_alloc is a possibly-stateful allocator object.
+ *
+ * It could either be an arena allocator (which doesn't require individual
+ * free() calls) or a regular malloc() (which does).  The client must therefore
+ * free memory unless it knows that the allocator is an arena allocator. */
+
+struct upb_alloc;
+typedef struct upb_alloc upb_alloc;
+
+/* A malloc()/free() function.
+ * If "size" is 0 then the function acts like free(), otherwise it acts like
+ * realloc().  Only "oldsize" bytes from a previous allocation are preserved. */
+typedef void* upb_alloc_func(upb_alloc* alloc, void* ptr, size_t oldsize,
+                             size_t size);
+
+struct upb_alloc {
+  upb_alloc_func* func;
+};
+
+UPB_INLINE void* upb_malloc(upb_alloc* alloc, size_t size) {
+  UPB_ASSERT(alloc);
+  return alloc->func(alloc, NULL, 0, size);
+}
+
+UPB_INLINE void* upb_realloc(upb_alloc* alloc, void* ptr, size_t oldsize,
+                             size_t size) {
+  UPB_ASSERT(alloc);
+  return alloc->func(alloc, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_free(upb_alloc* alloc, void* ptr) {
+  assert(alloc);
+  alloc->func(alloc, ptr, 0, 0);
+}
+
+/* The global allocator used by upb.  Uses the standard malloc()/free(). */
+
+extern upb_alloc upb_alloc_global;
+
+/* Functions that hard-code the global malloc.
+ *
+ * We still get benefit because we can put custom logic into our global
+ * allocator, like injecting out-of-memory faults in debug/testing builds. */
+
+UPB_INLINE void* upb_gmalloc(size_t size) {
+  return upb_malloc(&upb_alloc_global, size);
+}
+
+UPB_INLINE void* upb_grealloc(void* ptr, size_t oldsize, size_t size) {
+  return upb_realloc(&upb_alloc_global, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_gfree(void* ptr) { upb_free(&upb_alloc_global, ptr); }
+
+/* upb_Arena ******************************************************************/
+
+/* upb_Arena is a specific allocator implementation that uses arena allocation.
+ * The user provides an allocator that will be used to allocate the underlying
+ * arena blocks.  Arenas by nature do not require the individual allocations
+ * to be freed.  However the Arena does allow users to register cleanup
+ * functions that will run when the arena is destroyed.
+ *
+ * A upb_Arena is *not* thread-safe.
+ *
+ * You could write a thread-safe arena allocator that satisfies the
+ * upb_alloc interface, but it would not be as efficient for the
+ * single-threaded case. */
+
+typedef void upb_CleanupFunc(void* ud);
+
+struct upb_Arena;
+typedef struct upb_Arena upb_Arena;
+
+typedef struct {
+  /* We implement the allocator interface.
+   * This must be the first member of upb_Arena!
+   * TODO(haberman): remove once handlers are gone. */
+  upb_alloc alloc;
+
+  char *ptr, *end;
+} _upb_ArenaHead;
+
+/* Creates an arena from the given initial block (if any -- n may be 0).
+ * Additional blocks will be allocated from |alloc|.  If |alloc| is NULL, this
+ * is a fixed-size arena and cannot grow. */
+upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc);
+void upb_Arena_Free(upb_Arena* a);
+bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func);
+bool upb_Arena_Fuse(upb_Arena* a, upb_Arena* b);
+void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size);
+
+UPB_INLINE upb_alloc* upb_Arena_Alloc(upb_Arena* a) { return (upb_alloc*)a; }
+
+UPB_INLINE size_t _upb_ArenaHas(upb_Arena* a) {
+  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
+  return (size_t)(h->end - h->ptr);
+}
+
+UPB_INLINE void* _upb_Arena_FastMalloc(upb_Arena* a, size_t size) {
+  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
+  void* ret = h->ptr;
+  UPB_ASSERT(UPB_ALIGN_MALLOC((uintptr_t)ret) == (uintptr_t)ret);
+  UPB_ASSERT(UPB_ALIGN_MALLOC(size) == size);
+  UPB_UNPOISON_MEMORY_REGION(ret, size);
+
+  h->ptr += size;
+
+#if UPB_ASAN
+  {
+    size_t guard_size = 32;
+    if (_upb_ArenaHas(a) >= guard_size) {
+      h->ptr += guard_size;
+    } else {
+      h->ptr = h->end;
+    }
+  }
+#endif
+
+  return ret;
+}
+
+UPB_INLINE void* upb_Arena_Malloc(upb_Arena* a, size_t size) {
+  size = UPB_ALIGN_MALLOC(size);
+
+  if (UPB_UNLIKELY(_upb_ArenaHas(a) < size)) {
+    return _upb_Arena_SlowMalloc(a, size);
+  }
+
+  return _upb_Arena_FastMalloc(a, size);
+}
+
+// Shrinks the last alloc from arena.
+// REQUIRES: (ptr, oldsize) was the last malloc/realloc from this arena.
+// We could also add a upb_Arena_TryShrinkLast() which is simply a no-op if
+// this was not the last alloc.
+UPB_INLINE void upb_Arena_ShrinkLast(upb_Arena* a, void* ptr, size_t oldsize,
+                                     size_t size) {
+  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
+  oldsize = UPB_ALIGN_MALLOC(oldsize);
+  size = UPB_ALIGN_MALLOC(size);
+  UPB_ASSERT((char*)ptr + oldsize == h->ptr);  // Must be the last alloc.
+  UPB_ASSERT(size <= oldsize);
+  h->ptr = (char*)ptr + size;
+}
+
+UPB_INLINE void* upb_Arena_Realloc(upb_Arena* a, void* ptr, size_t oldsize,
+                                   size_t size) {
+  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
+  oldsize = UPB_ALIGN_MALLOC(oldsize);
+  size = UPB_ALIGN_MALLOC(size);
+  bool is_most_recent_alloc = (uintptr_t)ptr + oldsize == (uintptr_t)h->ptr;
+
+  if (is_most_recent_alloc) {
+    ptrdiff_t diff = size - oldsize;
+    if ((ptrdiff_t)_upb_ArenaHas(a) >= diff) {
+      h->ptr += diff;
+      return ptr;
+    }
+  } else if (size <= oldsize) {
+    return ptr;
+  }
+
+  void* ret = upb_Arena_Malloc(a, size);
+
+  if (ret && oldsize > 0) {
+    memcpy(ret, ptr, UPB_MIN(oldsize, size));
+  }
+
+  return ret;
+}
+
+UPB_INLINE upb_Arena* upb_Arena_New(void) {
+  return upb_Arena_Init(NULL, 0, &upb_alloc_global);
+}
+
+#include "upb/port_undef.inc"
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* UPB_ARENA_H_ */
diff --git a/upb/collections.c b/upb/array.c
similarity index 60%
copy from upb/collections.c
copy to upb/array.c
index 6bf7fbb..b4cdc49 100644
--- a/upb/collections.c
+++ b/upb/array.c
@@ -25,29 +25,13 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "upb/collections.h"
+#include "upb/array.h"
 
 #include <string.h>
 
+#include "upb/internal/table.h"
 #include "upb/msg.h"
 #include "upb/port_def.inc"
-#include "upb/table_internal.h"
-
-/* Strings/bytes are special-cased in maps. */
-static char _upb_CTypeo_mapsize[12] = {
-    0,
-    1,             /* kUpb_CType_Bool */
-    4,             /* kUpb_CType_Float */
-    4,             /* kUpb_CType_Int32 */
-    4,             /* kUpb_CType_UInt32 */
-    4,             /* kUpb_CType_Enum */
-    sizeof(void*), /* kUpb_CType_Message */
-    8,             /* kUpb_CType_Double */
-    8,             /* kUpb_CType_Int64 */
-    8,             /* kUpb_CType_UInt64 */
-    0,             /* kUpb_CType_String */
-    0,             /* kUpb_CType_Bytes */
-};
 
 static const char _upb_CTypeo_sizelg2[12] = {
     0,
@@ -64,8 +48,6 @@
     UPB_SIZE(3, 4), /* kUpb_CType_Bytes */
 };
 
-/** upb_Array *****************************************************************/
-
 upb_Array* upb_Array_New(upb_Arena* a, upb_CType type) {
   return _upb_Array_New(a, 4, _upb_CTypeo_sizelg2[type]);
 }
@@ -130,63 +112,3 @@
 bool upb_Array_Resize(upb_Array* arr, size_t size, upb_Arena* arena) {
   return _upb_Array_Resize(arr, size, arena);
 }
-
-/** upb_Map *******************************************************************/
-
-upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type) {
-  return _upb_Map_New(a, _upb_CTypeo_mapsize[key_type],
-                      _upb_CTypeo_mapsize[value_type]);
-}
-
-size_t upb_Map_Size(const upb_Map* map) { return _upb_Map_Size(map); }
-
-bool upb_Map_Get(const upb_Map* map, upb_MessageValue key,
-                 upb_MessageValue* val) {
-  return _upb_Map_Get(map, &key, map->key_size, val, map->val_size);
-}
-
-void upb_Map_Clear(upb_Map* map) { _upb_Map_Clear(map); }
-
-upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key,
-                                   upb_MessageValue val, upb_Arena* arena) {
-  return (upb_MapInsertStatus)_upb_Map_Insert(map, &key, map->key_size, &val,
-                                              map->val_size, arena);
-}
-
-bool upb_Map_Delete(upb_Map* map, upb_MessageValue key) {
-  return _upb_Map_Delete(map, &key, map->key_size);
-}
-
-bool upb_MapIterator_Next(const upb_Map* map, size_t* iter) {
-  return _upb_map_next(map, iter);
-}
-
-bool upb_MapIterator_Done(const upb_Map* map, size_t iter) {
-  upb_strtable_iter i;
-  UPB_ASSERT(iter != kUpb_Map_Begin);
-  i.t = &map->table;
-  i.index = iter;
-  return upb_strtable_done(&i);
-}
-
-/* Returns the key and value for this entry of the map. */
-upb_MessageValue upb_MapIterator_Key(const upb_Map* map, size_t iter) {
-  upb_strtable_iter i;
-  upb_MessageValue ret;
-  i.t = &map->table;
-  i.index = iter;
-  _upb_map_fromkey(upb_strtable_iter_key(&i), &ret, map->key_size);
-  return ret;
-}
-
-upb_MessageValue upb_MapIterator_Value(const upb_Map* map, size_t iter) {
-  upb_strtable_iter i;
-  upb_MessageValue ret;
-  i.t = &map->table;
-  i.index = iter;
-  _upb_map_fromvalue(upb_strtable_iter_value(&i), &ret, map->val_size);
-  return ret;
-}
-
-/* void upb_MapIterator_SetValue(upb_Map *map, size_t iter, upb_MessageValue
- * value); */
diff --git a/upb/array.h b/upb/array.h
new file mode 100644
index 0000000..0715d58
--- /dev/null
+++ b/upb/array.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_ARRAY_H_
+#define UPB_ARRAY_H_
+
+#include "google/protobuf/descriptor.upb.h"
+#include "upb/message_value.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Creates a new array on the given arena that holds elements of this type. */
+upb_Array* upb_Array_New(upb_Arena* a, upb_CType type);
+
+/* Returns the size of the array. */
+size_t upb_Array_Size(const upb_Array* arr);
+
+/* Returns the given element, which must be within the array's current size. */
+upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i);
+
+/* Sets the given element, which must be within the array's current size. */
+void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val);
+
+/* Appends an element to the array.  Returns false on allocation failure. */
+bool upb_Array_Append(upb_Array* array, upb_MessageValue val, upb_Arena* arena);
+
+/* Moves elements within the array using memmove(). Like memmove(), the source
+ * and destination elements may be overlapping. */
+void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx,
+                    size_t count);
+
+/* Inserts one or more empty elements into the array.  Existing elements are
+ * shifted right.  The new elements have undefined state and must be set with
+ * `upb_Array_Set()`.
+ * REQUIRES: `i <= upb_Array_Size(arr)` */
+bool upb_Array_Insert(upb_Array* array, size_t i, size_t count,
+                      upb_Arena* arena);
+
+/* Deletes one or more elements from the array.  Existing elements are shifted
+ * left.
+ * REQUIRES: `i + count <= upb_Array_Size(arr)` */
+void upb_Array_Delete(upb_Array* array, size_t i, size_t count);
+
+/* Changes the size of a vector.  New elements are initialized to empty/0.
+ * Returns false on allocation failure. */
+bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif /* UPB_ARRAY_H_ */
diff --git a/upb/collections.h b/upb/collections.h
index 047e14e..81558dc 100644
--- a/upb/collections.h
+++ b/upb/collections.h
@@ -28,147 +28,9 @@
 #ifndef UPB_COLLECTIONS_H_
 #define UPB_COLLECTIONS_H_
 
-#include "google/protobuf/descriptor.upb.h"
-#include "upb/port_def.inc"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef union {
-  bool bool_val;
-  float float_val;
-  double double_val;
-  int32_t int32_val;
-  int64_t int64_val;
-  uint32_t uint32_val;
-  uint64_t uint64_val;
-  const upb_Map* map_val;
-  const upb_Message* msg_val;
-  const upb_Array* array_val;
-  upb_StringView str_val;
-} upb_MessageValue;
-
-typedef union {
-  upb_Map* map;
-  upb_Message* msg;
-  upb_Array* array;
-} upb_MutableMessageValue;
-
-/** upb_Array *****************************************************************/
-
-/* Creates a new array on the given arena that holds elements of this type. */
-upb_Array* upb_Array_New(upb_Arena* a, upb_CType type);
-
-/* Returns the size of the array. */
-size_t upb_Array_Size(const upb_Array* arr);
-
-/* Returns the given element, which must be within the array's current size. */
-upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i);
-
-/* Sets the given element, which must be within the array's current size. */
-void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val);
-
-/* Appends an element to the array.  Returns false on allocation failure. */
-bool upb_Array_Append(upb_Array* array, upb_MessageValue val, upb_Arena* arena);
-
-/* Moves elements within the array using memmove(). Like memmove(), the source
- * and destination elements may be overlapping. */
-void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx,
-                    size_t count);
-
-/* Inserts one or more empty elements into the array.  Existing elements are
- * shifted right.  The new elements have undefined state and must be set with
- * `upb_Array_Set()`.
- * REQUIRES: `i <= upb_Array_Size(arr)` */
-bool upb_Array_Insert(upb_Array* array, size_t i, size_t count,
-                      upb_Arena* arena);
-
-/* Deletes one or more elements from the array.  Existing elements are shifted
- * left.
- * REQUIRES: `i + count <= upb_Array_Size(arr)` */
-void upb_Array_Delete(upb_Array* array, size_t i, size_t count);
-
-/* Changes the size of a vector.  New elements are initialized to empty/0.
- * Returns false on allocation failure. */
-bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena);
-
-/** upb_Map *******************************************************************/
-
-/* Creates a new map on the given arena with the given key/value size. */
-upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type);
-
-/* Returns the number of entries in the map. */
-size_t upb_Map_Size(const upb_Map* map);
-
-/* Stores a value for the given key into |*val| (or the zero value if the key is
- * not present).  Returns whether the key was present.  The |val| pointer may be
- * NULL, in which case the function tests whether the given key is present.  */
-bool upb_Map_Get(const upb_Map* map, upb_MessageValue key,
-                 upb_MessageValue* val);
-
-/* Removes all entries in the map. */
-void upb_Map_Clear(upb_Map* map);
-
-typedef enum {
-  // LINT.IfChange
-  kUpb_MapInsertStatus_Inserted = 0,
-  kUpb_MapInsertStatus_Replaced = 1,
-  kUpb_MapInsertStatus_OutOfMemory = 2,
-  // LINT.ThenChange(//depot/google3/third_party/upb/upb/msg_internal.h)
-} upb_MapInsertStatus;
-
-/* Sets the given key to the given value, returning whether the key was inserted
- * or replaced.  If the key was inserted, then any existing iterators will be
- * invalidated. */
-upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key,
-                                   upb_MessageValue val, upb_Arena* arena);
-
-/* Sets the given key to the given value.  Returns false if memory allocation
- * failed. If the key is newly inserted, then any existing iterators will be
- * invalidated. */
-UPB_INLINE bool upb_Map_Set(upb_Map* map, upb_MessageValue key,
-                            upb_MessageValue val, upb_Arena* arena) {
-  return upb_Map_Insert(map, key, val, arena) !=
-         kUpb_MapInsertStatus_OutOfMemory;
-}
-
-/* Deletes this key from the table.  Returns true if the key was present. */
-bool upb_Map_Delete(upb_Map* map, upb_MessageValue key);
-
-/* Map iteration:
- *
- * size_t iter = kUpb_Map_Begin;
- * while (upb_MapIterator_Next(map, &iter)) {
- *   upb_MessageValue key = upb_MapIterator_Key(map, iter);
- *   upb_MessageValue val = upb_MapIterator_Value(map, iter);
- *
- *   // If mutating is desired.
- *   upb_MapIterator_SetValue(map, iter, value2);
- * }
- */
-
-/* Advances to the next entry.  Returns false if no more entries are present. */
-bool upb_MapIterator_Next(const upb_Map* map, size_t* iter);
-
-/* Returns true if the iterator still points to a valid entry, or false if the
- * iterator is past the last element. It is an error to call this function with
- * kUpb_Map_Begin (you must call next() at least once first). */
-bool upb_MapIterator_Done(const upb_Map* map, size_t iter);
-
-/* Returns the key and value for this entry of the map. */
-upb_MessageValue upb_MapIterator_Key(const upb_Map* map, size_t iter);
-upb_MessageValue upb_MapIterator_Value(const upb_Map* map, size_t iter);
-
-/* Sets the value for this entry.  The iterator must not be done, and the
- * iterator must not have been initialized const. */
-void upb_MapIterator_SetValue(upb_Map* map, size_t iter,
-                              upb_MessageValue value);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#include "upb/port_undef.inc"
+// TODO(b/232091617): Delete this entire header which currently exists only for
+// temporary backwards compatibility.
+#include "upb/array.h"
+#include "upb/map.h"
 
 #endif /* UPB_COLLECTIONS_H_ */
diff --git a/upb/decode.c b/upb/decode.c
index e6dac27..d314875 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -30,9 +30,9 @@
 #include <setjmp.h>
 #include <string.h>
 
-#include "upb/decode_internal.h"
+#include "upb/internal/decode.h"
+#include "upb/internal/upb.h"
 #include "upb/upb.h"
-#include "upb/upb_internal.h"
 
 /* Must be last. */
 #include "upb/port_def.inc"
diff --git a/upb/decode.h b/upb/decode.h
index 1fa8131..0c09497 100644
--- a/upb/decode.h
+++ b/upb/decode.h
@@ -32,6 +32,7 @@
 #ifndef UPB_DECODE_H_
 #define UPB_DECODE_H_
 
+#include "upb/extension_registry.h"
 #include "upb/msg.h"
 
 /* Must be last. */
diff --git a/upb/decode_fast.c b/upb/decode_fast.c
index e05bf8e..a02ad09 100644
--- a/upb/decode_fast.c
+++ b/upb/decode_fast.c
@@ -37,7 +37,7 @@
 
 #include "upb/decode_fast.h"
 
-#include "upb/decode_internal.h"
+#include "upb/internal/decode.h"
 
 /* Must be last. */
 #include "upb/port_def.inc"
diff --git a/upb/decode_internal.h b/upb/decode_internal.h
index 11bb9aa..fe3cbc7 100644
--- a/upb/decode_internal.h
+++ b/upb/decode_internal.h
@@ -25,187 +25,12 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/*
- * Internal implementation details of the decoder that are shared between
- * decode.c and decode_fast.c.
- */
-
 #ifndef UPB_DECODE_INT_H_
 #define UPB_DECODE_INT_H_
 
-#include <setjmp.h>
+// TODO(b/232091617): Delete this entire header which currently exists only for
+// temporary backwards compatibility.
 
-#include "third_party/utf8_range/utf8_range.h"
-#include "upb/decode.h"
-#include "upb/msg_internal.h"
-#include "upb/upb_internal.h"
-
-/* Must be last. */
-#include "upb/port_def.inc"
-
-#define DECODE_NOGROUP (uint32_t) - 1
-
-typedef struct upb_Decoder {
-  const char* end;          /* Can read up to 16 bytes slop beyond this. */
-  const char* limit_ptr;    /* = end + UPB_MIN(limit, 0) */
-  upb_Message* unknown_msg; /* If non-NULL, add unknown data at buffer flip. */
-  const char* unknown;      /* Start of unknown data. */
-  const upb_ExtensionRegistry*
-      extreg;         /* For looking up extensions during the parse. */
-  int limit;          /* Submessage limit relative to end. */
-  int depth;          /* Tracks recursion depth to bound stack usage. */
-  uint32_t end_group; /* field number of END_GROUP tag, else DECODE_NOGROUP */
-  uint16_t options;
-  bool missing_required;
-  char patch[32];
-  upb_Arena arena;
-  jmp_buf err;
-
-#ifndef NDEBUG
-  const char* debug_tagstart;
-  const char* debug_valstart;
-#endif
-} upb_Decoder;
-
-/* Error function that will abort decoding with longjmp(). We can't declare this
- * UPB_NORETURN, even though it is appropriate, because if we do then compilers
- * will "helpfully" refuse to tailcall to it
- * (see: https://stackoverflow.com/a/55657013), which will defeat a major goal
- * of our optimizations. That is also why we must declare it in a separate file,
- * otherwise the compiler will see that it calls longjmp() and deduce that it is
- * noreturn. */
-const char* fastdecode_err(upb_Decoder* d, int status);
-
-extern const uint8_t upb_utf8_offsets[];
-
-UPB_INLINE
-bool decode_verifyutf8_inl(const char* ptr, int len) {
-  const char* end = ptr + len;
-
-  // Check 8 bytes at a time for any non-ASCII char.
-  while (end - ptr >= 8) {
-    uint64_t data;
-    memcpy(&data, ptr, 8);
-    if (data & 0x8080808080808080) goto non_ascii;
-    ptr += 8;
-  }
-
-  // Check one byte at a time for non-ASCII.
-  while (ptr < end) {
-    if (*ptr & 0x80) goto non_ascii;
-    ptr++;
-  }
-
-  return true;
-
-non_ascii:
-  return utf8_range2((const unsigned char*)ptr, end - ptr) == 0;
-}
-
-const char* decode_checkrequired(upb_Decoder* d, const char* ptr,
-                                 const upb_Message* msg,
-                                 const upb_MiniTable* l);
-
-/* x86-64 pointers always have the high 16 bits matching. So we can shift
- * left 8 and right 8 without loss of information. */
-UPB_INLINE intptr_t decode_totable(const upb_MiniTable* tablep) {
-  return ((intptr_t)tablep << 8) | tablep->table_mask;
-}
-
-UPB_INLINE const upb_MiniTable* decode_totablep(intptr_t table) {
-  return (const upb_MiniTable*)(table >> 8);
-}
-
-UPB_INLINE
-const char* decode_isdonefallback_inl(upb_Decoder* d, const char* ptr,
-                                      int overrun, int* status) {
-  if (overrun < d->limit) {
-    /* Need to copy remaining data into patch buffer. */
-    UPB_ASSERT(overrun < 16);
-    if (d->unknown_msg) {
-      if (!_upb_Message_AddUnknown(d->unknown_msg, d->unknown, ptr - d->unknown,
-                                   &d->arena)) {
-        *status = kUpb_DecodeStatus_OutOfMemory;
-        return NULL;
-      }
-      d->unknown = &d->patch[0] + overrun;
-    }
-    memset(d->patch + 16, 0, 16);
-    memcpy(d->patch, d->end, 16);
-    ptr = &d->patch[0] + overrun;
-    d->end = &d->patch[16];
-    d->limit -= 16;
-    d->limit_ptr = d->end + d->limit;
-    d->options &= ~kUpb_DecodeOption_AliasString;
-    UPB_ASSERT(ptr < d->limit_ptr);
-    return ptr;
-  } else {
-    *status = kUpb_DecodeStatus_Malformed;
-    return NULL;
-  }
-}
-
-const char* decode_isdonefallback(upb_Decoder* d, const char* ptr, int overrun);
-
-UPB_INLINE
-bool decode_isdone(upb_Decoder* d, const char** ptr) {
-  int overrun = *ptr - d->end;
-  if (UPB_LIKELY(*ptr < d->limit_ptr)) {
-    return false;
-  } else if (UPB_LIKELY(overrun == d->limit)) {
-    return true;
-  } else {
-    *ptr = decode_isdonefallback(d, *ptr, overrun);
-    return false;
-  }
-}
-
-#if UPB_FASTTABLE
-UPB_INLINE
-const char* fastdecode_tagdispatch(upb_Decoder* d, const char* ptr,
-                                   upb_Message* msg, intptr_t table,
-                                   uint64_t hasbits, uint64_t tag) {
-  const upb_MiniTable* table_p = decode_totablep(table);
-  uint8_t mask = table;
-  uint64_t data;
-  size_t idx = tag & mask;
-  UPB_ASSUME((idx & 7) == 0);
-  idx >>= 3;
-  data = table_p->fasttable[idx].field_data ^ tag;
-  UPB_MUSTTAIL return table_p->fasttable[idx].field_parser(d, ptr, msg, table,
-                                                           hasbits, data);
-}
-#endif
-
-UPB_INLINE uint32_t fastdecode_loadtag(const char* ptr) {
-  uint16_t tag;
-  memcpy(&tag, ptr, 2);
-  return tag;
-}
-
-UPB_INLINE void decode_checklimit(upb_Decoder* d) {
-  UPB_ASSERT(d->limit_ptr == d->end + UPB_MIN(0, d->limit));
-}
-
-UPB_INLINE int decode_pushlimit(upb_Decoder* d, const char* ptr, int size) {
-  int limit = size + (int)(ptr - d->end);
-  int delta = d->limit - limit;
-  decode_checklimit(d);
-  d->limit = limit;
-  d->limit_ptr = d->end + UPB_MIN(0, limit);
-  decode_checklimit(d);
-  return delta;
-}
-
-UPB_INLINE void decode_poplimit(upb_Decoder* d, const char* ptr,
-                                int saved_delta) {
-  UPB_ASSERT(ptr - d->end == d->limit);
-  decode_checklimit(d);
-  d->limit += saved_delta;
-  d->limit_ptr = d->end + UPB_MIN(0, d->limit);
-  decode_checklimit(d);
-}
-
-#include "upb/port_undef.inc"
+#include "upb/internal/decode.h"
 
 #endif /* UPB_DECODE_INT_H_ */
diff --git a/upb/def.h b/upb/def.h
index c520f85..2cb312d 100644
--- a/upb/def.h
+++ b/upb/def.h
@@ -29,7 +29,7 @@
 #define UPB_DEF_H_
 
 #include "google/protobuf/descriptor.upb.h"
-#include "upb/table_internal.h"
+#include "upb/internal/table.h"
 #include "upb/upb.h"
 
 /* Must be last. */
diff --git a/upb/encode.c b/upb/encode.c
index c917397..048ecd2 100644
--- a/upb/encode.c
+++ b/upb/encode.c
@@ -32,6 +32,7 @@
 #include <setjmp.h>
 #include <string.h>
 
+#include "upb/extension_registry.h"
 #include "upb/msg_internal.h"
 #include "upb/upb.h"
 
diff --git a/upb/extension_registry.c b/upb/extension_registry.c
new file mode 100644
index 0000000..921cf22
--- /dev/null
+++ b/upb/extension_registry.c
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include "upb/extension_registry.h"
+
+#include "upb/internal/table.h"
+#include "upb/msg.h"
+#include "upb/msg_internal.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+struct upb_ExtensionRegistry {
+  upb_Arena* arena;
+  upb_strtable exts; /* Key is upb_MiniTable* concatenated with fieldnum. */
+};
+
+#define EXTREG_KEY_SIZE (sizeof(upb_MiniTable*) + sizeof(uint32_t))
+
+static void extreg_key(char* buf, const upb_MiniTable* l, uint32_t fieldnum) {
+  memcpy(buf, &l, sizeof(l));
+  memcpy(buf + sizeof(l), &fieldnum, sizeof(fieldnum));
+}
+
+upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena) {
+  upb_ExtensionRegistry* r = upb_Arena_Malloc(arena, sizeof(*r));
+  if (!r) return NULL;
+  r->arena = arena;
+  if (!upb_strtable_init(&r->exts, 8, arena)) return NULL;
+  return r;
+}
+
+bool _upb_extreg_add(upb_ExtensionRegistry* r,
+                     const upb_MiniTable_Extension** e, size_t count) {
+  char buf[EXTREG_KEY_SIZE];
+  const upb_MiniTable_Extension** start = e;
+  const upb_MiniTable_Extension** end = UPB_PTRADD(e, count);
+  for (; e < end; e++) {
+    const upb_MiniTable_Extension* ext = *e;
+    extreg_key(buf, ext->extendee, ext->field.number);
+    if (!upb_strtable_insert(&r->exts, buf, EXTREG_KEY_SIZE,
+                             upb_value_constptr(ext), r->arena)) {
+      goto failure;
+    }
+  }
+  return true;
+
+failure:
+  /* Back out the entries previously added. */
+  for (end = e, e = start; e < end; e++) {
+    const upb_MiniTable_Extension* ext = *e;
+    extreg_key(buf, ext->extendee, ext->field.number);
+    upb_strtable_remove2(&r->exts, buf, EXTREG_KEY_SIZE, NULL);
+  }
+  return false;
+}
+
+const upb_MiniTable_Extension* _upb_extreg_get(const upb_ExtensionRegistry* r,
+                                               const upb_MiniTable* l,
+                                               uint32_t num) {
+  char buf[EXTREG_KEY_SIZE];
+  upb_value v;
+  extreg_key(buf, l, num);
+  if (upb_strtable_lookup2(&r->exts, buf, EXTREG_KEY_SIZE, &v)) {
+    return upb_value_getconstptr(v);
+  } else {
+    return NULL;
+  }
+}
diff --git a/upb/extension_registry.h b/upb/extension_registry.h
new file mode 100644
index 0000000..0e0a444
--- /dev/null
+++ b/upb/extension_registry.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_EXTENSION_REGISTRY_H_
+#define UPB_EXTENSION_REGISTRY_H_
+
+#include <stddef.h>
+
+#include "upb/upb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Extension registry: a dynamic data structure that stores a map of:
+ *   (upb_MiniTable, number) -> extension info
+ *
+ * upb_decode() uses upb_ExtensionRegistry to look up extensions while parsing
+ * binary format.
+ *
+ * upb_ExtensionRegistry is part of the mini-table (msglayout) family of
+ * objects. Like all mini-table objects, it is suitable for reflection-less
+ * builds that do not want to expose names into the binary.
+ *
+ * Unlike most mini-table types, upb_ExtensionRegistry requires dynamic memory
+ * allocation and dynamic initialization:
+ * * If reflection is being used, then upb_DefPool will construct an appropriate
+ *   upb_ExtensionRegistry automatically.
+ * * For a mini-table only build, the user must manually construct the
+ *   upb_ExtensionRegistry and populate it with all of the extensions the user
+ * cares about.
+ * * A third alternative is to manually unpack relevant extensions after the
+ *   main parse is complete, similar to how Any works. This is perhaps the
+ *   nicest solution from the perspective of reducing dependencies, avoiding
+ *   dynamic memory allocation, and avoiding the need to parse uninteresting
+ *   extensions.  The downsides are:
+ *     (1) parse errors are not caught during the main parse
+ *     (2) the CPU hit of parsing comes during access, which could cause an
+ *         undesirable stutter in application performance.
+ *
+ * Users cannot directly get or put into this map. Users can only add the
+ * extensions from a generated module and pass the extension registry to the
+ * binary decoder.
+ *
+ * A upb_DefPool provides a upb_ExtensionRegistry, so any users who use
+ * reflection do not need to populate a upb_ExtensionRegistry directly.
+ */
+
+struct upb_ExtensionRegistry;
+typedef struct upb_ExtensionRegistry upb_ExtensionRegistry;
+
+/* Creates a upb_ExtensionRegistry in the given arena.  The arena must outlive
+ * any use of the extreg. */
+upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* UPB_EXTENSION_REGISTRY_H_ */
diff --git a/upb/internal/decode.h b/upb/internal/decode.h
new file mode 100644
index 0000000..708874c
--- /dev/null
+++ b/upb/internal/decode.h
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+/*
+ * Internal implementation details of the decoder that are shared between
+ * decode.c and decode_fast.c.
+ */
+
+#ifndef UPB_INTERNAL_DECODE_H_
+#define UPB_INTERNAL_DECODE_H_
+
+#include <setjmp.h>
+
+#include "upb/decode.h"
+#include "upb/internal/upb.h"
+#include "upb/msg_internal.h"
+#include "third_party/utf8_range/utf8_range.h"
+
+/* Must be last. */
+#include "upb/port_def.inc"
+
+#define DECODE_NOGROUP (uint32_t) - 1
+
+typedef struct upb_Decoder {
+  const char* end;          /* Can read up to 16 bytes slop beyond this. */
+  const char* limit_ptr;    /* = end + UPB_MIN(limit, 0) */
+  upb_Message* unknown_msg; /* If non-NULL, add unknown data at buffer flip. */
+  const char* unknown;      /* Start of unknown data. */
+  const upb_ExtensionRegistry*
+      extreg;         /* For looking up extensions during the parse. */
+  int limit;          /* Submessage limit relative to end. */
+  int depth;          /* Tracks recursion depth to bound stack usage. */
+  uint32_t end_group; /* field number of END_GROUP tag, else DECODE_NOGROUP */
+  uint16_t options;
+  bool missing_required;
+  char patch[32];
+  upb_Arena arena;
+  jmp_buf err;
+
+#ifndef NDEBUG
+  const char* debug_tagstart;
+  const char* debug_valstart;
+#endif
+} upb_Decoder;
+
+/* Error function that will abort decoding with longjmp(). We can't declare this
+ * UPB_NORETURN, even though it is appropriate, because if we do then compilers
+ * will "helpfully" refuse to tailcall to it
+ * (see: https://stackoverflow.com/a/55657013), which will defeat a major goal
+ * of our optimizations. That is also why we must declare it in a separate file,
+ * otherwise the compiler will see that it calls longjmp() and deduce that it is
+ * noreturn. */
+const char* fastdecode_err(upb_Decoder* d, int status);
+
+extern const uint8_t upb_utf8_offsets[];
+
+UPB_INLINE
+bool decode_verifyutf8_inl(const char* ptr, int len) {
+  const char* end = ptr + len;
+
+  // Check 8 bytes at a time for any non-ASCII char.
+  while (end - ptr >= 8) {
+    uint64_t data;
+    memcpy(&data, ptr, 8);
+    if (data & 0x8080808080808080) goto non_ascii;
+    ptr += 8;
+  }
+
+  // Check one byte at a time for non-ASCII.
+  while (ptr < end) {
+    if (*ptr & 0x80) goto non_ascii;
+    ptr++;
+  }
+
+  return true;
+
+non_ascii:
+  return utf8_range2((const unsigned char*)ptr, end - ptr) == 0;
+}
+
+const char* decode_checkrequired(upb_Decoder* d, const char* ptr,
+                                 const upb_Message* msg,
+                                 const upb_MiniTable* l);
+
+/* x86-64 pointers always have the high 16 bits matching. So we can shift
+ * left 8 and right 8 without loss of information. */
+UPB_INLINE intptr_t decode_totable(const upb_MiniTable* tablep) {
+  return ((intptr_t)tablep << 8) | tablep->table_mask;
+}
+
+UPB_INLINE const upb_MiniTable* decode_totablep(intptr_t table) {
+  return (const upb_MiniTable*)(table >> 8);
+}
+
+UPB_INLINE
+const char* decode_isdonefallback_inl(upb_Decoder* d, const char* ptr,
+                                      int overrun, int* status) {
+  if (overrun < d->limit) {
+    /* Need to copy remaining data into patch buffer. */
+    UPB_ASSERT(overrun < 16);
+    if (d->unknown_msg) {
+      if (!_upb_Message_AddUnknown(d->unknown_msg, d->unknown, ptr - d->unknown,
+                                   &d->arena)) {
+        *status = kUpb_DecodeStatus_OutOfMemory;
+        return NULL;
+      }
+      d->unknown = &d->patch[0] + overrun;
+    }
+    memset(d->patch + 16, 0, 16);
+    memcpy(d->patch, d->end, 16);
+    ptr = &d->patch[0] + overrun;
+    d->end = &d->patch[16];
+    d->limit -= 16;
+    d->limit_ptr = d->end + d->limit;
+    d->options &= ~kUpb_DecodeOption_AliasString;
+    UPB_ASSERT(ptr < d->limit_ptr);
+    return ptr;
+  } else {
+    *status = kUpb_DecodeStatus_Malformed;
+    return NULL;
+  }
+}
+
+const char* decode_isdonefallback(upb_Decoder* d, const char* ptr, int overrun);
+
+UPB_INLINE
+bool decode_isdone(upb_Decoder* d, const char** ptr) {
+  int overrun = *ptr - d->end;
+  if (UPB_LIKELY(*ptr < d->limit_ptr)) {
+    return false;
+  } else if (UPB_LIKELY(overrun == d->limit)) {
+    return true;
+  } else {
+    *ptr = decode_isdonefallback(d, *ptr, overrun);
+    return false;
+  }
+}
+
+#if UPB_FASTTABLE
+UPB_INLINE
+const char* fastdecode_tagdispatch(upb_Decoder* d, const char* ptr,
+                                   upb_Message* msg, intptr_t table,
+                                   uint64_t hasbits, uint64_t tag) {
+  const upb_MiniTable* table_p = decode_totablep(table);
+  uint8_t mask = table;
+  uint64_t data;
+  size_t idx = tag & mask;
+  UPB_ASSUME((idx & 7) == 0);
+  idx >>= 3;
+  data = table_p->fasttable[idx].field_data ^ tag;
+  UPB_MUSTTAIL return table_p->fasttable[idx].field_parser(d, ptr, msg, table,
+                                                           hasbits, data);
+}
+#endif
+
+UPB_INLINE uint32_t fastdecode_loadtag(const char* ptr) {
+  uint16_t tag;
+  memcpy(&tag, ptr, 2);
+  return tag;
+}
+
+UPB_INLINE void decode_checklimit(upb_Decoder* d) {
+  UPB_ASSERT(d->limit_ptr == d->end + UPB_MIN(0, d->limit));
+}
+
+UPB_INLINE int decode_pushlimit(upb_Decoder* d, const char* ptr, int size) {
+  int limit = size + (int)(ptr - d->end);
+  int delta = d->limit - limit;
+  decode_checklimit(d);
+  d->limit = limit;
+  d->limit_ptr = d->end + UPB_MIN(0, limit);
+  decode_checklimit(d);
+  return delta;
+}
+
+UPB_INLINE void decode_poplimit(upb_Decoder* d, const char* ptr,
+                                int saved_delta) {
+  UPB_ASSERT(ptr - d->end == d->limit);
+  decode_checklimit(d);
+  d->limit += saved_delta;
+  d->limit_ptr = d->end + UPB_MIN(0, d->limit);
+  decode_checklimit(d);
+}
+
+#include "upb/port_undef.inc"
+
+#endif /* UPB_INTERNAL_DECODE_H_ */
diff --git a/upb/internal/mini_table_accessors.h b/upb/internal/mini_table_accessors.h
new file mode 100644
index 0000000..23f694b
--- /dev/null
+++ b/upb/internal/mini_table_accessors.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009-2022, 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.
+ */
+
+#ifndef UPB_INTERNAL_MINI_TABLE_ACCESSORS_H_
+#define UPB_INTERNAL_MINI_TABLE_ACCESSORS_H_
+
+#include "upb/msg_internal.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UPB_INLINE bool _upb_MiniTable_Field_InOneOf(const upb_MiniTable_Field* field) {
+  return field->presence < 0;
+}
+
+UPB_INLINE void _upb_MiniTable_SetPresence(upb_Message* msg,
+                                           const upb_MiniTable_Field* field) {
+  if (field->presence > 0) {
+    _upb_sethas_field(msg, field);
+  } else if (_upb_MiniTable_Field_InOneOf(field)) {
+    *_upb_oneofcase_field(msg, field) = field->number;
+  }
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif  // UPB_INTERNAL_MINI_TABLE_ACCESSORS_H_
diff --git a/upb/internal/table.h b/upb/internal/table.h
new file mode 100644
index 0000000..bc647be
--- /dev/null
+++ b/upb/internal/table.h
@@ -0,0 +1,385 @@
+/*
+ * 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.
+ */
+
+/*
+ * upb_table
+ *
+ * This header is INTERNAL-ONLY!  Its interfaces are not public or stable!
+ * This file defines very fast int->upb_value (inttable) and string->upb_value
+ * (strtable) hash tables.
+ *
+ * The table uses chained scatter with Brent's variation (inspired by the Lua
+ * implementation of hash tables).  The hash function for strings is Austin
+ * Appleby's "MurmurHash."
+ *
+ * The inttable uses uintptr_t as its key, which guarantees it can be used to
+ * store pointers or integers of at least 32 bits (upb isn't really useful on
+ * systems where sizeof(void*) < 4).
+ *
+ * The table must be homogeneous (all values of the same type).  In debug
+ * mode, we check this on insert and lookup.
+ */
+
+#ifndef UPB_INTERNAL_TABLE_H_
+#define UPB_INTERNAL_TABLE_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "upb/upb.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* upb_value ******************************************************************/
+
+typedef struct {
+  uint64_t val;
+} upb_value;
+
+/* Variant that works with a length-delimited rather than NULL-delimited string,
+ * as supported by strtable. */
+char* upb_strdup2(const char* s, size_t len, upb_Arena* a);
+
+UPB_INLINE void _upb_value_setval(upb_value* v, uint64_t val) { v->val = val; }
+
+/* For each value ctype, define the following set of functions:
+ *
+ * // Get/set an int32 from a upb_value.
+ * int32_t upb_value_getint32(upb_value val);
+ * void upb_value_setint32(upb_value *val, int32_t cval);
+ *
+ * // Construct a new upb_value from an int32.
+ * upb_value upb_value_int32(int32_t val); */
+#define FUNCS(name, membername, type_t, converter, proto_type)       \
+  UPB_INLINE void upb_value_set##name(upb_value* val, type_t cval) { \
+    val->val = (converter)cval;                                      \
+  }                                                                  \
+  UPB_INLINE upb_value upb_value_##name(type_t val) {                \
+    upb_value ret;                                                   \
+    upb_value_set##name(&ret, val);                                  \
+    return ret;                                                      \
+  }                                                                  \
+  UPB_INLINE type_t upb_value_get##name(upb_value val) {             \
+    return (type_t)(converter)val.val;                               \
+  }
+
+FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32)
+FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64)
+FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32)
+FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64)
+FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL)
+FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR)
+FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR)
+FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR)
+
+#undef FUNCS
+
+UPB_INLINE void upb_value_setfloat(upb_value* val, float cval) {
+  memcpy(&val->val, &cval, sizeof(cval));
+}
+
+UPB_INLINE void upb_value_setdouble(upb_value* val, double cval) {
+  memcpy(&val->val, &cval, sizeof(cval));
+}
+
+UPB_INLINE upb_value upb_value_float(float cval) {
+  upb_value ret;
+  upb_value_setfloat(&ret, cval);
+  return ret;
+}
+
+UPB_INLINE upb_value upb_value_double(double cval) {
+  upb_value ret;
+  upb_value_setdouble(&ret, cval);
+  return ret;
+}
+
+#undef SET_TYPE
+
+/* upb_tabkey *****************************************************************/
+
+/* Either:
+ *   1. an actual integer key, or
+ *   2. a pointer to a string prefixed by its uint32_t length, owned by us.
+ *
+ * ...depending on whether this is a string table or an int table.  We would
+ * make this a union of those two types, but C89 doesn't support statically
+ * initializing a non-first union member. */
+typedef uintptr_t upb_tabkey;
+
+UPB_INLINE char* upb_tabstr(upb_tabkey key, uint32_t* len) {
+  char* mem = (char*)key;
+  if (len) memcpy(len, mem, sizeof(*len));
+  return mem + sizeof(*len);
+}
+
+UPB_INLINE upb_StringView upb_tabstrview(upb_tabkey key) {
+  upb_StringView ret;
+  uint32_t len;
+  ret.data = upb_tabstr(key, &len);
+  ret.size = len;
+  return ret;
+}
+
+/* upb_tabval *****************************************************************/
+
+typedef struct upb_tabval {
+  uint64_t val;
+} upb_tabval;
+
+#define UPB_TABVALUE_EMPTY_INIT \
+  { -1 }
+
+/* upb_table ******************************************************************/
+
+typedef struct _upb_tabent {
+  upb_tabkey key;
+  upb_tabval val;
+
+  /* Internal chaining.  This is const so we can create static initializers for
+   * tables.  We cast away const sometimes, but *only* when the containing
+   * upb_table is known to be non-const.  This requires a bit of care, but
+   * the subtlety is confined to table.c. */
+  const struct _upb_tabent* next;
+} upb_tabent;
+
+typedef struct {
+  size_t count;       /* Number of entries in the hash part. */
+  uint32_t mask;      /* Mask to turn hash value -> bucket. */
+  uint32_t max_count; /* Max count before we hit our load limit. */
+  uint8_t size_lg2;   /* Size of the hashtable part is 2^size_lg2 entries. */
+  upb_tabent* entries;
+} upb_table;
+
+typedef struct {
+  upb_table t;
+} upb_strtable;
+
+typedef struct {
+  upb_table t;             /* For entries that don't fit in the array part. */
+  const upb_tabval* array; /* Array part of the table. See const note above. */
+  size_t array_size;       /* Array part size. */
+  size_t array_count;      /* Array part number of elements. */
+} upb_inttable;
+
+UPB_INLINE size_t upb_table_size(const upb_table* t) {
+  if (t->size_lg2 == 0)
+    return 0;
+  else
+    return 1 << t->size_lg2;
+}
+
+/* Internal-only functions, in .h file only out of necessity. */
+UPB_INLINE bool upb_tabent_isempty(const upb_tabent* e) { return e->key == 0; }
+
+/* Initialize and uninitialize a table, respectively.  If memory allocation
+ * failed, false is returned that the table is uninitialized. */
+bool upb_inttable_init(upb_inttable* table, upb_Arena* a);
+bool upb_strtable_init(upb_strtable* table, size_t expected_size, upb_Arena* a);
+
+/* Returns the number of values in the table. */
+size_t upb_inttable_count(const upb_inttable* t);
+UPB_INLINE size_t upb_strtable_count(const upb_strtable* t) {
+  return t->t.count;
+}
+
+void upb_strtable_clear(upb_strtable* t);
+
+/* Inserts the given key into the hashtable with the given value.  The key must
+ * not already exist in the hash table.  For strtables, the key is not required
+ * to be NULL-terminated, and the table will make an internal copy of the key.
+ * Inttables must not insert a value of UINTPTR_MAX.
+ *
+ * If a table resize was required but memory allocation failed, false is
+ * returned and the table is unchanged. */
+bool upb_inttable_insert(upb_inttable* t, uintptr_t key, upb_value val,
+                         upb_Arena* a);
+bool upb_strtable_insert(upb_strtable* t, const char* key, size_t len,
+                         upb_value val, upb_Arena* a);
+
+/* Looks up key in this table, returning "true" if the key was found.
+ * If v is non-NULL, copies the value for this key into *v. */
+bool upb_inttable_lookup(const upb_inttable* t, uintptr_t key, upb_value* v);
+bool upb_strtable_lookup2(const upb_strtable* t, const char* key, size_t len,
+                          upb_value* v);
+
+/* For NULL-terminated strings. */
+UPB_INLINE bool upb_strtable_lookup(const upb_strtable* t, const char* key,
+                                    upb_value* v) {
+  return upb_strtable_lookup2(t, key, strlen(key), v);
+}
+
+/* Removes an item from the table.  Returns true if the remove was successful,
+ * and stores the removed item in *val if non-NULL. */
+bool upb_inttable_remove(upb_inttable* t, uintptr_t key, upb_value* val);
+bool upb_strtable_remove2(upb_strtable* t, const char* key, size_t len,
+                          upb_value* val);
+
+UPB_INLINE bool upb_strtable_remove(upb_strtable* t, const char* key,
+                                    upb_value* v) {
+  return upb_strtable_remove2(t, key, strlen(key), v);
+}
+
+/* Updates an existing entry in an inttable.  If the entry does not exist,
+ * returns false and does nothing.  Unlike insert/remove, this does not
+ * invalidate iterators. */
+bool upb_inttable_replace(upb_inttable* t, uintptr_t key, upb_value val);
+
+/* Optimizes the table for the current set of entries, for both memory use and
+ * lookup time.  Client should call this after all entries have been inserted;
+ * inserting more entries is legal, but will likely require a table resize. */
+void upb_inttable_compact(upb_inttable* t, upb_Arena* a);
+
+/* Exposed for testing only. */
+bool upb_strtable_resize(upb_strtable* t, size_t size_lg2, upb_Arena* a);
+
+/* Iterators ******************************************************************/
+
+/* Iteration over inttable.
+ *
+ *   intptr_t iter = UPB_INTTABLE_BEGIN;
+ *   uintptr_t key;
+ *   upb_value val;
+ *   while (upb_inttable_next2(t, &key, &val, &iter)) {
+ *      // ...
+ *   }
+ */
+
+#define UPB_INTTABLE_BEGIN -1
+
+bool upb_inttable_next2(const upb_inttable* t, uintptr_t* key, upb_value* val,
+                        intptr_t* iter);
+void upb_inttable_removeiter(upb_inttable* t, intptr_t* iter);
+
+/* Iteration over strtable.
+ *
+ *   intptr_t iter = UPB_INTTABLE_BEGIN;
+ *   upb_StringView key;
+ *   upb_value val;
+ *   while (upb_strtable_next2(t, &key, &val, &iter)) {
+ *      // ...
+ *   }
+ */
+
+#define UPB_STRTABLE_BEGIN -1
+
+bool upb_strtable_next2(const upb_strtable* t, upb_StringView* key,
+                        upb_value* val, intptr_t* iter);
+void upb_strtable_removeiter(upb_strtable* t, intptr_t* iter);
+
+/* DEPRECATED iterators, slated for removal.
+ *
+ * Iterators for int and string tables.  We are subject to some kind of unusual
+ * design constraints:
+ *
+ * For high-level languages:
+ *  - we must be able to guarantee that we don't crash or corrupt memory even if
+ *    the program accesses an invalidated iterator.
+ *
+ * For C++11 range-based for:
+ *  - iterators must be copyable
+ *  - iterators must be comparable
+ *  - it must be possible to construct an "end" value.
+ *
+ * Iteration order is undefined.
+ *
+ * Modifying the table invalidates iterators.  upb_{str,int}table_done() is
+ * guaranteed to work even on an invalidated iterator, as long as the table it
+ * is iterating over has not been freed.  Calling next() or accessing data from
+ * an invalidated iterator yields unspecified elements from the table, but it is
+ * guaranteed not to crash and to return real table elements (except when done()
+ * is true). */
+
+/* upb_strtable_iter **********************************************************/
+
+/*   upb_strtable_iter i;
+ *   upb_strtable_begin(&i, t);
+ *   for(; !upb_strtable_done(&i); upb_strtable_next(&i)) {
+ *     const char *key = upb_strtable_iter_key(&i);
+ *     const upb_value val = upb_strtable_iter_value(&i);
+ *     // ...
+ *   }
+ */
+
+typedef struct {
+  const upb_strtable* t;
+  size_t index;
+} upb_strtable_iter;
+
+void upb_strtable_begin(upb_strtable_iter* i, const upb_strtable* t);
+void upb_strtable_next(upb_strtable_iter* i);
+bool upb_strtable_done(const upb_strtable_iter* i);
+upb_StringView upb_strtable_iter_key(const upb_strtable_iter* i);
+upb_value upb_strtable_iter_value(const upb_strtable_iter* i);
+void upb_strtable_iter_setdone(upb_strtable_iter* i);
+bool upb_strtable_iter_isequal(const upb_strtable_iter* i1,
+                               const upb_strtable_iter* i2);
+
+/* upb_inttable_iter **********************************************************/
+
+/*   upb_inttable_iter i;
+ *   upb_inttable_begin(&i, t);
+ *   for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+ *     uintptr_t key = upb_inttable_iter_key(&i);
+ *     upb_value val = upb_inttable_iter_value(&i);
+ *     // ...
+ *   }
+ */
+
+typedef struct {
+  const upb_inttable* t;
+  size_t index;
+  bool array_part;
+} upb_inttable_iter;
+
+UPB_INLINE const upb_tabent* str_tabent(const upb_strtable_iter* i) {
+  return &i->t->t.entries[i->index];
+}
+
+void upb_inttable_begin(upb_inttable_iter* i, const upb_inttable* t);
+void upb_inttable_next(upb_inttable_iter* i);
+bool upb_inttable_done(const upb_inttable_iter* i);
+uintptr_t upb_inttable_iter_key(const upb_inttable_iter* i);
+upb_value upb_inttable_iter_value(const upb_inttable_iter* i);
+void upb_inttable_iter_setdone(upb_inttable_iter* i);
+bool upb_inttable_iter_isequal(const upb_inttable_iter* i1,
+                               const upb_inttable_iter* i2);
+
+uint32_t _upb_Hash(const void* p, size_t n, uint64_t seed);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif /* UPB_INTERNAL_TABLE_H_ */
diff --git a/upb/internal/upb.h b/upb/internal/upb.h
new file mode 100644
index 0000000..cdc8bfc
--- /dev/null
+++ b/upb/internal/upb.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_INTERNAL_UPB_H_
+#define UPB_INTERNAL_UPB_H_
+
+#include "upb/upb.h"
+
+struct mem_block;
+typedef struct mem_block mem_block;
+
+struct upb_Arena {
+  _upb_ArenaHead head;
+  /* Stores cleanup metadata for this arena.
+   * - a pointer to the current cleanup counter.
+   * - a boolean indicating if there is an unowned initial block.  */
+  uintptr_t cleanup_metadata;
+
+  /* Allocator to allocate arena blocks.  We are responsible for freeing these
+   * when we are destroyed. */
+  upb_alloc* block_alloc;
+  uint32_t last_size;
+
+  /* When multiple arenas are fused together, each arena points to a parent
+   * arena (root points to itself). The root tracks how many live arenas
+   * reference it. */
+  uint32_t refcount; /* Only used when a->parent == a */
+  struct upb_Arena* parent;
+
+  /* Linked list of blocks to free/cleanup. */
+  mem_block *freelist, *freelist_tail;
+};
+
+// Encodes a float or double that is round-trippable, but as short as possible.
+// These routines are not fully optimal (not guaranteed to be shortest), but are
+// short-ish and match the implementation that has been used in protobuf since
+// the beginning.
+//
+// The given buffer size must be at least kUpb_RoundTripBufferSize.
+enum { kUpb_RoundTripBufferSize = 32 };
+void _upb_EncodeRoundTripDouble(double val, char* buf, size_t size);
+void _upb_EncodeRoundTripFloat(float val, char* buf, size_t size);
+
+#endif /* UPB_INTERNAL_UPB_H_ */
diff --git a/upb/json_encode.c b/upb/json_encode.c
index 81c0501..e13c0f7 100644
--- a/upb/json_encode.c
+++ b/upb/json_encode.c
@@ -37,9 +37,9 @@
 #include <string.h>
 
 #include "upb/decode.h"
+#include "upb/internal/upb.h"
 #include "upb/internal/vsnprintf_compat.h"
 #include "upb/reflection.h"
-#include "upb/upb_internal.h"
 
 /* Must be last. */
 #include "upb/port_def.inc"
diff --git a/upb/collections.c b/upb/map.c
similarity index 60%
rename from upb/collections.c
rename to upb/map.c
index 6bf7fbb..d92776f 100644
--- a/upb/collections.c
+++ b/upb/map.c
@@ -25,13 +25,13 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "upb/collections.h"
+#include "upb/map.h"
 
 #include <string.h>
 
+#include "upb/internal/table.h"
 #include "upb/msg.h"
 #include "upb/port_def.inc"
-#include "upb/table_internal.h"
 
 /* Strings/bytes are special-cased in maps. */
 static char _upb_CTypeo_mapsize[12] = {
@@ -49,90 +49,6 @@
     0,             /* kUpb_CType_Bytes */
 };
 
-static const char _upb_CTypeo_sizelg2[12] = {
-    0,
-    0,              /* kUpb_CType_Bool */
-    2,              /* kUpb_CType_Float */
-    2,              /* kUpb_CType_Int32 */
-    2,              /* kUpb_CType_UInt32 */
-    2,              /* kUpb_CType_Enum */
-    UPB_SIZE(2, 3), /* kUpb_CType_Message */
-    3,              /* kUpb_CType_Double */
-    3,              /* kUpb_CType_Int64 */
-    3,              /* kUpb_CType_UInt64 */
-    UPB_SIZE(3, 4), /* kUpb_CType_String */
-    UPB_SIZE(3, 4), /* kUpb_CType_Bytes */
-};
-
-/** upb_Array *****************************************************************/
-
-upb_Array* upb_Array_New(upb_Arena* a, upb_CType type) {
-  return _upb_Array_New(a, 4, _upb_CTypeo_sizelg2[type]);
-}
-
-size_t upb_Array_Size(const upb_Array* arr) { return arr->len; }
-
-upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i) {
-  upb_MessageValue ret;
-  const char* data = _upb_array_constptr(arr);
-  int lg2 = arr->data & 7;
-  UPB_ASSERT(i < arr->len);
-  memcpy(&ret, data + (i << lg2), 1 << lg2);
-  return ret;
-}
-
-void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val) {
-  char* data = _upb_array_ptr(arr);
-  int lg2 = arr->data & 7;
-  UPB_ASSERT(i < arr->len);
-  memcpy(data + (i << lg2), &val, 1 << lg2);
-}
-
-bool upb_Array_Append(upb_Array* arr, upb_MessageValue val, upb_Arena* arena) {
-  if (!upb_Array_Resize(arr, arr->len + 1, arena)) {
-    return false;
-  }
-  upb_Array_Set(arr, arr->len - 1, val);
-  return true;
-}
-
-void upb_Array_Move(upb_Array* arr, size_t dst_idx, size_t src_idx,
-                    size_t count) {
-  char* data = _upb_array_ptr(arr);
-  int lg2 = arr->data & 7;
-  memmove(&data[dst_idx << lg2], &data[src_idx << lg2], count << lg2);
-}
-
-bool upb_Array_Insert(upb_Array* arr, size_t i, size_t count,
-                      upb_Arena* arena) {
-  UPB_ASSERT(i <= arr->len);
-  UPB_ASSERT(count + arr->len >= count);
-  size_t oldsize = arr->len;
-  if (!upb_Array_Resize(arr, arr->len + count, arena)) {
-    return false;
-  }
-  upb_Array_Move(arr, i + count, i, oldsize - i);
-  return true;
-}
-
-/*
- *              i        end      arr->len
- * |------------|XXXXXXXX|--------|
- */
-void upb_Array_Delete(upb_Array* arr, size_t i, size_t count) {
-  size_t end = i + count;
-  UPB_ASSERT(i <= end);
-  UPB_ASSERT(end <= arr->len);
-  upb_Array_Move(arr, i, end, arr->len - end);
-  arr->len -= count;
-}
-
-bool upb_Array_Resize(upb_Array* arr, size_t size, upb_Arena* arena) {
-  return _upb_Array_Resize(arr, size, arena);
-}
-
-/** upb_Map *******************************************************************/
-
 upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type) {
   return _upb_Map_New(a, _upb_CTypeo_mapsize[key_type],
                       _upb_CTypeo_mapsize[value_type]);
diff --git a/upb/map.h b/upb/map.h
new file mode 100644
index 0000000..8ee0ddd
--- /dev/null
+++ b/upb/map.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_MAP_H_
+#define UPB_MAP_H_
+
+#include "google/protobuf/descriptor.upb.h"
+#include "upb/message_value.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Creates a new map on the given arena with the given key/value size. */
+upb_Map* upb_Map_New(upb_Arena* a, upb_CType key_type, upb_CType value_type);
+
+/* Returns the number of entries in the map. */
+size_t upb_Map_Size(const upb_Map* map);
+
+/* Stores a value for the given key into |*val| (or the zero value if the key is
+ * not present).  Returns whether the key was present.  The |val| pointer may be
+ * NULL, in which case the function tests whether the given key is present.  */
+bool upb_Map_Get(const upb_Map* map, upb_MessageValue key,
+                 upb_MessageValue* val);
+
+/* Removes all entries in the map. */
+void upb_Map_Clear(upb_Map* map);
+
+typedef enum {
+  // LINT.IfChange
+  kUpb_MapInsertStatus_Inserted = 0,
+  kUpb_MapInsertStatus_Replaced = 1,
+  kUpb_MapInsertStatus_OutOfMemory = 2,
+  // LINT.ThenChange(//depot/google3/third_party/upb/upb/msg_internal.h)
+} upb_MapInsertStatus;
+
+/* Sets the given key to the given value, returning whether the key was inserted
+ * or replaced.  If the key was inserted, then any existing iterators will be
+ * invalidated. */
+upb_MapInsertStatus upb_Map_Insert(upb_Map* map, upb_MessageValue key,
+                                   upb_MessageValue val, upb_Arena* arena);
+
+/* Sets the given key to the given value.  Returns false if memory allocation
+ * failed. If the key is newly inserted, then any existing iterators will be
+ * invalidated. */
+UPB_INLINE bool upb_Map_Set(upb_Map* map, upb_MessageValue key,
+                            upb_MessageValue val, upb_Arena* arena) {
+  return upb_Map_Insert(map, key, val, arena) !=
+         kUpb_MapInsertStatus_OutOfMemory;
+}
+
+/* Deletes this key from the table.  Returns true if the key was present. */
+bool upb_Map_Delete(upb_Map* map, upb_MessageValue key);
+
+/* Map iteration:
+ *
+ * size_t iter = kUpb_Map_Begin;
+ * while (upb_MapIterator_Next(map, &iter)) {
+ *   upb_MessageValue key = upb_MapIterator_Key(map, iter);
+ *   upb_MessageValue val = upb_MapIterator_Value(map, iter);
+ *
+ *   // If mutating is desired.
+ *   upb_MapIterator_SetValue(map, iter, value2);
+ * }
+ */
+
+/* Advances to the next entry.  Returns false if no more entries are present. */
+bool upb_MapIterator_Next(const upb_Map* map, size_t* iter);
+
+/* Returns true if the iterator still points to a valid entry, or false if the
+ * iterator is past the last element. It is an error to call this function with
+ * kUpb_Map_Begin (you must call next() at least once first). */
+bool upb_MapIterator_Done(const upb_Map* map, size_t iter);
+
+/* Returns the key and value for this entry of the map. */
+upb_MessageValue upb_MapIterator_Key(const upb_Map* map, size_t iter);
+upb_MessageValue upb_MapIterator_Value(const upb_Map* map, size_t iter);
+
+/* Sets the value for this entry.  The iterator must not be done, and the
+ * iterator must not have been initialized const. */
+void upb_MapIterator_SetValue(upb_Map* map, size_t iter,
+                              upb_MessageValue value);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif /* UPB_MAP_H_ */
diff --git a/upb/message_value.h b/upb/message_value.h
new file mode 100644
index 0000000..a08b90f
--- /dev/null
+++ b/upb/message_value.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_MESSAGE_VALUE_H_
+#define UPB_MESSAGE_VALUE_H_
+
+#include "google/protobuf/descriptor.upb.h"
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Definitions commn to both upb_Array and upb_Map.
+
+typedef union {
+  bool bool_val;
+  float float_val;
+  double double_val;
+  int32_t int32_val;
+  int64_t int64_val;
+  uint32_t uint32_val;
+  uint64_t uint64_val;
+  const upb_Map* map_val;
+  const upb_Message* msg_val;
+  const upb_Array* array_val;
+  upb_StringView str_val;
+} upb_MessageValue;
+
+typedef union {
+  upb_Map* map;
+  upb_Message* msg;
+  upb_Array* array;
+} upb_MutableMessageValue;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#include "upb/port_undef.inc"
+
+#endif /* UPB_MESSAGE_VALUE_H_ */
diff --git a/upb/mini_table_accessors.h b/upb/mini_table_accessors.h
index e80b20e..750f66f 100644
--- a/upb/mini_table_accessors.h
+++ b/upb/mini_table_accessors.h
@@ -28,8 +28,8 @@
 #ifndef UPB_MINI_TABLE_ACCESSORS_H_
 #define UPB_MINI_TABLE_ACCESSORS_H_
 
-#include "upb/collections.h"
-#include "upb/mini_table_accessors_internal.h"
+#include "upb/array.h"
+#include "upb/internal/mini_table_accessors.h"
 #include "upb/msg_internal.h"
 
 // Must be last.
diff --git a/upb/mini_table_accessors_internal.h b/upb/mini_table_accessors_internal.h
index a2cfa44..8368b65 100644
--- a/upb/mini_table_accessors_internal.h
+++ b/upb/mini_table_accessors_internal.h
@@ -28,32 +28,9 @@
 #ifndef UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_
 #define UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_
 
-#include "upb/msg_internal.h"
+// TODO(b/232091617): Delete this entire header which currently exists only for
+// temporary backwards compatibility.
 
-// Must be last.
-#include "upb/port_def.inc"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-UPB_INLINE bool _upb_MiniTable_Field_InOneOf(const upb_MiniTable_Field* field) {
-  return field->presence < 0;
-}
-
-UPB_INLINE void _upb_MiniTable_SetPresence(upb_Message* msg,
-                                           const upb_MiniTable_Field* field) {
-  if (field->presence > 0) {
-    _upb_sethas_field(msg, field);
-  } else if (_upb_MiniTable_Field_InOneOf(field)) {
-    *_upb_oneofcase_field(msg, field) = field->number;
-  }
-}
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#include "upb/port_undef.inc"
+#include "upb/internal/mini_table_accessors.h"
 
 #endif  // UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_
diff --git a/upb/mini_table_accessors_test.cc b/upb/mini_table_accessors_test.cc
index 7758e19..1627447 100644
--- a/upb/mini_table_accessors_test.cc
+++ b/upb/mini_table_accessors_test.cc
@@ -36,7 +36,7 @@
 #include "gtest/gtest.h"
 #include "google/protobuf/test_messages_proto2.upb.h"
 #include "google/protobuf/test_messages_proto3.upb.h"
-#include "upb/collections.h"
+#include "upb/array.h"
 #include "upb/mini_table.h"
 #include "upb/test.upb.h"
 
diff --git a/upb/msg.c b/upb/msg.c
index 46738ca..036c621 100644
--- a/upb/msg.c
+++ b/upb/msg.c
@@ -27,9 +27,9 @@
 
 #include "upb/msg.h"
 
+#include "upb/internal/table.h"
 #include "upb/msg_internal.h"
 #include "upb/port_def.inc"
-#include "upb/table_internal.h"
 
 /** upb_Message ***************************************************************/
 
@@ -366,63 +366,3 @@
   qsort(&s->entries[sorted->start], map_size, sizeof(*s->entries), compar);
   return true;
 }
-
-/** upb_ExtensionRegistry *****************************************************/
-
-struct upb_ExtensionRegistry {
-  upb_Arena* arena;
-  upb_strtable exts; /* Key is upb_MiniTable* concatenated with fieldnum. */
-};
-
-#define EXTREG_KEY_SIZE (sizeof(upb_MiniTable*) + sizeof(uint32_t))
-
-static void extreg_key(char* buf, const upb_MiniTable* l, uint32_t fieldnum) {
-  memcpy(buf, &l, sizeof(l));
-  memcpy(buf + sizeof(l), &fieldnum, sizeof(fieldnum));
-}
-
-upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena) {
-  upb_ExtensionRegistry* r = upb_Arena_Malloc(arena, sizeof(*r));
-  if (!r) return NULL;
-  r->arena = arena;
-  if (!upb_strtable_init(&r->exts, 8, arena)) return NULL;
-  return r;
-}
-
-bool _upb_extreg_add(upb_ExtensionRegistry* r,
-                     const upb_MiniTable_Extension** e, size_t count) {
-  char buf[EXTREG_KEY_SIZE];
-  const upb_MiniTable_Extension** start = e;
-  const upb_MiniTable_Extension** end = UPB_PTRADD(e, count);
-  for (; e < end; e++) {
-    const upb_MiniTable_Extension* ext = *e;
-    extreg_key(buf, ext->extendee, ext->field.number);
-    if (!upb_strtable_insert(&r->exts, buf, EXTREG_KEY_SIZE,
-                             upb_value_constptr(ext), r->arena)) {
-      goto failure;
-    }
-  }
-  return true;
-
-failure:
-  /* Back out the entries previously added. */
-  for (end = e, e = start; e < end; e++) {
-    const upb_MiniTable_Extension* ext = *e;
-    extreg_key(buf, ext->extendee, ext->field.number);
-    upb_strtable_remove2(&r->exts, buf, EXTREG_KEY_SIZE, NULL);
-  }
-  return false;
-}
-
-const upb_MiniTable_Extension* _upb_extreg_get(const upb_ExtensionRegistry* r,
-                                               const upb_MiniTable* l,
-                                               uint32_t num) {
-  char buf[EXTREG_KEY_SIZE];
-  upb_value v;
-  extreg_key(buf, l, num);
-  if (upb_strtable_lookup2(&r->exts, buf, EXTREG_KEY_SIZE, &v)) {
-    return upb_value_getconstptr(v);
-  } else {
-    return NULL;
-  }
-}
diff --git a/upb/msg.h b/upb/msg.h
index c984b13..5c1e8e8 100644
--- a/upb/msg.h
+++ b/upb/msg.h
@@ -38,14 +38,14 @@
 
 #include <stddef.h>
 
+// TODO(b/232091617): Remove this and fix everything that breaks as a result.
+#include "upb/extension_registry.h"
 #include "upb/upb.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/** upb_Message ***************************************************************/
-
 typedef void upb_Message;
 
 /* For users these are opaque. They can be obtained from
@@ -64,49 +64,6 @@
 /* Returns the number of extensions present in this message. */
 size_t upb_Message_ExtensionCount(const upb_Message* msg);
 
-/** upb_ExtensionRegistry *****************************************************/
-
-/* Extension registry: a dynamic data structure that stores a map of:
- *   (upb_MiniTable, number) -> extension info
- *
- * upb_decode() uses upb_ExtensionRegistry to look up extensions while parsing
- * binary format.
- *
- * upb_ExtensionRegistry is part of the mini-table (msglayout) family of
- * objects. Like all mini-table objects, it is suitable for reflection-less
- * builds that do not want to expose names into the binary.
- *
- * Unlike most mini-table types, upb_ExtensionRegistry requires dynamic memory
- * allocation and dynamic initialization:
- * * If reflection is being used, then upb_DefPool will construct an appropriate
- *   upb_ExtensionRegistry automatically.
- * * For a mini-table only build, the user must manually construct the
- *   upb_ExtensionRegistry and populate it with all of the extensions the user
- * cares about.
- * * A third alternative is to manually unpack relevant extensions after the
- *   main parse is complete, similar to how Any works. This is perhaps the
- *   nicest solution from the perspective of reducing dependencies, avoiding
- *   dynamic memory allocation, and avoiding the need to parse uninteresting
- *   extensions.  The downsides are:
- *     (1) parse errors are not caught during the main parse
- *     (2) the CPU hit of parsing comes during access, which could cause an
- *         undesirable stutter in application performance.
- *
- * Users cannot directly get or put into this map. Users can only add the
- * extensions from a generated module and pass the extension registry to the
- * binary decoder.
- *
- * A upb_DefPool provides a upb_ExtensionRegistry, so any users who use
- * reflection do not need to populate a upb_ExtensionRegistry directly.
- */
-
-struct upb_ExtensionRegistry;
-typedef struct upb_ExtensionRegistry upb_ExtensionRegistry;
-
-/* Creates a upb_ExtensionRegistry in the given arena.  The arena must outlive
- * any use of the extreg. */
-upb_ExtensionRegistry* upb_ExtensionRegistry_New(upb_Arena* arena);
-
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/upb/msg_internal.h b/upb/msg_internal.h
index 62ef185..4f5c479 100644
--- a/upb/msg_internal.h
+++ b/upb/msg_internal.h
@@ -39,8 +39,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "upb/extension_registry.h"
+#include "upb/internal/table.h"
 #include "upb/msg.h"
-#include "upb/table_internal.h"
 #include "upb/upb.h"
 
 /* Must be last. */
@@ -672,7 +673,7 @@
   _kUpb_MapInsertStatus_Inserted = 0,
   _kUpb_MapInsertStatus_Replaced = 1,
   _kUpb_MapInsertStatus_OutOfMemory = 2,
-  // LINT.ThenChange(//depot/google3/third_party/upb/upb/collections.h)
+  // LINT.ThenChange(//depot/google3/third_party/upb/upb/map.h)
 } _upb_MapInsertStatus;
 
 UPB_INLINE _upb_MapInsertStatus _upb_Map_Insert(upb_Map* map, const void* key,
diff --git a/upb/reflection.c b/upb/reflection.c
index 0c92248..43f8844 100644
--- a/upb/reflection.c
+++ b/upb/reflection.c
@@ -29,9 +29,9 @@
 
 #include <string.h>
 
+#include "upb/internal/table.h"
 #include "upb/msg.h"
 #include "upb/port_def.inc"
-#include "upb/table_internal.h"
 
 static size_t get_field_size(const upb_MiniTable_Field* f) {
   static unsigned char sizes[] = {
diff --git a/upb/reflection.h b/upb/reflection.h
index acae9ab..6071fba 100644
--- a/upb/reflection.h
+++ b/upb/reflection.h
@@ -28,8 +28,9 @@
 #ifndef UPB_REFLECTION_H_
 #define UPB_REFLECTION_H_
 
-#include "upb/collections.h"
+#include "upb/array.h"
 #include "upb/def.h"
+#include "upb/map.h"
 #include "upb/msg.h"
 #include "upb/port_def.inc"
 #include "upb/upb.h"
diff --git a/upb/status.c b/upb/status.c
new file mode 100644
index 0000000..c00b9c8
--- /dev/null
+++ b/upb/status.c
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#include "upb/status.h"
+
+#include <errno.h>
+#include <float.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "upb/internal/upb.h"
+
+// Must be last.
+#include "upb/port_def.inc"
+
+void upb_Status_Clear(upb_Status* status) {
+  if (!status) return;
+  status->ok = true;
+  status->msg[0] = '\0';
+}
+
+bool upb_Status_IsOk(const upb_Status* status) { return status->ok; }
+
+const char* upb_Status_ErrorMessage(const upb_Status* status) {
+  return status->msg;
+}
+
+void upb_Status_SetErrorMessage(upb_Status* status, const char* msg) {
+  if (!status) return;
+  status->ok = false;
+  strncpy(status->msg, msg, _kUpb_Status_MaxMessage - 1);
+  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
+}
+
+void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  upb_Status_VSetErrorFormat(status, fmt, args);
+  va_end(args);
+}
+
+void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt,
+                                va_list args) {
+  if (!status) return;
+  status->ok = false;
+  vsnprintf(status->msg, sizeof(status->msg), fmt, args);
+  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
+}
+
+void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt,
+                                   va_list args) {
+  size_t len;
+  if (!status) return;
+  status->ok = false;
+  len = strlen(status->msg);
+  vsnprintf(status->msg + len, sizeof(status->msg) - len, fmt, args);
+  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
+}
diff --git a/upb/status.h b/upb/status.h
new file mode 100644
index 0000000..2c5c2a7
--- /dev/null
+++ b/upb/status.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef UPB_STATUS_H_
+#define UPB_STATUS_H_
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "upb/port_def.inc"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _kUpb_Status_MaxMessage 127
+
+typedef struct {
+  bool ok;
+  char msg[_kUpb_Status_MaxMessage]; /* Error message; NULL-terminated. */
+} upb_Status;
+
+const char* upb_Status_ErrorMessage(const upb_Status* status);
+bool upb_Status_IsOk(const upb_Status* status);
+
+/* These are no-op if |status| is NULL. */
+void upb_Status_Clear(upb_Status* status);
+void upb_Status_SetErrorMessage(upb_Status* status, const char* msg);
+void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...)
+    UPB_PRINTF(2, 3);
+void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt,
+                                va_list args) UPB_PRINTF(2, 0);
+void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt,
+                                   va_list args) UPB_PRINTF(2, 0);
+
+#include "upb/port_undef.inc"
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* UPB_STATUS_H_ */
diff --git a/upb/table.c b/upb/table.c
index 941f258..e8e55f7 100644
--- a/upb/table.c
+++ b/upb/table.c
@@ -31,9 +31,9 @@
  * Implementation is heavily inspired by Lua's ltable.c.
  */
 
-#include <string.h>
+#include "upb/internal/table.h"
 
-#include "upb/table_internal.h"
+#include <string.h>
 
 /* Must be last. */
 #include "upb/port_def.inc"
diff --git a/upb/table_internal.h b/upb/table_internal.h
index 1e77470..7ac8bf7 100644
--- a/upb/table_internal.h
+++ b/upb/table_internal.h
@@ -25,361 +25,12 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/*
- * upb_table
- *
- * This header is INTERNAL-ONLY!  Its interfaces are not public or stable!
- * This file defines very fast int->upb_value (inttable) and string->upb_value
- * (strtable) hash tables.
- *
- * The table uses chained scatter with Brent's variation (inspired by the Lua
- * implementation of hash tables).  The hash function for strings is Austin
- * Appleby's "MurmurHash."
- *
- * The inttable uses uintptr_t as its key, which guarantees it can be used to
- * store pointers or integers of at least 32 bits (upb isn't really useful on
- * systems where sizeof(void*) < 4).
- *
- * The table must be homogeneous (all values of the same type).  In debug
- * mode, we check this on insert and lookup.
- */
-
 #ifndef UPB_TABLE_H_
 #define UPB_TABLE_H_
 
-#include <stdint.h>
-#include <string.h>
+// TODO(b/232091617): Delete this entire header which currently exists only for
+// temporary backwards compatibility.
 
-#include "upb/upb.h"
-
-// Must be last.
-#include "upb/port_def.inc"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* upb_value ******************************************************************/
-
-typedef struct {
-  uint64_t val;
-} upb_value;
-
-/* Variant that works with a length-delimited rather than NULL-delimited string,
- * as supported by strtable. */
-char* upb_strdup2(const char* s, size_t len, upb_Arena* a);
-
-UPB_INLINE void _upb_value_setval(upb_value* v, uint64_t val) { v->val = val; }
-
-/* For each value ctype, define the following set of functions:
- *
- * // Get/set an int32 from a upb_value.
- * int32_t upb_value_getint32(upb_value val);
- * void upb_value_setint32(upb_value *val, int32_t cval);
- *
- * // Construct a new upb_value from an int32.
- * upb_value upb_value_int32(int32_t val); */
-#define FUNCS(name, membername, type_t, converter, proto_type)       \
-  UPB_INLINE void upb_value_set##name(upb_value* val, type_t cval) { \
-    val->val = (converter)cval;                                      \
-  }                                                                  \
-  UPB_INLINE upb_value upb_value_##name(type_t val) {                \
-    upb_value ret;                                                   \
-    upb_value_set##name(&ret, val);                                  \
-    return ret;                                                      \
-  }                                                                  \
-  UPB_INLINE type_t upb_value_get##name(upb_value val) {             \
-    return (type_t)(converter)val.val;                               \
-  }
-
-FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32)
-FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64)
-FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32)
-FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64)
-FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL)
-FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR)
-FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR)
-FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR)
-
-#undef FUNCS
-
-UPB_INLINE void upb_value_setfloat(upb_value* val, float cval) {
-  memcpy(&val->val, &cval, sizeof(cval));
-}
-
-UPB_INLINE void upb_value_setdouble(upb_value* val, double cval) {
-  memcpy(&val->val, &cval, sizeof(cval));
-}
-
-UPB_INLINE upb_value upb_value_float(float cval) {
-  upb_value ret;
-  upb_value_setfloat(&ret, cval);
-  return ret;
-}
-
-UPB_INLINE upb_value upb_value_double(double cval) {
-  upb_value ret;
-  upb_value_setdouble(&ret, cval);
-  return ret;
-}
-
-#undef SET_TYPE
-
-/* upb_tabkey *****************************************************************/
-
-/* Either:
- *   1. an actual integer key, or
- *   2. a pointer to a string prefixed by its uint32_t length, owned by us.
- *
- * ...depending on whether this is a string table or an int table.  We would
- * make this a union of those two types, but C89 doesn't support statically
- * initializing a non-first union member. */
-typedef uintptr_t upb_tabkey;
-
-UPB_INLINE char* upb_tabstr(upb_tabkey key, uint32_t* len) {
-  char* mem = (char*)key;
-  if (len) memcpy(len, mem, sizeof(*len));
-  return mem + sizeof(*len);
-}
-
-UPB_INLINE upb_StringView upb_tabstrview(upb_tabkey key) {
-  upb_StringView ret;
-  uint32_t len;
-  ret.data = upb_tabstr(key, &len);
-  ret.size = len;
-  return ret;
-}
-
-/* upb_tabval *****************************************************************/
-
-typedef struct upb_tabval {
-  uint64_t val;
-} upb_tabval;
-
-#define UPB_TABVALUE_EMPTY_INIT \
-  { -1 }
-
-/* upb_table ******************************************************************/
-
-typedef struct _upb_tabent {
-  upb_tabkey key;
-  upb_tabval val;
-
-  /* Internal chaining.  This is const so we can create static initializers for
-   * tables.  We cast away const sometimes, but *only* when the containing
-   * upb_table is known to be non-const.  This requires a bit of care, but
-   * the subtlety is confined to table.c. */
-  const struct _upb_tabent* next;
-} upb_tabent;
-
-typedef struct {
-  size_t count;       /* Number of entries in the hash part. */
-  uint32_t mask;      /* Mask to turn hash value -> bucket. */
-  uint32_t max_count; /* Max count before we hit our load limit. */
-  uint8_t size_lg2;   /* Size of the hashtable part is 2^size_lg2 entries. */
-  upb_tabent* entries;
-} upb_table;
-
-typedef struct {
-  upb_table t;
-} upb_strtable;
-
-typedef struct {
-  upb_table t;             /* For entries that don't fit in the array part. */
-  const upb_tabval* array; /* Array part of the table. See const note above. */
-  size_t array_size;       /* Array part size. */
-  size_t array_count;      /* Array part number of elements. */
-} upb_inttable;
-
-UPB_INLINE size_t upb_table_size(const upb_table* t) {
-  if (t->size_lg2 == 0)
-    return 0;
-  else
-    return 1 << t->size_lg2;
-}
-
-/* Internal-only functions, in .h file only out of necessity. */
-UPB_INLINE bool upb_tabent_isempty(const upb_tabent* e) { return e->key == 0; }
-
-/* Initialize and uninitialize a table, respectively.  If memory allocation
- * failed, false is returned that the table is uninitialized. */
-bool upb_inttable_init(upb_inttable* table, upb_Arena* a);
-bool upb_strtable_init(upb_strtable* table, size_t expected_size, upb_Arena* a);
-
-/* Returns the number of values in the table. */
-size_t upb_inttable_count(const upb_inttable* t);
-UPB_INLINE size_t upb_strtable_count(const upb_strtable* t) {
-  return t->t.count;
-}
-
-void upb_strtable_clear(upb_strtable* t);
-
-/* Inserts the given key into the hashtable with the given value.  The key must
- * not already exist in the hash table.  For strtables, the key is not required
- * to be NULL-terminated, and the table will make an internal copy of the key.
- * Inttables must not insert a value of UINTPTR_MAX.
- *
- * If a table resize was required but memory allocation failed, false is
- * returned and the table is unchanged. */
-bool upb_inttable_insert(upb_inttable* t, uintptr_t key, upb_value val,
-                         upb_Arena* a);
-bool upb_strtable_insert(upb_strtable* t, const char* key, size_t len,
-                         upb_value val, upb_Arena* a);
-
-/* Looks up key in this table, returning "true" if the key was found.
- * If v is non-NULL, copies the value for this key into *v. */
-bool upb_inttable_lookup(const upb_inttable* t, uintptr_t key, upb_value* v);
-bool upb_strtable_lookup2(const upb_strtable* t, const char* key, size_t len,
-                          upb_value* v);
-
-/* For NULL-terminated strings. */
-UPB_INLINE bool upb_strtable_lookup(const upb_strtable* t, const char* key,
-                                    upb_value* v) {
-  return upb_strtable_lookup2(t, key, strlen(key), v);
-}
-
-/* Removes an item from the table.  Returns true if the remove was successful,
- * and stores the removed item in *val if non-NULL. */
-bool upb_inttable_remove(upb_inttable* t, uintptr_t key, upb_value* val);
-bool upb_strtable_remove2(upb_strtable* t, const char* key, size_t len,
-                          upb_value* val);
-
-UPB_INLINE bool upb_strtable_remove(upb_strtable* t, const char* key,
-                                    upb_value* v) {
-  return upb_strtable_remove2(t, key, strlen(key), v);
-}
-
-/* Updates an existing entry in an inttable.  If the entry does not exist,
- * returns false and does nothing.  Unlike insert/remove, this does not
- * invalidate iterators. */
-bool upb_inttable_replace(upb_inttable* t, uintptr_t key, upb_value val);
-
-/* Optimizes the table for the current set of entries, for both memory use and
- * lookup time.  Client should call this after all entries have been inserted;
- * inserting more entries is legal, but will likely require a table resize. */
-void upb_inttable_compact(upb_inttable* t, upb_Arena* a);
-
-/* Exposed for testing only. */
-bool upb_strtable_resize(upb_strtable* t, size_t size_lg2, upb_Arena* a);
-
-/* Iterators ******************************************************************/
-
-/* Iteration over inttable.
- *
- *   intptr_t iter = UPB_INTTABLE_BEGIN;
- *   uintptr_t key;
- *   upb_value val;
- *   while (upb_inttable_next2(t, &key, &val, &iter)) {
- *      // ...
- *   }
- */
-
-#define UPB_INTTABLE_BEGIN -1
-
-bool upb_inttable_next2(const upb_inttable* t, uintptr_t* key, upb_value* val,
-                        intptr_t* iter);
-void upb_inttable_removeiter(upb_inttable* t, intptr_t* iter);
-
-/* Iteration over strtable.
- *
- *   intptr_t iter = UPB_INTTABLE_BEGIN;
- *   upb_StringView key;
- *   upb_value val;
- *   while (upb_strtable_next2(t, &key, &val, &iter)) {
- *      // ...
- *   }
- */
-
-#define UPB_STRTABLE_BEGIN -1
-
-bool upb_strtable_next2(const upb_strtable* t, upb_StringView* key,
-                        upb_value* val, intptr_t* iter);
-void upb_strtable_removeiter(upb_strtable* t, intptr_t* iter);
-
-/* DEPRECATED iterators, slated for removal.
- *
- * Iterators for int and string tables.  We are subject to some kind of unusual
- * design constraints:
- *
- * For high-level languages:
- *  - we must be able to guarantee that we don't crash or corrupt memory even if
- *    the program accesses an invalidated iterator.
- *
- * For C++11 range-based for:
- *  - iterators must be copyable
- *  - iterators must be comparable
- *  - it must be possible to construct an "end" value.
- *
- * Iteration order is undefined.
- *
- * Modifying the table invalidates iterators.  upb_{str,int}table_done() is
- * guaranteed to work even on an invalidated iterator, as long as the table it
- * is iterating over has not been freed.  Calling next() or accessing data from
- * an invalidated iterator yields unspecified elements from the table, but it is
- * guaranteed not to crash and to return real table elements (except when done()
- * is true). */
-
-/* upb_strtable_iter **********************************************************/
-
-/*   upb_strtable_iter i;
- *   upb_strtable_begin(&i, t);
- *   for(; !upb_strtable_done(&i); upb_strtable_next(&i)) {
- *     const char *key = upb_strtable_iter_key(&i);
- *     const upb_value val = upb_strtable_iter_value(&i);
- *     // ...
- *   }
- */
-
-typedef struct {
-  const upb_strtable* t;
-  size_t index;
-} upb_strtable_iter;
-
-void upb_strtable_begin(upb_strtable_iter* i, const upb_strtable* t);
-void upb_strtable_next(upb_strtable_iter* i);
-bool upb_strtable_done(const upb_strtable_iter* i);
-upb_StringView upb_strtable_iter_key(const upb_strtable_iter* i);
-upb_value upb_strtable_iter_value(const upb_strtable_iter* i);
-void upb_strtable_iter_setdone(upb_strtable_iter* i);
-bool upb_strtable_iter_isequal(const upb_strtable_iter* i1,
-                               const upb_strtable_iter* i2);
-
-/* upb_inttable_iter **********************************************************/
-
-/*   upb_inttable_iter i;
- *   upb_inttable_begin(&i, t);
- *   for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
- *     uintptr_t key = upb_inttable_iter_key(&i);
- *     upb_value val = upb_inttable_iter_value(&i);
- *     // ...
- *   }
- */
-
-typedef struct {
-  const upb_inttable* t;
-  size_t index;
-  bool array_part;
-} upb_inttable_iter;
-
-UPB_INLINE const upb_tabent* str_tabent(const upb_strtable_iter* i) {
-  return &i->t->t.entries[i->index];
-}
-
-void upb_inttable_begin(upb_inttable_iter* i, const upb_inttable* t);
-void upb_inttable_next(upb_inttable_iter* i);
-bool upb_inttable_done(const upb_inttable_iter* i);
-uintptr_t upb_inttable_iter_key(const upb_inttable_iter* i);
-upb_value upb_inttable_iter_value(const upb_inttable_iter* i);
-void upb_inttable_iter_setdone(upb_inttable_iter* i);
-bool upb_inttable_iter_isequal(const upb_inttable_iter* i1,
-                               const upb_inttable_iter* i2);
-
-uint32_t _upb_Hash(const void* p, size_t n, uint64_t seed);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#include "upb/port_undef.inc"
+#include "upb/internal/table.h"
 
 #endif /* UPB_TABLE_H_ */
diff --git a/upb/test_table.cc b/upb/test_table.cc
index 2c368f8..ddb129a 100644
--- a/upb/test_table.cc
+++ b/upb/test_table.cc
@@ -39,7 +39,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-#include "upb/table_internal.h"
+#include "upb/internal/table.h"
 #include "upb/upb.hpp"
 
 // Must be last.
diff --git a/upb/text_encode.c b/upb/text_encode.c
index 4f5710b..dfb213f 100644
--- a/upb/text_encode.c
+++ b/upb/text_encode.c
@@ -34,9 +34,9 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "upb/internal/upb.h"
 #include "upb/internal/vsnprintf_compat.h"
 #include "upb/reflection.h"
-#include "upb/upb_internal.h"
 
 // Must be last.
 #include "upb/port_def.inc"
diff --git a/upb/upb.c b/upb/upb.c
index 5bf74bf..08ae964 100644
--- a/upb/upb.c
+++ b/upb/upb.c
@@ -25,6 +25,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "upb/internal/upb.h"
+
 #include <errno.h>
 #include <float.h>
 #include <stdarg.h>
@@ -34,299 +36,12 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "upb/upb_internal.h"
+#include "upb/arena.h"
+#include "upb/status.h"
 
 // Must be last.
 #include "upb/port_def.inc"
 
-/* upb_Status *****************************************************************/
-
-void upb_Status_Clear(upb_Status* status) {
-  if (!status) return;
-  status->ok = true;
-  status->msg[0] = '\0';
-}
-
-bool upb_Status_IsOk(const upb_Status* status) { return status->ok; }
-
-const char* upb_Status_ErrorMessage(const upb_Status* status) {
-  return status->msg;
-}
-
-void upb_Status_SetErrorMessage(upb_Status* status, const char* msg) {
-  if (!status) return;
-  status->ok = false;
-  strncpy(status->msg, msg, _kUpb_Status_MaxMessage - 1);
-  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
-}
-
-void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  upb_Status_VSetErrorFormat(status, fmt, args);
-  va_end(args);
-}
-
-void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt,
-                                va_list args) {
-  if (!status) return;
-  status->ok = false;
-  vsnprintf(status->msg, sizeof(status->msg), fmt, args);
-  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
-}
-
-void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt,
-                                   va_list args) {
-  size_t len;
-  if (!status) return;
-  status->ok = false;
-  len = strlen(status->msg);
-  vsnprintf(status->msg + len, sizeof(status->msg) - len, fmt, args);
-  status->msg[_kUpb_Status_MaxMessage - 1] = '\0';
-}
-
-/* upb_alloc ******************************************************************/
-
-static void* upb_global_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize,
-                                  size_t size) {
-  UPB_UNUSED(alloc);
-  UPB_UNUSED(oldsize);
-  if (size == 0) {
-    free(ptr);
-    return NULL;
-  } else {
-    return realloc(ptr, size);
-  }
-}
-
-static uint32_t* upb_cleanup_pointer(uintptr_t cleanup_metadata) {
-  return (uint32_t*)(cleanup_metadata & ~0x1);
-}
-
-static bool upb_cleanup_has_initial_block(uintptr_t cleanup_metadata) {
-  return cleanup_metadata & 0x1;
-}
-
-static uintptr_t upb_cleanup_metadata(uint32_t* cleanup,
-                                      bool has_initial_block) {
-  return (uintptr_t)cleanup | has_initial_block;
-}
-
-upb_alloc upb_alloc_global = {&upb_global_allocfunc};
-
-/* upb_Arena ******************************************************************/
-
-struct mem_block {
-  struct mem_block* next;
-  uint32_t size;
-  uint32_t cleanups;
-  /* Data follows. */
-};
-
-typedef struct cleanup_ent {
-  upb_CleanupFunc* cleanup;
-  void* ud;
-} cleanup_ent;
-
-static const size_t memblock_reserve =
-    UPB_ALIGN_UP(sizeof(mem_block), UPB_MALLOC_ALIGN);
-
-static upb_Arena* arena_findroot(upb_Arena* a) {
-  /* Path splitting keeps time complexity down, see:
-   *   https://en.wikipedia.org/wiki/Disjoint-set_data_structure */
-  while (a->parent != a) {
-    upb_Arena* next = a->parent;
-    a->parent = next->parent;
-    a = next;
-  }
-  return a;
-}
-
-static void upb_Arena_addblock(upb_Arena* a, upb_Arena* root, void* ptr,
-                               size_t size) {
-  mem_block* block = ptr;
-
-  /* The block is for arena |a|, but should appear in the freelist of |root|. */
-  block->next = root->freelist;
-  block->size = (uint32_t)size;
-  block->cleanups = 0;
-  root->freelist = block;
-  a->last_size = block->size;
-  if (!root->freelist_tail) root->freelist_tail = block;
-
-  a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char);
-  a->head.end = UPB_PTR_AT(block, size, char);
-  a->cleanup_metadata = upb_cleanup_metadata(
-      &block->cleanups, upb_cleanup_has_initial_block(a->cleanup_metadata));
-
-  UPB_POISON_MEMORY_REGION(a->head.ptr, a->head.end - a->head.ptr);
-}
-
-static bool upb_Arena_Allocblock(upb_Arena* a, size_t size) {
-  upb_Arena* root = arena_findroot(a);
-  size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve;
-  mem_block* block = upb_malloc(root->block_alloc, block_size);
-
-  if (!block) return false;
-  upb_Arena_addblock(a, root, block, block_size);
-  return true;
-}
-
-void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size) {
-  if (!upb_Arena_Allocblock(a, size)) return NULL; /* Out of memory. */
-  UPB_ASSERT(_upb_ArenaHas(a) >= size);
-  return upb_Arena_Malloc(a, size);
-}
-
-static void* upb_Arena_doalloc(upb_alloc* alloc, void* ptr, size_t oldsize,
-                               size_t size) {
-  upb_Arena* a = (upb_Arena*)alloc; /* upb_alloc is initial member. */
-  return upb_Arena_Realloc(a, ptr, oldsize, size);
-}
-
-/* Public Arena API ***********************************************************/
-
-upb_Arena* arena_initslow(void* mem, size_t n, upb_alloc* alloc) {
-  const size_t first_block_overhead = sizeof(upb_Arena) + memblock_reserve;
-  upb_Arena* a;
-
-  /* We need to malloc the initial block. */
-  n = first_block_overhead + 256;
-  if (!alloc || !(mem = upb_malloc(alloc, n))) {
-    return NULL;
-  }
-
-  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena);
-  n -= sizeof(*a);
-
-  a->head.alloc.func = &upb_Arena_doalloc;
-  a->block_alloc = alloc;
-  a->parent = a;
-  a->refcount = 1;
-  a->freelist = NULL;
-  a->freelist_tail = NULL;
-  a->cleanup_metadata = upb_cleanup_metadata(NULL, false);
-
-  upb_Arena_addblock(a, a, mem, n);
-
-  return a;
-}
-
-upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc) {
-  upb_Arena* a;
-
-  if (n) {
-    /* Align initial pointer up so that we return properly-aligned pointers. */
-    void* aligned = (void*)UPB_ALIGN_UP((uintptr_t)mem, UPB_MALLOC_ALIGN);
-    size_t delta = (uintptr_t)aligned - (uintptr_t)mem;
-    n = delta <= n ? n - delta : 0;
-    mem = aligned;
-  }
-
-  /* Round block size down to alignof(*a) since we will allocate the arena
-   * itself at the end. */
-  n = UPB_ALIGN_DOWN(n, UPB_ALIGN_OF(upb_Arena));
-
-  if (UPB_UNLIKELY(n < sizeof(upb_Arena))) {
-    return arena_initslow(mem, n, alloc);
-  }
-
-  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_Arena);
-
-  a->head.alloc.func = &upb_Arena_doalloc;
-  a->block_alloc = alloc;
-  a->parent = a;
-  a->refcount = 1;
-  a->last_size = UPB_MAX(128, n);
-  a->head.ptr = mem;
-  a->head.end = UPB_PTR_AT(mem, n - sizeof(*a), char);
-  a->freelist = NULL;
-  a->cleanup_metadata = upb_cleanup_metadata(NULL, true);
-
-  return a;
-}
-
-static void arena_dofree(upb_Arena* a) {
-  mem_block* block = a->freelist;
-  UPB_ASSERT(a->parent == a);
-  UPB_ASSERT(a->refcount == 0);
-
-  while (block) {
-    /* Load first since we are deleting block. */
-    mem_block* next = block->next;
-
-    if (block->cleanups > 0) {
-      cleanup_ent* end = UPB_PTR_AT(block, block->size, void);
-      cleanup_ent* ptr = end - block->cleanups;
-
-      for (; ptr < end; ptr++) {
-        ptr->cleanup(ptr->ud);
-      }
-    }
-
-    upb_free(a->block_alloc, block);
-    block = next;
-  }
-}
-
-void upb_Arena_Free(upb_Arena* a) {
-  a = arena_findroot(a);
-  if (--a->refcount == 0) arena_dofree(a);
-}
-
-bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func) {
-  cleanup_ent* ent;
-  uint32_t* cleanups = upb_cleanup_pointer(a->cleanup_metadata);
-
-  if (!cleanups || _upb_ArenaHas(a) < sizeof(cleanup_ent)) {
-    if (!upb_Arena_Allocblock(a, 128)) return false; /* Out of memory. */
-    UPB_ASSERT(_upb_ArenaHas(a) >= sizeof(cleanup_ent));
-    cleanups = upb_cleanup_pointer(a->cleanup_metadata);
-  }
-
-  a->head.end -= sizeof(cleanup_ent);
-  ent = (cleanup_ent*)a->head.end;
-  (*cleanups)++;
-  UPB_UNPOISON_MEMORY_REGION(ent, sizeof(cleanup_ent));
-
-  ent->cleanup = func;
-  ent->ud = ud;
-
-  return true;
-}
-
-bool upb_Arena_Fuse(upb_Arena* a1, upb_Arena* a2) {
-  upb_Arena* r1 = arena_findroot(a1);
-  upb_Arena* r2 = arena_findroot(a2);
-
-  if (r1 == r2) return true; /* Already fused. */
-
-  /* Do not fuse initial blocks since we cannot lifetime extend them. */
-  if (upb_cleanup_has_initial_block(r1->cleanup_metadata)) return false;
-  if (upb_cleanup_has_initial_block(r2->cleanup_metadata)) return false;
-
-  /* Only allow fuse with a common allocator */
-  if (r1->block_alloc != r2->block_alloc) return false;
-
-  /* We want to join the smaller tree to the larger tree.
-   * So swap first if they are backwards. */
-  if (r1->refcount < r2->refcount) {
-    upb_Arena* tmp = r1;
-    r1 = r2;
-    r2 = tmp;
-  }
-
-  /* r1 takes over r2's freelist and refcount. */
-  r1->refcount += r2->refcount;
-  if (r2->freelist_tail) {
-    UPB_ASSERT(r2->freelist_tail->next == NULL);
-    r2->freelist_tail->next = r1->freelist;
-    r1->freelist = r2->freelist;
-  }
-  r2->parent = r1;
-  return true;
-}
-
 /* Miscellaneous utilities ****************************************************/
 
 static void upb_FixLocale(char* p) {
diff --git a/upb/upb.h b/upb/upb.h
index d1ac913..7398d09 100644
--- a/upb/upb.h
+++ b/upb/upb.h
@@ -39,34 +39,17 @@
 #include <stdint.h>
 #include <string.h>
 
+// TODO(b/232091617): Remove these and fix everything that breaks as a result.
+#include "upb/arena.h"
+#include "upb/status.h"
+
+// Must be last.
 #include "upb/port_def.inc"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* upb_Status *****************************************************************/
-
-#define _kUpb_Status_MaxMessage 127
-
-typedef struct {
-  bool ok;
-  char msg[_kUpb_Status_MaxMessage]; /* Error message; NULL-terminated. */
-} upb_Status;
-
-const char* upb_Status_ErrorMessage(const upb_Status* status);
-bool upb_Status_IsOk(const upb_Status* status);
-
-/* These are no-op if |status| is NULL. */
-void upb_Status_Clear(upb_Status* status);
-void upb_Status_SetErrorMessage(upb_Status* status, const char* msg);
-void upb_Status_SetErrorFormat(upb_Status* status, const char* fmt, ...)
-    UPB_PRINTF(2, 3);
-void upb_Status_VSetErrorFormat(upb_Status* status, const char* fmt,
-                                va_list args) UPB_PRINTF(2, 0);
-void upb_Status_VAppendErrorFormat(upb_Status* status, const char* fmt,
-                                   va_list args) UPB_PRINTF(2, 0);
-
 /** upb_StringView ************************************************************/
 
 typedef struct {
@@ -96,183 +79,6 @@
 #define UPB_STRINGVIEW_FORMAT "%.*s"
 #define UPB_STRINGVIEW_ARGS(view) (int)(view).size, (view).data
 
-/** upb_alloc *****************************************************************/
-
-/* A upb_alloc is a possibly-stateful allocator object.
- *
- * It could either be an arena allocator (which doesn't require individual
- * free() calls) or a regular malloc() (which does).  The client must therefore
- * free memory unless it knows that the allocator is an arena allocator. */
-
-struct upb_alloc;
-typedef struct upb_alloc upb_alloc;
-
-/* A malloc()/free() function.
- * If "size" is 0 then the function acts like free(), otherwise it acts like
- * realloc().  Only "oldsize" bytes from a previous allocation are preserved. */
-typedef void* upb_alloc_func(upb_alloc* alloc, void* ptr, size_t oldsize,
-                             size_t size);
-
-struct upb_alloc {
-  upb_alloc_func* func;
-};
-
-UPB_INLINE void* upb_malloc(upb_alloc* alloc, size_t size) {
-  UPB_ASSERT(alloc);
-  return alloc->func(alloc, NULL, 0, size);
-}
-
-UPB_INLINE void* upb_realloc(upb_alloc* alloc, void* ptr, size_t oldsize,
-                             size_t size) {
-  UPB_ASSERT(alloc);
-  return alloc->func(alloc, ptr, oldsize, size);
-}
-
-UPB_INLINE void upb_free(upb_alloc* alloc, void* ptr) {
-  assert(alloc);
-  alloc->func(alloc, ptr, 0, 0);
-}
-
-/* The global allocator used by upb.  Uses the standard malloc()/free(). */
-
-extern upb_alloc upb_alloc_global;
-
-/* Functions that hard-code the global malloc.
- *
- * We still get benefit because we can put custom logic into our global
- * allocator, like injecting out-of-memory faults in debug/testing builds. */
-
-UPB_INLINE void* upb_gmalloc(size_t size) {
-  return upb_malloc(&upb_alloc_global, size);
-}
-
-UPB_INLINE void* upb_grealloc(void* ptr, size_t oldsize, size_t size) {
-  return upb_realloc(&upb_alloc_global, ptr, oldsize, size);
-}
-
-UPB_INLINE void upb_gfree(void* ptr) { upb_free(&upb_alloc_global, ptr); }
-
-/* upb_Arena ******************************************************************/
-
-/* upb_Arena is a specific allocator implementation that uses arena allocation.
- * The user provides an allocator that will be used to allocate the underlying
- * arena blocks.  Arenas by nature do not require the individual allocations
- * to be freed.  However the Arena does allow users to register cleanup
- * functions that will run when the arena is destroyed.
- *
- * A upb_Arena is *not* thread-safe.
- *
- * You could write a thread-safe arena allocator that satisfies the
- * upb_alloc interface, but it would not be as efficient for the
- * single-threaded case. */
-
-typedef void upb_CleanupFunc(void* ud);
-
-struct upb_Arena;
-typedef struct upb_Arena upb_Arena;
-
-typedef struct {
-  /* We implement the allocator interface.
-   * This must be the first member of upb_Arena!
-   * TODO(haberman): remove once handlers are gone. */
-  upb_alloc alloc;
-
-  char *ptr, *end;
-} _upb_ArenaHead;
-
-/* Creates an arena from the given initial block (if any -- n may be 0).
- * Additional blocks will be allocated from |alloc|.  If |alloc| is NULL, this
- * is a fixed-size arena and cannot grow. */
-upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc);
-void upb_Arena_Free(upb_Arena* a);
-bool upb_Arena_AddCleanup(upb_Arena* a, void* ud, upb_CleanupFunc* func);
-bool upb_Arena_Fuse(upb_Arena* a, upb_Arena* b);
-void* _upb_Arena_SlowMalloc(upb_Arena* a, size_t size);
-
-UPB_INLINE upb_alloc* upb_Arena_Alloc(upb_Arena* a) { return (upb_alloc*)a; }
-
-UPB_INLINE size_t _upb_ArenaHas(upb_Arena* a) {
-  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
-  return (size_t)(h->end - h->ptr);
-}
-
-UPB_INLINE void* _upb_Arena_FastMalloc(upb_Arena* a, size_t size) {
-  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
-  void* ret = h->ptr;
-  UPB_ASSERT(UPB_ALIGN_MALLOC((uintptr_t)ret) == (uintptr_t)ret);
-  UPB_ASSERT(UPB_ALIGN_MALLOC(size) == size);
-  UPB_UNPOISON_MEMORY_REGION(ret, size);
-
-  h->ptr += size;
-
-#if UPB_ASAN
-  {
-    size_t guard_size = 32;
-    if (_upb_ArenaHas(a) >= guard_size) {
-      h->ptr += guard_size;
-    } else {
-      h->ptr = h->end;
-    }
-  }
-#endif
-
-  return ret;
-}
-
-UPB_INLINE void* upb_Arena_Malloc(upb_Arena* a, size_t size) {
-  size = UPB_ALIGN_MALLOC(size);
-
-  if (UPB_UNLIKELY(_upb_ArenaHas(a) < size)) {
-    return _upb_Arena_SlowMalloc(a, size);
-  }
-
-  return _upb_Arena_FastMalloc(a, size);
-}
-
-// Shrinks the last alloc from arena.
-// REQUIRES: (ptr, oldsize) was the last malloc/realloc from this arena.
-// We could also add a upb_Arena_TryShrinkLast() which is simply a no-op if
-// this was not the last alloc.
-UPB_INLINE void upb_Arena_ShrinkLast(upb_Arena* a, void* ptr, size_t oldsize,
-                                     size_t size) {
-  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
-  oldsize = UPB_ALIGN_MALLOC(oldsize);
-  size = UPB_ALIGN_MALLOC(size);
-  UPB_ASSERT((char*)ptr + oldsize == h->ptr);  // Must be the last alloc.
-  UPB_ASSERT(size <= oldsize);
-  h->ptr = (char*)ptr + size;
-}
-
-UPB_INLINE void* upb_Arena_Realloc(upb_Arena* a, void* ptr, size_t oldsize,
-                                   size_t size) {
-  _upb_ArenaHead* h = (_upb_ArenaHead*)a;
-  oldsize = UPB_ALIGN_MALLOC(oldsize);
-  size = UPB_ALIGN_MALLOC(size);
-  bool is_most_recent_alloc = (uintptr_t)ptr + oldsize == (uintptr_t)h->ptr;
-
-  if (is_most_recent_alloc) {
-    ptrdiff_t diff = size - oldsize;
-    if ((ptrdiff_t)_upb_ArenaHas(a) >= diff) {
-      h->ptr += diff;
-      return ptr;
-    }
-  } else if (size <= oldsize) {
-    return ptr;
-  }
-
-  void* ret = upb_Arena_Malloc(a, size);
-
-  if (ret && oldsize > 0) {
-    memcpy(ret, ptr, UPB_MIN(oldsize, size));
-  }
-
-  return ret;
-}
-
-UPB_INLINE upb_Arena* upb_Arena_New(void) {
-  return upb_Arena_Init(NULL, 0, &upb_alloc_global);
-}
-
 /* Constants ******************************************************************/
 
 /* A list of types as they are encoded on-the-wire. */
diff --git a/upb/upb_internal.h b/upb/upb_internal.h
index 1eb166f..6c1fec3 100644
--- a/upb/upb_internal.h
+++ b/upb/upb_internal.h
@@ -28,41 +28,9 @@
 #ifndef UPB_INT_H_
 #define UPB_INT_H_
 
-#include "upb/upb.h"
+// TODO(b/232091617): Delete this entire header which currently exists only for
+// temporary backwards compatibility.
 
-struct mem_block;
-typedef struct mem_block mem_block;
-
-struct upb_Arena {
-  _upb_ArenaHead head;
-  /* Stores cleanup metadata for this arena.
-   * - a pointer to the current cleanup counter.
-   * - a boolean indicating if there is an unowned initial block.  */
-  uintptr_t cleanup_metadata;
-
-  /* Allocator to allocate arena blocks.  We are responsible for freeing these
-   * when we are destroyed. */
-  upb_alloc* block_alloc;
-  uint32_t last_size;
-
-  /* When multiple arenas are fused together, each arena points to a parent
-   * arena (root points to itself). The root tracks how many live arenas
-   * reference it. */
-  uint32_t refcount; /* Only used when a->parent == a */
-  struct upb_Arena* parent;
-
-  /* Linked list of blocks to free/cleanup. */
-  mem_block *freelist, *freelist_tail;
-};
-
-// Encodes a float or double that is round-trippable, but as short as possible.
-// These routines are not fully optimal (not guaranteed to be shortest), but are
-// short-ish and match the implementation that has been used in protobuf since
-// the beginning.
-//
-// The given buffer size must be at least kUpb_RoundTripBufferSize.
-enum { kUpb_RoundTripBufferSize = 32 };
-void _upb_EncodeRoundTripDouble(double val, char* buf, size_t size);
-void _upb_EncodeRoundTripFloat(float val, char* buf, size_t size);
+#include "upb/internal/upb.h"
 
 #endif /* UPB_INT_H_ */