Updated upb and simplified ruby code a bit with new upb method.
diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c
index 9bc7273..f6bea50 100644
--- a/ruby/ext/google/protobuf_c/encode_decode.c
+++ b/ruby/ext/google/protobuf_c/encode_decode.c
@@ -656,7 +656,6 @@
 #define STACK_ENV_STACKBYTES 4096
 typedef struct {
   upb_env env;
-  upb_seededalloc alloc;
   const char* ruby_error_template;
   char allocbuf[STACK_ENV_STACKBYTES];
 } stackenv;
@@ -681,16 +680,12 @@
 
 static void stackenv_init(stackenv* se, const char* errmsg) {
   se->ruby_error_template = errmsg;
-  upb_env_init(&se->env);
-  upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES);
-  upb_env_setallocfunc(
-      &se->env, upb_seededalloc_getallocfunc(&se->alloc), &se->alloc);
+  upb_env_init2(&se->env, se->allocbuf, sizeof(se->allocbuf), NULL);
   upb_env_seterrorfunc(&se->env, env_error_func, se);
 }
 
 static void stackenv_uninit(stackenv* se) {
   upb_env_uninit(&se->env);
-  upb_seededalloc_uninit(&se->alloc);
 }
 
 /*
diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c
index 3a51fe4..e16250f 100644
--- a/ruby/ext/google/protobuf_c/message.c
+++ b/ruby/ext/google/protobuf_c/message.c
@@ -151,32 +151,30 @@
     name_len--;
   }
 
-  // Check for a oneof name first.
-  o = upb_msgdef_ntoo(self->descriptor->msgdef,
-                                          name, name_len);
+  // See if this name corresponds to either a oneof or field in this message.
+  if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f,
+                             &o)) {
+    return rb_call_super(argc, argv);
+  }
+
   if (o != NULL) {
+    // This is a oneof -- return which field inside the oneof is set.
     if (setter) {
       rb_raise(rb_eRuntimeError, "Oneof accessors are read-only.");
     }
     return which_oneof_field(self, o);
-  }
-
-  // Otherwise, check for a field with that name.
-  f = upb_msgdef_ntof(self->descriptor->msgdef,
-                                          name, name_len);
-
-  if (f == NULL) {
-    return rb_call_super(argc, argv);
-  }
-
-  if (setter) {
-    if (argc < 2) {
-      rb_raise(rb_eArgError, "No value provided to setter.");
-    }
-    layout_set(self->descriptor->layout, Message_data(self), f, argv[1]);
-    return Qnil;
   } else {
-    return layout_get(self->descriptor->layout, Message_data(self), f);
+    // This is a field -- get or set the field's value.
+    assert(f);
+    if (setter) {
+      if (argc < 2) {
+        rb_raise(rb_eArgError, "No value provided to setter.");
+      }
+      layout_set(self->descriptor->layout, Message_data(self), f, argv[1]);
+      return Qnil;
+    } else {
+      return layout_get(self->descriptor->layout, Message_data(self), f);
+    }
   }
 }
 
diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c
index 212f1e0..74a2a1d 100644
--- a/ruby/ext/google/protobuf_c/upb.c
+++ b/ruby/ext/google/protobuf_c/upb.c
@@ -12,7 +12,7 @@
 } str_t;
 
 static str_t *newstr(const char *data, size_t len) {
-  str_t *ret = malloc(sizeof(*ret) + len);
+  str_t *ret = upb_gmalloc(sizeof(*ret) + len);
   if (!ret) return NULL;
   ret->len = len;
   memcpy(ret->str, data, len);
@@ -20,7 +20,7 @@
   return ret;
 }
 
-static void freestr(str_t *s) { free(s); }
+static void freestr(str_t *s) { upb_gfree(s); }
 
 /* isalpha() etc. from <ctype.h> are locale-dependent, which we don't want. */
 static bool upb_isbetween(char c, char low, char high) {
@@ -65,6 +65,22 @@
   return !start;
 }
 
+static bool upb_isoneof(const upb_refcounted *def) {
+  return def->vtbl == &upb_oneofdef_vtbl;
+}
+
+static bool upb_isfield(const upb_refcounted *def) {
+  return def->vtbl == &upb_fielddef_vtbl;
+}
+
+static const upb_oneofdef *upb_trygetoneof(const upb_refcounted *def) {
+  return upb_isoneof(def) ? (const upb_oneofdef*)def : NULL;
+}
+
+static const upb_fielddef *upb_trygetfield(const upb_refcounted *def) {
+  return upb_isfield(def) ? (const upb_fielddef*)def : NULL;
+}
+
 
 /* upb_def ********************************************************************/
 
@@ -88,9 +104,18 @@
 
 bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) {
   assert(!upb_def_isfrozen(def));
-  if (!upb_isident(fullname, strlen(fullname), true, s)) return false;
-  free((void*)def->fullname);
-  def->fullname = upb_strdup(fullname);
+  if (!upb_isident(fullname, strlen(fullname), true, s)) {
+    return false;
+  }
+
+  fullname = upb_gstrdup(fullname);
+  if (!fullname) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+
+  upb_gfree((void*)def->fullname);
+  def->fullname = fullname;
   return true;
 }
 
@@ -123,7 +148,7 @@
 }
 
 static void upb_def_uninit(upb_def *def) {
-  free((void*)def->fullname);
+  upb_gfree((void*)def->fullname);
 }
 
 static const char *msgdef_name(const upb_msgdef *m) {
@@ -260,8 +285,19 @@
   int i;
   uint32_t selector;
   int n = upb_msgdef_numfields(m);
-  upb_fielddef **fields = malloc(n * sizeof(*fields));
-  if (!fields) return false;
+  upb_fielddef **fields;
+
+  if (n == 0) {
+    m->selector_count = UPB_STATIC_SELECTOR_COUNT;
+    m->submsg_field_count = 0;
+    return true;
+  }
+
+  fields = upb_gmalloc(n * sizeof(*fields));
+  if (!fields) {
+    upb_upberr_setoom(s);
+    return false;
+  }
 
   m->submsg_field_count = 0;
   for(i = 0, upb_msg_field_begin(&j, m);
@@ -270,7 +306,7 @@
     upb_fielddef *f = upb_msg_iter_field(&j);
     assert(f->msg.def == m);
     if (!upb_validate_field(f, s)) {
-      free(fields);
+      upb_gfree(fields);
       return false;
     }
     if (upb_fielddef_issubmsg(f)) {
@@ -330,7 +366,7 @@
 #undef TRY
 #endif
 
-  free(fields);
+  upb_gfree(fields);
   return true;
 }
 
@@ -407,21 +443,26 @@
   upb_inttable_iter i;
   upb_inttable_begin(&i, &e->iton);
   for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) {
-    /* To clean up the upb_strdup() from upb_enumdef_addval(). */
-    free(upb_value_getcstr(upb_inttable_iter_value(&i)));
+    /* To clean up the upb_gstrdup() from upb_enumdef_addval(). */
+    upb_gfree(upb_value_getcstr(upb_inttable_iter_value(&i)));
   }
   upb_strtable_uninit(&e->ntoi);
   upb_inttable_uninit(&e->iton);
   upb_def_uninit(upb_enumdef_upcast_mutable(e));
-  free(e);
+  upb_gfree(e);
 }
 
+const struct upb_refcounted_vtbl upb_enumdef_vtbl = {NULL, &upb_enumdef_free};
+
 upb_enumdef *upb_enumdef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free};
-  upb_enumdef *e = malloc(sizeof(*e));
+  upb_enumdef *e = upb_gmalloc(sizeof(*e));
   if (!e) return NULL;
-  if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner))
+
+  if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM,
+                    &upb_enumdef_vtbl, owner)) {
     goto err2;
+  }
+
   if (!upb_strtable_init(&e->ntoi, UPB_CTYPE_INT32)) goto err2;
   if (!upb_inttable_init(&e->iton, UPB_CTYPE_CSTR)) goto err1;
   return e;
@@ -429,7 +470,7 @@
 err1:
   upb_strtable_uninit(&e->ntoi);
 err2:
-  free(e);
+  upb_gfree(e);
   return NULL;
 }
 
@@ -468,27 +509,36 @@
 
 bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num,
                         upb_status *status) {
+  char *name2;
+
   if (!upb_isident(name, strlen(name), false, status)) {
     return false;
   }
+
   if (upb_enumdef_ntoiz(e, name, NULL)) {
     upb_status_seterrf(status, "name '%s' is already defined", name);
     return false;
   }
+
   if (!upb_strtable_insert(&e->ntoi, name, upb_value_int32(num))) {
     upb_status_seterrmsg(status, "out of memory");
     return false;
   }
-  if (!upb_inttable_lookup(&e->iton, num, NULL) &&
-      !upb_inttable_insert(&e->iton, num, upb_value_cstr(upb_strdup(name)))) {
-    upb_status_seterrmsg(status, "out of memory");
-    upb_strtable_remove(&e->ntoi, name, NULL);
-    return false;
+
+  if (!upb_inttable_lookup(&e->iton, num, NULL)) {
+    name2 = upb_gstrdup(name);
+    if (!name2 || !upb_inttable_insert(&e->iton, num, upb_value_cstr(name2))) {
+      upb_status_seterrmsg(status, "out of memory");
+      upb_strtable_remove(&e->ntoi, name, NULL);
+      return false;
+    }
   }
+
   if (upb_enumdef_numvals(e) == 1) {
     bool ok = upb_enumdef_setdefault(e, num, NULL);
     UPB_ASSERT_VAR(ok, ok);
   }
+
   return true;
 }
 
@@ -575,9 +625,9 @@
   upb_fielddef *f = (upb_fielddef*)r;
   upb_fielddef_uninit_default(f);
   if (f->subdef_is_symbolic)
-    free(f->sub.name);
+    upb_gfree(f->sub.name);
   upb_def_uninit(upb_fielddef_upcast_mutable(f));
-  free(f);
+  upb_gfree(f);
 }
 
 static const char *enumdefaultstr(const upb_fielddef *f) {
@@ -633,12 +683,14 @@
   return false;
 }
 
+const struct upb_refcounted_vtbl upb_fielddef_vtbl = {visitfield, freefield};
+
 upb_fielddef *upb_fielddef_new(const void *o) {
-  static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield};
-  upb_fielddef *f = malloc(sizeof(*f));
+  upb_fielddef *f = upb_gmalloc(sizeof(*f));
   if (!f) return NULL;
-  if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) {
-    free(f);
+  if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD,
+                    &upb_fielddef_vtbl, o)) {
+    upb_gfree(f);
     return NULL;
   }
   f->msg.def = NULL;
@@ -689,7 +741,7 @@
     srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL;
   }
   if (srcname) {
-    char *newname = malloc(strlen(f->sub.def->fullname) + 2);
+    char *newname = upb_gmalloc(strlen(f->sub.def->fullname) + 2);
     if (!newname) {
       upb_fielddef_unref(newf, owner);
       return NULL;
@@ -697,7 +749,7 @@
     strcpy(newname, ".");
     strcat(newname, f->sub.def->fullname);
     upb_fielddef_setsubdefname(newf, newname, NULL);
-    free(newname);
+    upb_gfree(newname);
   }
 
   return newf;
@@ -804,11 +856,12 @@
 }
 
 static void release_containingtype(upb_fielddef *f) {
-  if (f->msg_is_symbolic) free(f->msg.name);
+  if (f->msg_is_symbolic) upb_gfree(f->msg.name);
 }
 
 bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name,
                                         upb_status *s) {
+  char *name_copy;
   assert(!upb_fielddef_isfrozen(f));
   if (upb_fielddef_containingtype(f)) {
     upb_status_seterrmsg(s, "field has already been added to a message.");
@@ -816,8 +869,15 @@
   }
   /* TODO: validate name (upb_isident() doesn't quite work atm because this name
    * may have a leading "."). */
+
+  name_copy = upb_gstrdup(name);
+  if (!name_copy) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+
   release_containingtype(f);
-  f->msg.name = upb_strdup(name);
+  f->msg.name = name_copy;
   f->msg_is_symbolic = true;
   return true;
 }
@@ -1218,7 +1278,7 @@
 
 static void release_subdef(upb_fielddef *f) {
   if (f->subdef_is_symbolic) {
-    free(f->sub.name);
+    upb_gfree(f->sub.name);
   } else if (f->sub.def) {
     upb_unref2(f->sub.def, f);
   }
@@ -1248,15 +1308,23 @@
 
 bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name,
                                 upb_status *s) {
+  char *name_copy;
   assert(!upb_fielddef_isfrozen(f));
   if (!upb_fielddef_hassubdef(f)) {
     upb_status_seterrmsg(s, "field type does not accept a subdef");
     return false;
   }
+
+  name_copy = upb_gstrdup(name);
+  if (!name_copy) {
+    upb_upberr_setoom(s);
+    return false;
+  }
+
   /* TODO: validate name (upb_isident() doesn't quite work atm because this name
    * may have a leading "."). */
   release_subdef(f);
-  f->sub.name = upb_strdup(name);
+  f->sub.name = name_copy;
   f->subdef_is_symbolic = true;
   return true;
 }
@@ -1332,32 +1400,33 @@
 
 static void freemsg(upb_refcounted *r) {
   upb_msgdef *m = (upb_msgdef*)r;
-  upb_strtable_uninit(&m->ntoo);
   upb_strtable_uninit(&m->ntof);
   upb_inttable_uninit(&m->itof);
   upb_def_uninit(upb_msgdef_upcast_mutable(m));
-  free(m);
+  upb_gfree(m);
 }
 
+const struct upb_refcounted_vtbl upb_msgdef_vtbl = {visitmsg, freemsg};
+
 upb_msgdef *upb_msgdef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg};
-  upb_msgdef *m = malloc(sizeof(*m));
+  upb_msgdef *m = upb_gmalloc(sizeof(*m));
   if (!m) return NULL;
-  if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner))
+
+  if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &upb_msgdef_vtbl,
+                    owner)) {
     goto err2;
-  if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err3;
-  if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err2;
-  if (!upb_strtable_init(&m->ntoo, UPB_CTYPE_PTR)) goto err1;
+  }
+
+  if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err2;
+  if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err1;
   m->map_entry = false;
   m->syntax = UPB_SYNTAX_PROTO2;
   return m;
 
 err1:
-  upb_strtable_uninit(&m->ntof);
-err2:
   upb_inttable_uninit(&m->itof);
-err3:
-  free(m);
+err2:
+  upb_gfree(m);
   return NULL;
 }
 
@@ -1415,6 +1484,19 @@
   return upb_def_setfullname(upb_msgdef_upcast_mutable(m), fullname, s);
 }
 
+bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax) {
+  if (syntax != UPB_SYNTAX_PROTO2 && syntax != UPB_SYNTAX_PROTO3) {
+    return false;
+  }
+
+  m->syntax = syntax;
+  return true;
+}
+
+upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m) {
+  return m->syntax;
+}
+
 /* Helper: check that the field |f| is safe to add to msgdef |m|. Set an error
  * on status |s| and return false if not. */
 static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f,
@@ -1425,9 +1507,11 @@
   } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) {
     upb_status_seterrmsg(s, "field name or number were not set");
     return false;
-  } else if (upb_msgdef_ntofz(m, upb_fielddef_name(f)) ||
-             upb_msgdef_itof(m, upb_fielddef_number(f))) {
-    upb_status_seterrmsg(s, "duplicate field name or number for field");
+  } else if (upb_msgdef_itof(m, upb_fielddef_number(f))) {
+    upb_status_seterrmsg(s, "duplicate field number");
+    return false;
+  } else if (upb_strtable_lookup(&m->ntof, upb_fielddef_name(f), NULL)) {
+    upb_status_seterrmsg(s, "name conflicts with existing field or oneof");
     return false;
   }
   return true;
@@ -1488,8 +1572,8 @@
   } else if (upb_oneofdef_name(o) == NULL) {
     upb_status_seterrmsg(s, "oneofdef name was not set");
     return false;
-  } else if (upb_msgdef_ntooz(m, upb_oneofdef_name(o))) {
-    upb_status_seterrmsg(s, "duplicate oneof name");
+  } else if (upb_strtable_lookup(&m->ntof, upb_oneofdef_name(o), NULL)) {
+    upb_status_seterrmsg(s, "name conflicts with existing field or oneof");
     return false;
   }
 
@@ -1506,7 +1590,7 @@
 
   /* Add oneof itself first. */
   o->parent = m;
-  upb_strtable_insert(&m->ntoo, upb_oneofdef_name(o), upb_value_ptr(o));
+  upb_strtable_insert(&m->ntof, upb_oneofdef_name(o), upb_value_ptr(o));
   upb_ref2(o, m);
   upb_ref2(m, o);
 
@@ -1530,23 +1614,47 @@
 const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name,
                                     size_t len) {
   upb_value val;
-  return upb_strtable_lookup2(&m->ntof, name, len, &val) ?
-      upb_value_getptr(val) : NULL;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  return upb_trygetfield(upb_value_getptr(val));
 }
 
 const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name,
                                     size_t len) {
   upb_value val;
-  return upb_strtable_lookup2(&m->ntoo, name, len, &val) ?
-      upb_value_getptr(val) : NULL;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  return upb_trygetoneof(upb_value_getptr(val));
+}
+
+bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len,
+                           const upb_fielddef **f, const upb_oneofdef **o) {
+  upb_value val;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return false;
+  }
+
+  *o = upb_trygetoneof(upb_value_getptr(val));
+  *f = upb_trygetfield(upb_value_getptr(val));
+  assert((*o != NULL) ^ (*f != NULL));  /* Exactly one of the two should be set. */
+  return true;
 }
 
 int upb_msgdef_numfields(const upb_msgdef *m) {
-  return upb_strtable_count(&m->ntof);
+  /* The number table contains only fields. */
+  return upb_inttable_count(&m->itof);
 }
 
 int upb_msgdef_numoneofs(const upb_msgdef *m) {
-  return upb_strtable_count(&m->ntoo);
+  /* The name table includes oneofs, and the number table does not. */
+  return upb_strtable_count(&m->ntof) - upb_inttable_count(&m->itof);
 }
 
 void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) {
@@ -1577,10 +1685,21 @@
 }
 
 void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) {
-  upb_strtable_begin(iter, &m->ntoo);
+  upb_strtable_begin(iter, &m->ntof);
+  /* We need to skip past any initial fields. */
+  while (!upb_strtable_done(iter) &&
+         !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter)))) {
+    upb_strtable_next(iter);
+  }
 }
 
-void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { upb_strtable_next(iter); }
+void upb_msg_oneof_next(upb_msg_oneof_iter *iter) {
+  /* We need to skip past fields to return only oneofs. */
+  do {
+    upb_strtable_next(iter);
+  } while (!upb_strtable_done(iter) &&
+           !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter))));
+}
 
 bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) {
   return upb_strtable_done(iter);
@@ -1613,26 +1732,36 @@
   upb_oneofdef *o = (upb_oneofdef*)r;
   upb_strtable_uninit(&o->ntof);
   upb_inttable_uninit(&o->itof);
-  free((void*)o->name);
-  free(o);
+  upb_gfree((void*)o->name);
+  upb_gfree(o);
 }
 
+const struct upb_refcounted_vtbl upb_oneofdef_vtbl = {visitoneof, freeoneof};
+
 upb_oneofdef *upb_oneofdef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof};
-  upb_oneofdef *o = malloc(sizeof(*o));
+  upb_oneofdef *o = upb_gmalloc(sizeof(*o));
+
+  if (!o) {
+    return NULL;
+  }
+
   o->parent = NULL;
-  if (!o) return NULL;
-  if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &vtbl, owner))
-    goto err2;
   o->name = NULL;
+
+  if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &upb_oneofdef_vtbl,
+                           owner)) {
+    goto err2;
+  }
+
   if (!upb_inttable_init(&o->itof, UPB_CTYPE_PTR)) goto err2;
   if (!upb_strtable_init(&o->ntof, UPB_CTYPE_PTR)) goto err1;
+
   return o;
 
 err1:
   upb_inttable_uninit(&o->itof);
 err2:
-  free(o);
+  upb_gfree(o);
   return NULL;
 }
 
@@ -1661,9 +1790,19 @@
     upb_status_seterrmsg(s, "oneof already added to a message");
     return false;
   }
-  if (!upb_isident(name, strlen(name), true, s)) return false;
-  free((void*)o->name);
-  o->name = upb_strdup(name);
+
+  if (!upb_isident(name, strlen(name), true, s)) {
+    return false;
+  }
+
+  name = upb_gstrdup(name);
+  if (!name) {
+    upb_status_seterrmsg(s, "One of memory");
+    return false;
+  }
+
+  upb_gfree((void*)o->name);
+  o->name = name;
   return true;
 }
 
@@ -1805,14 +1944,15 @@
 
   upb_inttable_uninit(&f->defs);
   upb_inttable_uninit(&f->deps);
-  free((void*)f->name);
-  free((void*)f->package);
-  free(f);
+  upb_gfree((void*)f->name);
+  upb_gfree((void*)f->package);
+  upb_gfree(f);
 }
 
+const struct upb_refcounted_vtbl upb_filedef_vtbl = {visitfiledef, freefiledef};
+
 upb_filedef *upb_filedef_new(const void *owner) {
-  static const struct upb_refcounted_vtbl vtbl = {visitfiledef, freefiledef};
-  upb_filedef *f = malloc(sizeof(*f));
+  upb_filedef *f = upb_gmalloc(sizeof(*f));
 
   if (!f) {
     return NULL;
@@ -1822,7 +1962,8 @@
   f->name = NULL;
   f->syntax = UPB_SYNTAX_PROTO2;
 
-  if (!upb_refcounted_init(upb_filedef_upcast_mutable(f), &vtbl, owner)) {
+  if (!upb_refcounted_init(upb_filedef_upcast_mutable(f), &upb_filedef_vtbl,
+                           owner)) {
     goto err;
   }
 
@@ -1841,7 +1982,7 @@
   upb_inttable_uninit(&f->defs);
 
 err:
-  free(f);
+  upb_gfree(f);
   return NULL;
 }
 
@@ -1886,12 +2027,12 @@
 }
 
 bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) {
-  name = upb_strdup(name);
+  name = upb_gstrdup(name);
   if (!name) {
-    upb_status_seterrmsg(s, "Out of memory");
+    upb_upberr_setoom(s);
     return false;
   }
-  free((void*)f->name);
+  upb_gfree((void*)f->name);
   f->name = name;
   return true;
 }
@@ -1899,12 +2040,12 @@
 bool upb_filedef_setpackage(upb_filedef *f, const char *package,
                             upb_status *s) {
   if (!upb_isident(package, strlen(package), true, s)) return false;
-  package = upb_strdup(package);
+  package = upb_gstrdup(package);
   if (!package) {
-    upb_status_seterrmsg(s, "Out of memory");
+    upb_upberr_setoom(s);
     return false;
   }
-  free((void*)f->package);
+  upb_gfree((void*)f->package);
   f->package = package;
   return true;
 }
@@ -1953,7 +2094,7 @@
     }
     return true;
   } else {
-    upb_status_seterrmsg(s, "Out of memory.");
+    upb_upberr_setoom(s);
     return false;
   }
 }
@@ -1967,288 +2108,22 @@
     return false;
   }
 }
-
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-typedef struct cleanup_ent {
-  upb_cleanup_func *cleanup;
-  void *ud;
-  struct cleanup_ent *next;
-} cleanup_ent;
-
-static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size);
-
-/* Default allocator **********************************************************/
-
-/* Just use realloc, keeping all allocated blocks in a linked list to destroy at
- * the end. */
-
-typedef struct mem_block {
-  /* List is doubly-linked, because in cases where realloc() moves an existing
-   * block, we need to be able to remove the old pointer from the list
-   * efficiently. */
-  struct mem_block *prev, *next;
-#ifndef NDEBUG
-  size_t size;  /* Doesn't include mem_block structure. */
-#endif
-} mem_block;
-
-typedef struct {
-  mem_block *head;
-} default_alloc_ud;
-
-static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) {
-  default_alloc_ud *ud = _ud;
-  mem_block *from, *block;
-  void *ret;
-  UPB_UNUSED(oldsize);
-
-  from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL;
-
-#ifndef NDEBUG
-  if (from) {
-    assert(oldsize <= from->size);
-  }
-#endif
-
-  /* TODO(haberman): we probably need to provide even better alignment here,
-   * like 16-byte alignment of the returned data pointer. */
-  block = realloc(from, size + sizeof(mem_block));
-  if (!block) return NULL;
-  ret = (char*)block + sizeof(*block);
-
-#ifndef NDEBUG
-  block->size = size;
-#endif
-
-  if (from) {
-    if (block != from) {
-      /* The block was moved, so pointers in next and prev blocks must be
-       * updated to its new location. */
-      if (block->next) block->next->prev = block;
-      if (block->prev) block->prev->next = block;
-      if (ud->head == from) ud->head = block;
-    }
-  } else {
-    /* Insert at head of linked list. */
-    block->prev = NULL;
-    block->next = ud->head;
-    if (block->next) block->next->prev = block;
-    ud->head = block;
-  }
-
-  return ret;
-}
-
-static void default_alloc_cleanup(void *_ud) {
-  default_alloc_ud *ud = _ud;
-  mem_block *block = ud->head;
-
-  while (block) {
-    void *to_free = block;
-    block = block->next;
-    free(to_free);
-  }
-}
-
-
-/* Standard error functions ***************************************************/
-
-static bool default_err(void *ud, const upb_status *status) {
-  UPB_UNUSED(ud);
-  UPB_UNUSED(status);
-  return false;
-}
-
-static bool write_err_to(void *ud, const upb_status *status) {
-  upb_status *copy_to = ud;
-  upb_status_copy(copy_to, status);
-  return false;
-}
-
-
-/* upb_env ********************************************************************/
-
-void upb_env_init(upb_env *e) {
-  default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud;
-  e->ok_ = true;
-  e->bytes_allocated = 0;
-  e->cleanup_head = NULL;
-
-  ud->head = NULL;
-
-  /* Set default functions. */
-  upb_env_setallocfunc(e, default_alloc, ud);
-  upb_env_seterrorfunc(e, default_err, NULL);
-}
-
-void upb_env_uninit(upb_env *e) {
-  cleanup_ent *ent = e->cleanup_head;
-
-  while (ent) {
-    ent->cleanup(ent->ud);
-    ent = ent->next;
-  }
-
-  /* Must do this after running cleanup functions, because this will delete
-     the memory we store our cleanup entries in! */
-  if (e->alloc == default_alloc) {
-    default_alloc_cleanup(e->alloc_ud);
-  }
-}
-
-UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc,
-                                          void *ud) {
-  e->alloc = alloc;
-  e->alloc_ud = ud;
-}
-
-UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func,
-                                          void *ud) {
-  e->err = func;
-  e->err_ud = ud;
-}
-
-void upb_env_reporterrorsto(upb_env *e, upb_status *status) {
-  e->err = write_err_to;
-  e->err_ud = status;
-}
-
-bool upb_env_ok(const upb_env *e) {
-  return e->ok_;
-}
-
-bool upb_env_reporterror(upb_env *e, const upb_status *status) {
-  e->ok_ = false;
-  return e->err(e->err_ud, status);
-}
-
-bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) {
-  cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent));
-  if (!ent) return false;
-
-  ent->cleanup = func;
-  ent->ud = ud;
-  ent->next = e->cleanup_head;
-  e->cleanup_head = ent;
-
-  return true;
-}
-
-void *upb_env_malloc(upb_env *e, size_t size) {
-  e->bytes_allocated += size;
-  if (e->alloc == seeded_alloc) {
-    /* This is equivalent to the next branch, but allows inlining for a
-     * measurable perf benefit. */
-    return seeded_alloc(e->alloc_ud, NULL, 0, size);
-  } else {
-    return e->alloc(e->alloc_ud, NULL, 0, size);
-  }
-}
-
-void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) {
-  char *ret;
-  assert(oldsize <= size);
-  ret = e->alloc(e->alloc_ud, ptr, oldsize, size);
-
-#ifndef NDEBUG
-  /* Overwrite non-preserved memory to ensure callers are passing the oldsize
-   * that they truly require. */
-  memset(ret + oldsize, 0xff, size - oldsize);
-#endif
-
-  return ret;
-}
-
-size_t upb_env_bytesallocated(const upb_env *e) {
-  return e->bytes_allocated;
-}
-
-
-/* upb_seededalloc ************************************************************/
-
-/* Be conservative and choose 16 in case anyone is using SSE. */
-static const size_t maxalign = 16;
-
-static size_t align_up(size_t size) {
-  return ((size + maxalign - 1) / maxalign) * maxalign;
-}
-
-UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize,
-                                          size_t size) {
-  upb_seededalloc *a = ud;
-
-  size = align_up(size);
-
-  assert(a->mem_limit >= a->mem_ptr);
-
-  if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) {
-    /* Fast path: we can satisfy from the initial allocation. */
-    void *ret = a->mem_ptr;
-    a->mem_ptr += size;
-    return ret;
-  } else {
-    char *chptr = ptr;
-    /* Slow path: fallback to other allocator. */
-    a->need_cleanup = true;
-    /* Is `ptr` part of the user-provided initial block? Don't pass it to the
-     * default allocator if so; otherwise, it may try to realloc() the block. */
-    if (chptr >= a->mem_base && chptr < a->mem_limit) {
-      void *ret;
-      assert(chptr + oldsize <= a->mem_limit);
-      ret = a->alloc(a->alloc_ud, NULL, 0, size);
-      if (ret) memcpy(ret, ptr, oldsize);
-      return ret;
-    } else {
-      return a->alloc(a->alloc_ud, ptr, oldsize, size);
-    }
-  }
-}
-
-void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) {
-  default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud;
-  a->mem_base = mem;
-  a->mem_ptr = mem;
-  a->mem_limit = (char*)mem + len;
-  a->need_cleanup = false;
-  a->returned_allocfunc = false;
-
-  ud->head = NULL;
-
-  upb_seededalloc_setfallbackalloc(a, default_alloc, ud);
-}
-
-void upb_seededalloc_uninit(upb_seededalloc *a) {
-  if (a->alloc == default_alloc && a->need_cleanup) {
-    default_alloc_cleanup(a->alloc_ud);
-  }
-}
-
-UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a,
-                                                      upb_alloc_func *alloc,
-                                                      void *ud) {
-  assert(!a->returned_allocfunc);
-  a->alloc = alloc;
-  a->alloc_ud = ud;
-}
-
-upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) {
-  a->returned_allocfunc = true;
-  return seeded_alloc;
-}
 /*
 ** TODO(haberman): it's unclear whether a lot of the consistency checks should
 ** assert() or return false.
 */
 
 
-#include <stdlib.h>
 #include <string.h>
 
 
+static void *upb_calloc(size_t size) {
+  void *mem = upb_gmalloc(size);
+  if (mem) {
+    memset(mem, 0, size);
+  }
+  return mem;
+}
 
 /* Defined for the sole purpose of having a unique pointer value for
  * UPB_NO_CLOSURE. */
@@ -2268,8 +2143,8 @@
 
   upb_inttable_uninit(&h->cleanup_);
   upb_msgdef_unref(h->msg, h);
-  free(h->sub);
-  free(h);
+  upb_gfree(h->sub);
+  upb_gfree(h);
 }
 
 static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit,
@@ -2519,14 +2394,20 @@
   assert(upb_msgdef_isfrozen(md));
 
   extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1);
-  h = calloc(sizeof(*h) + extra, 1);
+  h = upb_calloc(sizeof(*h) + extra);
   if (!h) return NULL;
 
   h->msg = md;
   upb_msgdef_ref(h->msg, h);
   upb_status_clear(&h->status_);
-  h->sub = calloc(md->submsg_field_count, sizeof(*h->sub));
-  if (!h->sub) goto oom;
+
+  if (md->submsg_field_count > 0) {
+    h->sub = upb_calloc(md->submsg_field_count * sizeof(*h->sub));
+    if (!h->sub) goto oom;
+  } else {
+    h->sub = 0;
+  }
+
   if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner))
     goto oom;
   if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom;
@@ -2941,7 +2822,6 @@
 
 
 #include <setjmp.h>
-#include <stdlib.h>
 
 static void freeobj(upb_refcounted *o);
 
@@ -3017,8 +2897,31 @@
 /* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some
  * code-paths that can normally never fail, like upb_refcounted_ref().  Since
  * we have no way to propagage out-of-memory errors back to the user, and since
- * these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */
-#define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); }
+ * these errors can only occur in UPB_DEBUG_REFS mode, we use an allocator that
+ * immediately aborts on failure (avoiding the global allocator, which might
+ * inject failures). */
+
+#include <stdlib.h>
+
+static void *upb_debugrefs_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 {
+    void *ret = realloc(ptr, size);
+
+    if (!ret) {
+      abort();
+    }
+
+    return ret;
+  }
+}
+
+upb_alloc upb_alloc_debugrefs = {&upb_debugrefs_allocfunc};
 
 typedef struct {
   int count;  /* How many refs there are (duplicates only allowed for ref2). */
@@ -3026,8 +2929,7 @@
 } trackedref;
 
 static trackedref *trackedref_new(bool is_ref2) {
-  trackedref *ret = malloc(sizeof(*ret));
-  CHECK_OOM(ret);
+  trackedref *ret = upb_malloc(&upb_alloc_debugrefs, sizeof(*ret));
   ret->count = 1;
   ret->is_ref2 = is_ref2;
   return ret;
@@ -3052,15 +2954,15 @@
     ref->count++;
   } else {
     trackedref *ref = trackedref_new(ref2);
-    bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref));
-    CHECK_OOM(ok);
+    upb_inttable_insertptr2(r->refs, owner, upb_value_ptr(ref),
+                            &upb_alloc_debugrefs);
     if (ref2) {
       /* We know this cast is safe when it is a ref2, because it's coming from
        * another refcounted object. */
       const upb_refcounted *from = owner;
       assert(!upb_inttable_lookupptr(from->ref2s, r, NULL));
-      ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL));
-      CHECK_OOM(ok);
+      upb_inttable_insertptr2(from->ref2s, r, upb_value_ptr(NULL),
+                              &upb_alloc_debugrefs);
     }
   }
   upb_unlock();
@@ -3118,7 +3020,6 @@
     upb_value v;
     upb_value count;
     trackedref *ref;
-    bool ok;
     bool found;
 
     upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i);
@@ -3129,8 +3030,7 @@
     ref = upb_value_getptr(v);
     count = upb_value_int32(ref->count);
 
-    ok = upb_inttable_insertptr(tab, to, count);
-    CHECK_OOM(ok);
+    upb_inttable_insertptr2(tab, to, count, &upb_alloc_debugrefs);
   }
   upb_unlock();
 }
@@ -3156,21 +3056,19 @@
   assert(removed);
   newcount = upb_value_getint32(v) - 1;
   if (newcount > 0) {
-    upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount));
+    upb_inttable_insert2(ref2, (uintptr_t)subobj, upb_value_int32(newcount),
+                         &upb_alloc_debugrefs);
   }
 }
 
 static void visit(const upb_refcounted *r, upb_refcounted_visit *v,
                   void *closure) {
-  bool ok;
-
   /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know
    * exactly the set of nodes that visit() should visit.  So we verify visit()'s
    * correctness here. */
   check_state state;
   state.obj = r;
-  ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32);
-  CHECK_OOM(ok);
+  upb_inttable_init2(&state.ref2, UPB_CTYPE_INT32, &upb_alloc_debugrefs);
   getref2s(r, &state.ref2);
 
   /* This should visit any children in the ref2 table. */
@@ -3178,32 +3076,22 @@
 
   /* This assertion will fail if the visit() function missed any children. */
   assert(upb_inttable_count(&state.ref2) == 0);
-  upb_inttable_uninit(&state.ref2);
+  upb_inttable_uninit2(&state.ref2, &upb_alloc_debugrefs);
   if (r->vtbl->visit) r->vtbl->visit(r, v, closure);
 }
 
-static bool trackinit(upb_refcounted *r) {
-  r->refs = malloc(sizeof(*r->refs));
-  r->ref2s = malloc(sizeof(*r->ref2s));
-  if (!r->refs || !r->ref2s) goto err1;
-
-  if (!upb_inttable_init(r->refs, UPB_CTYPE_PTR)) goto err1;
-  if (!upb_inttable_init(r->ref2s, UPB_CTYPE_PTR)) goto err2;
-  return true;
-
-err2:
-  upb_inttable_uninit(r->refs);
-err1:
-  free(r->refs);
-  free(r->ref2s);
-  return false;
+static void trackinit(upb_refcounted *r) {
+  r->refs = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->refs));
+  r->ref2s = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->ref2s));
+  upb_inttable_init2(r->refs, UPB_CTYPE_PTR, &upb_alloc_debugrefs);
+  upb_inttable_init2(r->ref2s, UPB_CTYPE_PTR, &upb_alloc_debugrefs);
 }
 
 static void trackfree(const upb_refcounted *r) {
-  upb_inttable_uninit(r->refs);
-  upb_inttable_uninit(r->ref2s);
-  free(r->refs);
-  free(r->ref2s);
+  upb_inttable_uninit2(r->refs, &upb_alloc_debugrefs);
+  upb_inttable_uninit2(r->ref2s, &upb_alloc_debugrefs);
+  upb_free(&upb_alloc_debugrefs, r->refs);
+  upb_free(&upb_alloc_debugrefs, r->ref2s);
 }
 
 #else
@@ -3226,9 +3114,8 @@
   UPB_UNUSED(ref2);
 }
 
-static bool trackinit(upb_refcounted *r) {
+static void trackinit(upb_refcounted *r) {
   UPB_UNUSED(r);
-  return true;
 }
 
 static void trackfree(const upb_refcounted *r) {
@@ -3338,12 +3225,12 @@
 }
 
 static void tarjan_newgroup(tarjan *t) {
-  uint32_t *group = malloc(sizeof(*group));
+  uint32_t *group = upb_gmalloc(sizeof(*group));
   if (!group) oom(t);
   /* Push group and empty group leader (we'll fill in leader later). */
   if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) ||
       !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) {
-    free(group);
+    upb_gfree(group);
     oom(t);
   }
   *group = 0;
@@ -3518,7 +3405,7 @@
       if (obj == move) {
         /* Removing the last object from a group. */
         assert(*obj->group == obj->individual_count);
-        free(obj->group);
+        upb_gfree(obj->group);
       } else {
         obj->next = move->next;
         /* This may decrease to zero; we'll collect GRAY objects (if any) that
@@ -3574,7 +3461,7 @@
         /* We eagerly free() the group's count (since we can't easily determine
          * the group's remaining size it's the easiest way to ensure it gets
          * done). */
-        free(obj->group);
+        upb_gfree(obj->group);
 
         /* Visit to release ref2's (done in a separate pass since release_ref2
          * depends on o->group being unmodified so it can test merged()). */
@@ -3594,7 +3481,7 @@
   if (!ret) {
     upb_inttable_begin(&iter, &t.groups);
     for(; !upb_inttable_done(&iter); upb_inttable_next(&iter))
-      free(upb_value_getptr(upb_inttable_iter_value(&iter)));
+      upb_gfree(upb_value_getptr(upb_inttable_iter_value(&iter)));
   }
   upb_inttable_uninit(&t.groups);
 err3:
@@ -3618,7 +3505,7 @@
 
   if (merged(r, from)) return;
   *r->group += *from->group;
-  free(from->group);
+  upb_gfree(from->group);
   base = from;
 
   /* Set all refcount pointers in the "from" chain to the merged refcount.
@@ -3652,7 +3539,7 @@
   if (unrefgroup(r->group)) {
     const upb_refcounted *o;
 
-    free(r->group);
+    upb_gfree(r->group);
 
     /* In two passes, since release_ref2 needs a guarantee that any subobjs
      * are alive. */
@@ -3696,13 +3583,10 @@
   r->vtbl = vtbl;
   r->individual_count = 0;
   r->is_frozen = false;
-  r->group = malloc(sizeof(*r->group));
+  r->group = upb_gmalloc(sizeof(*r->group));
   if (!r->group) return false;
   *r->group = 0;
-  if (!trackinit(r)) {
-    free(r->group);
-    return false;
-  }
+  trackinit(r);
   upb_refcounted_ref(r, owner);
   return true;
 }
@@ -3771,8 +3655,6 @@
 }
 
 
-#include <stdlib.h>
-
 /* Fallback implementation if the shim is not specialized by the JIT. */
 #define SHIM_WRITER(type, ctype)                                              \
   bool upb_shim_set ## type (void *c, const void *hd, ctype val) {            \
@@ -3798,14 +3680,14 @@
   upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
   bool ok;
 
-  upb_shim_data *d = malloc(sizeof(*d));
+  upb_shim_data *d = upb_gmalloc(sizeof(*d));
   if (!d) return false;
   d->offset = offset;
   d->hasbit = hasbit;
 
   upb_handlerattr_sethandlerdata(&attr, d);
   upb_handlerattr_setalwaysok(&attr, true);
-  upb_handlers_addcleanup(h, d, free);
+  upb_handlers_addcleanup(h, d, upb_gfree);
 
 #define TYPE(u, l) \
   case UPB_TYPE_##u: \
@@ -3856,7 +3738,6 @@
 }
 
 
-#include <stdlib.h>
 #include <string.h>
 
 static void upb_symtab_free(upb_refcounted *r) {
@@ -3868,13 +3749,17 @@
     upb_def_unref(def, s);
   }
   upb_strtable_uninit(&s->symtab);
-  free(s);
+  upb_gfree(s);
 }
 
-
 upb_symtab *upb_symtab_new(const void *owner) {
   static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free};
-  upb_symtab *s = malloc(sizeof(*s));
+
+  upb_symtab *s = upb_gmalloc(sizeof(*s));
+  if (!s) {
+    return NULL;
+  }
+
   upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner);
   upb_strtable_init(&s->symtab, UPB_CTYPE_PTR);
   return s;
@@ -4061,6 +3946,10 @@
   upb_strtable addtab;
   upb_inttable seen;
 
+  if (n == 0 && !freeze_also) {
+    return true;
+  }
+
   assert(!upb_symtab_isfrozen(s));
   if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) {
     upb_status_seterrmsg(status, "out of memory");
@@ -4209,7 +4098,7 @@
     add_objs_size++;
   }
 
-  add_defs = malloc(sizeof(void*) * add_objs_size);
+  add_defs = upb_gmalloc(sizeof(void*) * add_objs_size);
   if (add_defs == NULL) goto oom_err;
   upb_strtable_begin(&iter, &addtab);
   for (add_n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) {
@@ -4254,7 +4143,7 @@
     success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def));
     UPB_ASSERT_VAR(success, success == true);
   }
-  free(add_objs);
+  upb_gfree(add_defs);
   return true;
 
 oom_err:
@@ -4275,7 +4164,7 @@
     }
   }
   upb_strtable_uninit(&addtab);
-  free(add_objs);
+  upb_gfree(add_defs);
   assert(!upb_ok(status));
   return false;
 }
@@ -4292,7 +4181,7 @@
   bool ret;
 
   n = upb_filedef_defcount(file);
-  defs = malloc(sizeof(*defs) * n);
+  defs = upb_gmalloc(sizeof(*defs) * n);
 
   if (defs == NULL) {
     upb_status_seterrmsg(status, "Out of memory");
@@ -4305,7 +4194,7 @@
 
   ret = symtab_add(s, defs, n, NULL, upb_filedef_upcast_mutable(file), status);
 
-  free(defs);
+  upb_gfree(defs);
   return ret;
 }
 
@@ -4347,7 +4236,6 @@
 */
 
 
-#include <stdlib.h>
 #include <string.h>
 
 #define UPB_MAXARRSIZE 16  /* 64k. */
@@ -4356,6 +4244,17 @@
 #define ARRAY_SIZE(x) \
     ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
 
+#ifdef NDEBUG
+static void upb_check_alloc(upb_table *t, upb_alloc *a) {
+  UPB_UNUSED(t);
+  UPB_UNUSED(a);
+}
+#else
+static void upb_check_alloc(upb_table *t, upb_alloc *a) {
+  assert(t->alloc == a);
+}
+#endif
+
 static const double MAX_LOAD = 0.85;
 
 /* The minimum utilization of the array part of a mixed hash/array table.  This
@@ -4373,11 +4272,11 @@
   return UPB_MIN(UPB_MAXARRSIZE, ret);
 }
 
-char *upb_strdup(const char *s) {
-  return upb_strdup2(s, strlen(s));
+char *upb_strdup(const char *s, upb_alloc *a) {
+  return upb_strdup2(s, strlen(s), a);
 }
 
-char *upb_strdup2(const char *s, size_t len) {
+char *upb_strdup2(const char *s, size_t len, upb_alloc *a) {
   size_t n;
   char *p;
 
@@ -4386,7 +4285,7 @@
   /* Always null-terminate, even if binary data; but don't rely on the input to
    * have a null-terminating byte since it may be a raw binary buffer. */
   n = len + 1;
-  p = malloc(n);
+  p = upb_malloc(a, n);
   if (p) {
     memcpy(p, s, len);
     p[len] = 0;
@@ -4434,16 +4333,20 @@
   }
 }
 
-static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) {
+static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2,
+                 upb_alloc *a) {
   size_t bytes;
 
   t->count = 0;
   t->ctype = ctype;
   t->size_lg2 = size_lg2;
   t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0;
+#ifndef NDEBUG
+  t->alloc = a;
+#endif
   bytes = upb_table_size(t) * sizeof(upb_tabent);
   if (bytes > 0) {
-    t->entries = malloc(bytes);
+    t->entries = upb_malloc(a, bytes);
     if (!t->entries) return false;
     memset(mutable_entries(t), 0, bytes);
   } else {
@@ -4452,7 +4355,10 @@
   return true;
 }
 
-static void uninit(upb_table *t) { free(mutable_entries(t)); }
+static void uninit(upb_table *t, upb_alloc *a) {
+  upb_check_alloc(t, a);
+  upb_free(a, mutable_entries(t));
+}
 
 static upb_tabent *emptyent(upb_table *t) {
   upb_tabent *e = mutable_entries(t) + upb_table_size(t);
@@ -4605,8 +4511,8 @@
 
 /* A simple "subclass" of upb_table that only adds a hash function for strings. */
 
-static upb_tabkey strcopy(lookupkey_t k2) {
-  char *str = malloc(k2.str.len + sizeof(uint32_t) + 1);
+static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) {
+  char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1);
   if (str == NULL) return 0;
   memcpy(str, &k2.str.len, sizeof(uint32_t));
   memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1);
@@ -4625,51 +4531,56 @@
   return len == k2.str.len && memcmp(str, k2.str.str, len) == 0;
 }
 
-bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) {
-  return init(&t->t, ctype, 2);
+bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) {
+  return init(&t->t, ctype, 2, a);
 }
 
-void upb_strtable_uninit(upb_strtable *t) {
+void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) {
   size_t i;
   for (i = 0; i < upb_table_size(&t->t); i++)
-    free((void*)t->t.entries[i].key);
-  uninit(&t->t);
+    upb_free(a, (void*)t->t.entries[i].key);
+  uninit(&t->t, a);
 }
 
-bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) {
+bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) {
   upb_strtable new_table;
   upb_strtable_iter i;
 
-  if (!init(&new_table.t, t->t.ctype, size_lg2))
+  upb_check_alloc(&t->t, a);
+
+  if (!init(&new_table.t, t->t.ctype, size_lg2, a))
     return false;
   upb_strtable_begin(&i, t);
   for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) {
-    upb_strtable_insert2(
+    upb_strtable_insert3(
         &new_table,
         upb_strtable_iter_key(&i),
         upb_strtable_iter_keylength(&i),
-        upb_strtable_iter_value(&i));
+        upb_strtable_iter_value(&i),
+        a);
   }
-  upb_strtable_uninit(t);
+  upb_strtable_uninit2(t, a);
   *t = new_table;
   return true;
 }
 
-bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len,
-                          upb_value v) {
+bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len,
+                          upb_value v, upb_alloc *a) {
   lookupkey_t key;
   upb_tabkey tabkey;
   uint32_t hash;
 
+  upb_check_alloc(&t->t, a);
+
   if (isfull(&t->t)) {
     /* Need to resize.  New table of double the size, add old elements to it. */
-    if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) {
+    if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) {
       return false;
     }
   }
 
   key = strkey2(k, len);
-  tabkey = strcopy(key);
+  tabkey = strcopy(key, a);
   if (tabkey == 0) return false;
 
   hash = MurmurHash2(key.str.str, key.str.len, 0);
@@ -4683,12 +4594,12 @@
   return lookup(&t->t, strkey2(key, len), v, hash, &streql);
 }
 
-bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
-                         upb_value *val) {
+bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
+                         upb_value *val, upb_alloc *alloc) {
   uint32_t hash = MurmurHash2(key, strlen(key), 0);
   upb_tabkey tabkey;
   if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) {
-    free((void*)tabkey);
+    upb_free(alloc, (void*)tabkey);
     return true;
   } else {
     return false;
@@ -4715,12 +4626,12 @@
          upb_tabent_isempty(str_tabent(i));
 }
 
-const char *upb_strtable_iter_key(upb_strtable_iter *i) {
+const char *upb_strtable_iter_key(const upb_strtable_iter *i) {
   assert(!upb_strtable_done(i));
   return upb_tabstr(str_tabent(i)->key, NULL);
 }
 
-size_t upb_strtable_iter_keylength(upb_strtable_iter *i) {
+size_t upb_strtable_iter_keylength(const upb_strtable_iter *i) {
   uint32_t len;
   assert(!upb_strtable_done(i));
   upb_tabstr(str_tabent(i)->key, &len);
@@ -4795,18 +4706,18 @@
 }
 
 bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype,
-                            size_t asize, int hsize_lg2) {
+                            size_t asize, int hsize_lg2, upb_alloc *a) {
   size_t array_bytes;
 
-  if (!init(&t->t, ctype, hsize_lg2)) return false;
+  if (!init(&t->t, ctype, hsize_lg2, a)) return false;
   /* Always make the array part at least 1 long, so that we know key 0
    * won't be in the hash part, which simplifies things. */
   t->array_size = UPB_MAX(1, asize);
   t->array_count = 0;
   array_bytes = t->array_size * sizeof(upb_value);
-  t->array = malloc(array_bytes);
+  t->array = upb_malloc(a, array_bytes);
   if (!t->array) {
-    uninit(&t->t);
+    uninit(&t->t, a);
     return false;
   }
   memset(mutable_array(t), 0xff, array_bytes);
@@ -4814,22 +4725,23 @@
   return true;
 }
 
-bool upb_inttable_init(upb_inttable *t, upb_ctype_t ctype) {
-  return upb_inttable_sizedinit(t, ctype, 0, 4);
+bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) {
+  return upb_inttable_sizedinit(t, ctype, 0, 4, a);
 }
 
-void upb_inttable_uninit(upb_inttable *t) {
-  uninit(&t->t);
-  free(mutable_array(t));
+void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) {
+  uninit(&t->t, a);
+  upb_free(a, mutable_array(t));
 }
 
-bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) {
-  /* XXX: Table can't store value (uint64_t)-1.  Need to somehow statically
-   * guarantee that this is not necessary, or fix the limitation. */
+bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
+                          upb_alloc *a) {
   upb_tabval tabval;
   tabval.val = val.val;
   UPB_UNUSED(tabval);
-  assert(upb_arrhas(tabval));
+  assert(upb_arrhas(tabval));  /* This will reject (uint64_t)-1.  Fix this. */
+
+  upb_check_alloc(&t->t, a);
 
   if (key < t->array_size) {
     assert(!upb_arrhas(t->array[key]));
@@ -4840,8 +4752,11 @@
       /* Need to resize the hash part, but we re-use the array part. */
       size_t i;
       upb_table new_table;
-      if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1))
+
+      if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1, a)) {
         return false;
+      }
+
       for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) {
         const upb_tabent *e = &t->t.entries[i];
         uint32_t hash;
@@ -4854,7 +4769,7 @@
 
       assert(t->t.count == new_table.count);
 
-      uninit(&t->t);
+      uninit(&t->t, a);
       t->t = new_table;
     }
     insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql);
@@ -4900,8 +4815,9 @@
   return success;
 }
 
-bool upb_inttable_push(upb_inttable *t, upb_value val) {
-  return upb_inttable_insert(t, upb_inttable_count(t), val);
+bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {
+  upb_check_alloc(&t->t, a);
+  return upb_inttable_insert2(t, upb_inttable_count(t), val, a);
 }
 
 upb_value upb_inttable_pop(upb_inttable *t) {
@@ -4911,8 +4827,10 @@
   return val;
 }
 
-bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val) {
-  return upb_inttable_insert(t, (uintptr_t)key, val);
+bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
+                             upb_alloc *a) {
+  upb_check_alloc(&t->t, a);
+  return upb_inttable_insert2(t, (uintptr_t)key, val, a);
 }
 
 bool upb_inttable_lookupptr(const upb_inttable *t, const void *key,
@@ -4924,7 +4842,7 @@
   return upb_inttable_remove(t, (uintptr_t)key, val);
 }
 
-void upb_inttable_compact(upb_inttable *t) {
+void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) {
   /* A power-of-two histogram of the table keys. */
   size_t counts[UPB_MAXARRSIZE + 1] = {0};
 
@@ -4936,6 +4854,8 @@
   int size_lg2;
   upb_inttable new_t;
 
+  upb_check_alloc(&t->t, a);
+
   upb_inttable_begin(&i, t);
   for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
     uintptr_t key = upb_inttable_iter_key(&i);
@@ -4968,16 +4888,16 @@
     size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
     size_t hashsize_lg2 = log2ceil(hash_size);
 
-    upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2);
+    upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2, a);
     upb_inttable_begin(&i, t);
     for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
       uintptr_t k = upb_inttable_iter_key(&i);
-      upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i));
+      upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a);
     }
     assert(new_t.array_size == arr_size);
     assert(new_t.t.size_lg2 == hashsize_lg2);
   }
-  upb_inttable_uninit(t);
+  upb_inttable_uninit2(t, a);
   *t = new_t;
 }
 
@@ -5253,6 +5173,19 @@
   memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len);
 }
 
+
+/* upb_upberr *****************************************************************/
+
+upb_errorspace upb_upberr = {"upb error"};
+
+void upb_upberr_setoom(upb_status *status) {
+  status->error_space_ = &upb_upberr;
+  upb_status_seterrmsg(status, "Out of memory");
+}
+
+
+/* upb_status *****************************************************************/
+
 void upb_status_clear(upb_status *status) {
   if (!status) return;
   status->ok_ = true;
@@ -5291,20 +5224,258 @@
   nullz(status);
 }
 
-void upb_status_seterrcode(upb_status *status, upb_errorspace *space,
-                           int code) {
-  if (!status) return;
-  status->ok_ = false;
-  status->error_space_ = space;
-  status->code_ = code;
-  space->set_message(status, code);
-}
-
 void upb_status_copy(upb_status *to, const upb_status *from) {
   if (!to) return;
   *to = *from;
 }
-/* This file was generated by upbc (the upb compiler).
+
+
+/* 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);
+  }
+}
+
+upb_alloc upb_alloc_global = {&upb_global_allocfunc};
+
+
+/* upb_arena ******************************************************************/
+
+/* Be conservative and choose 16 in case anyone is using SSE. */
+static const size_t maxalign = 16;
+
+static size_t align_up(size_t size) {
+  return ((size + maxalign - 1) / maxalign) * maxalign;
+}
+
+typedef struct mem_block {
+  struct mem_block *next;
+  size_t size;
+  size_t used;
+  bool owned;
+  /* Data follows. */
+} mem_block;
+
+typedef struct cleanup_ent {
+  struct cleanup_ent *next;
+  upb_cleanup_func *cleanup;
+  void *ud;
+} cleanup_ent;
+
+static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size,
+                               bool owned) {
+  mem_block *block = ptr;
+
+  block->next = a->block_head;
+  block->size = size;
+  block->used = align_up(sizeof(mem_block));
+  block->owned = owned;
+
+  a->block_head = block;
+
+  /* TODO(haberman): ASAN poison. */
+}
+
+
+static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) {
+  size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block);
+  mem_block *block = upb_malloc(a->block_alloc, block_size);
+
+  if (!block) {
+    return NULL;
+  }
+
+  upb_arena_addblock(a, block, block_size, true);
+  a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size);
+
+  return block;
+}
+
+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. */
+  mem_block *block = a->block_head;
+  void *ret;
+
+  if (size == 0) {
+    return NULL;  /* We are an arena, don't need individual frees. */
+  }
+
+  size = align_up(size);
+
+  /* TODO(haberman): special-case if this is a realloc of the last alloc? */
+
+  if (!block || block->size - block->used < size) {
+    /* Slow path: have to allocate a new block. */
+    block = upb_arena_allocblock(a, size);
+
+    if (!block) {
+      return NULL;  /* Out of memory. */
+    }
+  }
+
+  ret = (char*)block + block->used;
+  block->used += size;
+
+  if (oldsize > 0) {
+    memcpy(ret, ptr, oldsize);  /* Preserve existing data. */
+  }
+
+  /* TODO(haberman): ASAN unpoison. */
+
+  a->bytes_allocated += size;
+  return ret;
+}
+
+/* Public Arena API ***********************************************************/
+
+void upb_arena_init(upb_arena *a) {
+  a->alloc.func = &upb_arena_doalloc;
+  a->block_alloc = &upb_alloc_global;
+  a->bytes_allocated = 0;
+  a->next_block_size = 256;
+  a->max_block_size = 16384;
+  a->cleanup_head = NULL;
+  a->block_head = NULL;
+}
+
+void upb_arena_init2(upb_arena *a, void *mem, size_t size, upb_alloc *alloc) {
+  upb_arena_init(a);
+
+  if (size > sizeof(mem_block)) {
+    upb_arena_addblock(a, mem, size, false);
+  }
+
+  if (alloc) {
+    a->block_alloc = alloc;
+  }
+}
+
+void upb_arena_uninit(upb_arena *a) {
+  cleanup_ent *ent = a->cleanup_head;
+  mem_block *block = a->block_head;
+
+  while (ent) {
+    ent->cleanup(ent->ud);
+    ent = ent->next;
+  }
+
+  /* Must do this after running cleanup functions, because this will delete
+   * the memory we store our cleanup entries in! */
+  while (block) {
+    mem_block *next = block->next;
+
+    if (block->owned) {
+      upb_free(a->block_alloc, block);
+    }
+
+    block = next;
+  }
+}
+
+bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud) {
+  cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent));
+  if (!ent) {
+    return false;  /* Out of memory. */
+  }
+
+  ent->cleanup = func;
+  ent->ud = ud;
+  ent->next = a->cleanup_head;
+  a->cleanup_head = ent;
+
+  return true;
+}
+
+size_t upb_arena_bytesallocated(const upb_arena *a) {
+  return a->bytes_allocated;
+}
+
+
+/* Standard error functions ***************************************************/
+
+static bool default_err(void *ud, const upb_status *status) {
+  UPB_UNUSED(ud);
+  UPB_UNUSED(status);
+  return false;
+}
+
+static bool write_err_to(void *ud, const upb_status *status) {
+  upb_status *copy_to = ud;
+  upb_status_copy(copy_to, status);
+  return false;
+}
+
+
+/* upb_env ********************************************************************/
+
+void upb_env_initonly(upb_env *e) {
+  e->ok_ = true;
+  e->error_func_ = &default_err;
+  e->error_ud_ = NULL;
+}
+
+void upb_env_init(upb_env *e) {
+  upb_arena_init(&e->arena_);
+  upb_env_initonly(e);
+}
+
+void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc) {
+  upb_arena_init2(&e->arena_, mem, n, alloc);
+  upb_env_initonly(e);
+}
+
+void upb_env_uninit(upb_env *e) {
+  upb_arena_uninit(&e->arena_);
+}
+
+void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud) {
+  e->error_func_ = func;
+  e->error_ud_ = ud;
+}
+
+void upb_env_reporterrorsto(upb_env *e, upb_status *s) {
+  e->error_func_ = &write_err_to;
+  e->error_ud_ = s;
+}
+
+bool upb_env_reporterror(upb_env *e, const upb_status *status) {
+  e->ok_ = false;
+  return e->error_func_(e->error_ud_, status);
+}
+
+void *upb_env_malloc(upb_env *e, size_t size) {
+  return upb_malloc(&e->arena_.alloc, size);
+}
+
+void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) {
+  return upb_realloc(&e->arena_.alloc, ptr, oldsize, size);
+}
+
+void upb_env_free(upb_env *e, void *ptr) {
+  upb_free(&e->arena_.alloc, ptr);
+}
+
+bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) {
+  return upb_arena_addcleanup(&e->arena_, func, ud);
+}
+
+size_t upb_env_bytesallocated(const upb_env *e) {
+  return upb_arena_bytesallocated(&e->arena_);
+}
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     upb/descriptor/descriptor.proto
+ *
  * Do not edit -- your changes will be discarded when the file is
  * regenerated. */
 
@@ -5323,28 +5494,28 @@
 #endif
 
 static const upb_msgdef msgs[22] = {
-  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]),&reftables[0], &reftables[1]),
-  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]),&reftables[2], &reftables[3]),
-  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]),&reftables[4], &reftables[5]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]),&reftables[6], &reftables[7]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]),&reftables[8], &reftables[9]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]),&reftables[10], &reftables[11]),
-  UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]),&reftables[12], &reftables[13]),
-  UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]),&reftables[14], &reftables[15]),
-  UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]),&reftables[16], &reftables[17]),
-  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]),&reftables[18], &reftables[19]),
-  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]),&reftables[20], &reftables[21]),
-  UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]),&reftables[22], &reftables[23]),
-  UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]),&reftables[24], &reftables[25]),
-  UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]),&reftables[26], &reftables[27]),
-  UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]),&reftables[28], &reftables[29]),
-  UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]),&reftables[30], &reftables[31]),
-  UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]),&reftables[32], &reftables[33]),
-  UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]),&reftables[34], &reftables[35]),
-  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]),&reftables[36], &reftables[37]),
-  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]),&reftables[38], &reftables[39]),
-  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]),&reftables[40], &reftables[41]),
-  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]),&reftables[42], &reftables[43]),
+  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]), false, UPB_SYNTAX_PROTO2, &reftables[0], &reftables[1]),
+  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]), false, UPB_SYNTAX_PROTO2, &reftables[2], &reftables[3]),
+  UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]), false, UPB_SYNTAX_PROTO2, &reftables[4], &reftables[5]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]), false, UPB_SYNTAX_PROTO2, &reftables[6], &reftables[7]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]), false, UPB_SYNTAX_PROTO2, &reftables[8], &reftables[9]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]), false, UPB_SYNTAX_PROTO2, &reftables[10], &reftables[11]),
+  UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]), false, UPB_SYNTAX_PROTO2, &reftables[12], &reftables[13]),
+  UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]), false, UPB_SYNTAX_PROTO2, &reftables[14], &reftables[15]),
+  UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]), false, UPB_SYNTAX_PROTO2, &reftables[16], &reftables[17]),
+  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]), false, UPB_SYNTAX_PROTO2, &reftables[18], &reftables[19]),
+  UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]), false, UPB_SYNTAX_PROTO2, &reftables[20], &reftables[21]),
+  UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]), false, UPB_SYNTAX_PROTO2, &reftables[22], &reftables[23]),
+  UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]), false, UPB_SYNTAX_PROTO2, &reftables[24], &reftables[25]),
+  UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]), false, UPB_SYNTAX_PROTO2, &reftables[26], &reftables[27]),
+  UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]), false, UPB_SYNTAX_PROTO2, &reftables[28], &reftables[29]),
+  UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]), false, UPB_SYNTAX_PROTO2, &reftables[30], &reftables[31]),
+  UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]), false, UPB_SYNTAX_PROTO2, &reftables[32], &reftables[33]),
+  UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]), false, UPB_SYNTAX_PROTO2, &reftables[34], &reftables[35]),
+  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]), false, UPB_SYNTAX_PROTO2, &reftables[36], &reftables[37]),
+  UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]), false, UPB_SYNTAX_PROTO2, &reftables[38], &reftables[39]),
+  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]), false, UPB_SYNTAX_PROTO2, &reftables[40], &reftables[41]),
+  UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]), false, UPB_SYNTAX_PROTO2, &reftables[42], &reftables[43]),
 };
 
 static const upb_fielddef fields[105] = {
@@ -6277,7 +6448,7 @@
 };
 
 static char *upb_strndup(const char *buf, size_t n) {
-  char *ret = malloc(n + 1);
+  char *ret = upb_gmalloc(n + 1);
   if (!ret) return NULL;
   memcpy(ret, buf, n);
   ret[n] = '\0';
@@ -6291,9 +6462,12 @@
  * Caller owns a ref on the returned string. */
 static char *upb_join(const char *base, const char *name) {
   if (!base || strlen(base) == 0) {
-    return upb_strdup(name);
+    return upb_gstrdup(name);
   } else {
-    char *ret = malloc(strlen(base) + strlen(name) + 2);
+    char *ret = upb_gmalloc(strlen(base) + strlen(name) + 2);
+    if (!ret) {
+      return NULL;
+    }
     ret[0] = '\0';
     strcat(ret, base);
     strcat(ret, ".");
@@ -6303,14 +6477,20 @@
 }
 
 /* Qualify the defname for all defs starting with offset "start" with "str". */
-static void upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) {
+static bool upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) {
   size_t i;
   for (i = start; i < upb_filedef_defcount(f); i++) {
     upb_def *def = upb_filedef_mutabledef(f, i);
     char *name = upb_join(str, upb_def_fullname(def));
+    if (!name) {
+      /* Need better logic here; at this point we've qualified some names but
+       * not others. */
+      return false;
+    }
     upb_def_setfullname(def, name, NULL);
-    free(name);
+    upb_gfree(name);
   }
+  return true;
 }
 
 
@@ -6336,16 +6516,19 @@
   f->name = NULL;
 }
 
-void upb_descreader_endcontainer(upb_descreader *r) {
+bool upb_descreader_endcontainer(upb_descreader *r) {
   upb_descreader_frame *f = &r->stack[--r->stack_len];
-  upb_descreader_qualify(r->file, f->name, f->start);
-  free(f->name);
+  if (!upb_descreader_qualify(r->file, f->name, f->start)) {
+    return false;
+  }
+  upb_gfree(f->name);
   f->name = NULL;
+  return true;
 }
 
 void upb_descreader_setscopename(upb_descreader *r, char *str) {
   upb_descreader_frame *f = &r->stack[r->stack_len-1];
-  free(f->name);
+  upb_gfree(f->name);
   f->name = str;
 }
 
@@ -6372,8 +6555,7 @@
   upb_descreader *r = closure;
   UPB_UNUSED(hd);
   UPB_UNUSED(status);
-  upb_descreader_endcontainer(r);
-  return true;
+  return upb_descreader_endcontainer(r);
 }
 
 static size_t file_onname(void *closure, const void *hd, const char *buf,
@@ -6387,6 +6569,7 @@
   name = upb_strndup(buf, n);
   /* XXX: see comment at the top of the file. */
   ok = upb_filedef_setname(r->file, name, NULL);
+  upb_gfree(name);
   UPB_ASSERT_VAR(ok, ok);
   return n;
 }
@@ -6470,7 +6653,7 @@
   UPB_UNUSED(hd);
   UPB_UNUSED(handle);
   /* XXX: see comment at the top of the file. */
-  free(r->name);
+  upb_gfree(r->name);
   r->name = upb_strndup(buf, n);
   r->saw_name = true;
   return n;
@@ -6495,7 +6678,7 @@
   }
   e = upb_downcast_enumdef_mutable(upb_descreader_last(r));
   upb_enumdef_addval(e, r->name, r->number, status);
-  free(r->name);
+  upb_gfree(r->name);
   r->name = NULL;
   return true;
 }
@@ -6527,7 +6710,7 @@
   UPB_UNUSED(handle);
   /* XXX: see comment at the top of the file. */
   upb_def_setfullname(upb_descreader_last(r), fullname, NULL);
-  free(fullname);
+  upb_gfree(fullname);
   return n;
 }
 
@@ -6537,7 +6720,7 @@
   upb_descreader *r = closure;
   UPB_UNUSED(hd);
   assert(r->f);
-  free(r->default_string);
+  upb_gfree(r->default_string);
   r->default_string = NULL;
 
   /* fielddefs default to packed, but descriptors default to non-packed. */
@@ -6696,7 +6879,7 @@
 
   /* XXX: see comment at the top of the file. */
   upb_fielddef_setname(r->f, name, NULL);
-  free(name);
+  upb_gfree(name);
   return n;
 }
 
@@ -6709,7 +6892,7 @@
 
   /* XXX: see comment at the top of the file. */
   upb_fielddef_setsubdefname(r->f, name, NULL);
-  free(name);
+  upb_gfree(name);
   return n;
 }
 
@@ -6722,7 +6905,7 @@
 
   /* XXX: see comment at the top of the file. */
   upb_fielddef_setcontainingtypename(r->f, name, NULL);
-  free(name);
+  upb_gfree(name);
   return n;
 }
 
@@ -6735,7 +6918,7 @@
   /* Have to convert from string to the correct type, but we might not know the
    * type yet, so we save it as a string until the end of the field.
    * XXX: see comment at the top of the file. */
-  free(r->default_string);
+  upb_gfree(r->default_string);
   r->default_string = upb_strndup(buf, n);
   return n;
 }
@@ -6759,8 +6942,7 @@
     upb_status_seterrmsg(status, "Encountered message with no name.");
     return false;
   }
-  upb_descreader_endcontainer(r);
-  return true;
+  return upb_descreader_endcontainer(r);
 }
 
 static size_t msg_name(void *closure, const void *hd, const char *buf,
@@ -6814,6 +6996,17 @@
   return true;
 }
 
+static bool msg_onmapentry(void *closure, const void *hd, bool mapentry) {
+  upb_descreader *r = closure;
+  upb_msgdef *m = upb_descreader_top(r);
+  UPB_UNUSED(hd);
+
+  upb_msgdef_setmapentry(m, mapentry);
+  r->f = NULL;
+  return true;
+}
+
+
 
 /** Code to register handlers *************************************************/
 
@@ -6884,6 +7077,8 @@
   } else if (upbdefs_google_protobuf_FieldOptions_is(m)) {
     upb_handlers_setbool(h, F(FieldOptions, lazy), &field_onlazy, NULL);
     upb_handlers_setbool(h, F(FieldOptions, packed), &field_onpacked, NULL);
+  } else if (upbdefs_google_protobuf_MessageOptions_is(m)) {
+    upb_handlers_setbool(h, F(MessageOptions, map_entry), &msg_onmapentry, NULL);
   }
 
   assert(upb_ok(upb_handlers_status(h)));
@@ -6899,12 +7094,12 @@
     upb_filedef_unref(upb_descreader_file(r, i), &r->files);
   }
 
-  free(r->name);
+  upb_gfree(r->name);
   upb_inttable_uninit(&r->files);
-  free(r->default_string);
+  upb_gfree(r->default_string);
   while (r->stack_len > 0) {
     upb_descreader_frame *f = &r->stack[--r->stack_len];
-    free(f->name);
+    upb_gfree(f->name);
   }
 }
 
@@ -6980,8 +7175,8 @@
 #ifdef UPB_USE_JIT_X64
   upb_pbdecoder_freejit(g);
 #endif
-  free(g->bytecode);
-  free(g);
+  upb_gfree(g->bytecode);
+  upb_gfree(g);
 }
 
 static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit,
@@ -6996,7 +7191,7 @@
 }
 
 mgroup *newgroup(const void *owner) {
-  mgroup *g = malloc(sizeof(*g));
+  mgroup *g = upb_gmalloc(sizeof(*g));
   static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup};
   upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner);
   upb_inttable_init(&g->methods, UPB_CTYPE_PTR);
@@ -7016,7 +7211,7 @@
   }
 
   upb_inttable_uninit(&method->dispatch);
-  free(method);
+  upb_gfree(method);
 }
 
 static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit,
@@ -7028,7 +7223,7 @@
 static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers,
                                       mgroup *group) {
   static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod};
-  upb_pbdecodermethod *ret = malloc(sizeof(*ret));
+  upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret));
   upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret);
   upb_byteshandler_init(&ret->input_handler_);
 
@@ -7091,7 +7286,7 @@
 } compiler;
 
 static compiler *newcompiler(mgroup *group, bool lazy) {
-  compiler *ret = malloc(sizeof(*ret));
+  compiler *ret = upb_gmalloc(sizeof(*ret));
   int i;
 
   ret->group = group;
@@ -7104,7 +7299,7 @@
 }
 
 static void freecompiler(compiler *c) {
-  free(c);
+  upb_gfree(c);
 }
 
 const size_t ptr_words = sizeof(void*) / sizeof(uint32_t);
@@ -7208,7 +7403,8 @@
     size_t oldsize = g->bytecode_end - g->bytecode;
     size_t newsize = UPB_MAX(oldsize * 2, 64);
     /* TODO(haberman): handle OOM. */
-    g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t));
+    g->bytecode = upb_grealloc(g->bytecode, oldsize * sizeof(uint32_t),
+                                            newsize * sizeof(uint32_t));
     g->bytecode_end = g->bytecode + newsize;
     c->pc = g->bytecode + ofs;
   }
@@ -9073,7 +9269,6 @@
 */
 
 
-#include <stdlib.h>
 
 /* The output buffer is divided into segments; a segment is a string of data
  * that is "ready to go" -- it does not need any varint lengths inserted into
@@ -9318,12 +9513,12 @@
                     upb_handlerattr *attr) {
   uint32_t n = upb_fielddef_number(f);
 
-  tag_t *tag = malloc(sizeof(tag_t));
+  tag_t *tag = upb_gmalloc(sizeof(tag_t));
   tag->bytes = upb_vencode64((n << 3) | wt, tag->tag);
 
   upb_handlerattr_init(attr);
   upb_handlerattr_sethandlerdata(attr, tag);
-  upb_handlers_addcleanup(h, tag, free);
+  upb_handlers_addcleanup(h, tag, upb_gfree);
 }
 
 static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) {
@@ -9582,9 +9777,6 @@
 upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; }
 
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 
 upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner,
                                  upb_status *status) {
@@ -9615,7 +9807,7 @@
     goto cleanup;
   }
 
-  ret = malloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1));
+  ret = upb_gmalloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1));
 
   if (!ret) {
     goto cleanup;
@@ -9647,7 +9839,6 @@
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 
@@ -9743,14 +9934,14 @@
   va_end(args_copy);
 
   /* + 1 for NULL terminator (vsprintf() requires it even if we don't). */
-  str = malloc(len + 1);
+  str = upb_gmalloc(len + 1);
   if (!str) return false;
   written = vsprintf(str, fmt, args);
   va_end(args);
   UPB_ASSERT_VAR(written, written == len);
 
   ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL);
-  free(str);
+  upb_gfree(str);
   return ok;
 }
 
@@ -10119,12 +10310,11 @@
 ** - handling of keys/escape-sequences/etc that span input buffers.
 */
 
-#include <stdio.h>
-#include <stdint.h>
 #include <assert.h>
-#include <string.h>
-#include <stdlib.h>
 #include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
 
 
 #define UPB_JSON_MAX_DEPTH 64
@@ -11247,11 +11437,11 @@
  * final state once, when the closing '"' is seen. */
 
 
-#line 1246 "upb/json/parser.rl"
+#line 1245 "upb/json/parser.rl"
 
 
 
-#line 1158 "upb/json/parser.c"
+#line 1157 "upb/json/parser.c"
 static const char _json_actions[] = {
 	0, 1, 0, 1, 2, 1, 3, 1, 
 	5, 1, 6, 1, 7, 1, 8, 1, 
@@ -11400,7 +11590,7 @@
 static const int json_en_main = 1;
 
 
-#line 1249 "upb/json/parser.rl"
+#line 1248 "upb/json/parser.rl"
 
 size_t parse(void *closure, const void *hd, const char *buf, size_t size,
              const upb_bufhandle *handle) {
@@ -11422,7 +11612,7 @@
   capture_resume(parser, buf);
 
   
-#line 1329 "upb/json/parser.c"
+#line 1328 "upb/json/parser.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -11497,118 +11687,118 @@
 		switch ( *_acts++ )
 		{
 	case 0:
-#line 1161 "upb/json/parser.rl"
+#line 1160 "upb/json/parser.rl"
 	{ p--; {cs = stack[--top]; goto _again;} }
 	break;
 	case 1:
-#line 1162 "upb/json/parser.rl"
+#line 1161 "upb/json/parser.rl"
 	{ p--; {stack[top++] = cs; cs = 10; goto _again;} }
 	break;
 	case 2:
-#line 1166 "upb/json/parser.rl"
+#line 1165 "upb/json/parser.rl"
 	{ start_text(parser, p); }
 	break;
 	case 3:
-#line 1167 "upb/json/parser.rl"
+#line 1166 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_text(parser, p)); }
 	break;
 	case 4:
-#line 1173 "upb/json/parser.rl"
+#line 1172 "upb/json/parser.rl"
 	{ start_hex(parser); }
 	break;
 	case 5:
-#line 1174 "upb/json/parser.rl"
+#line 1173 "upb/json/parser.rl"
 	{ hexdigit(parser, p); }
 	break;
 	case 6:
-#line 1175 "upb/json/parser.rl"
+#line 1174 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_hex(parser)); }
 	break;
 	case 7:
-#line 1181 "upb/json/parser.rl"
+#line 1180 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(escape(parser, p)); }
 	break;
 	case 8:
-#line 1187 "upb/json/parser.rl"
+#line 1186 "upb/json/parser.rl"
 	{ p--; {cs = stack[--top]; goto _again;} }
 	break;
 	case 9:
-#line 1190 "upb/json/parser.rl"
+#line 1189 "upb/json/parser.rl"
 	{ {stack[top++] = cs; cs = 19; goto _again;} }
 	break;
 	case 10:
-#line 1192 "upb/json/parser.rl"
+#line 1191 "upb/json/parser.rl"
 	{ p--; {stack[top++] = cs; cs = 27; goto _again;} }
 	break;
 	case 11:
-#line 1197 "upb/json/parser.rl"
+#line 1196 "upb/json/parser.rl"
 	{ start_member(parser); }
 	break;
 	case 12:
-#line 1198 "upb/json/parser.rl"
+#line 1197 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_membername(parser)); }
 	break;
 	case 13:
-#line 1201 "upb/json/parser.rl"
+#line 1200 "upb/json/parser.rl"
 	{ end_member(parser); }
 	break;
 	case 14:
-#line 1207 "upb/json/parser.rl"
+#line 1206 "upb/json/parser.rl"
 	{ start_object(parser); }
 	break;
 	case 15:
-#line 1210 "upb/json/parser.rl"
+#line 1209 "upb/json/parser.rl"
 	{ end_object(parser); }
 	break;
 	case 16:
-#line 1216 "upb/json/parser.rl"
+#line 1215 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(start_array(parser)); }
 	break;
 	case 17:
-#line 1220 "upb/json/parser.rl"
+#line 1219 "upb/json/parser.rl"
 	{ end_array(parser); }
 	break;
 	case 18:
-#line 1225 "upb/json/parser.rl"
+#line 1224 "upb/json/parser.rl"
 	{ start_number(parser, p); }
 	break;
 	case 19:
-#line 1226 "upb/json/parser.rl"
+#line 1225 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_number(parser, p)); }
 	break;
 	case 20:
-#line 1228 "upb/json/parser.rl"
+#line 1227 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(start_stringval(parser)); }
 	break;
 	case 21:
-#line 1229 "upb/json/parser.rl"
+#line 1228 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(end_stringval(parser)); }
 	break;
 	case 22:
-#line 1231 "upb/json/parser.rl"
+#line 1230 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(parser_putbool(parser, true)); }
 	break;
 	case 23:
-#line 1233 "upb/json/parser.rl"
+#line 1232 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(parser_putbool(parser, false)); }
 	break;
 	case 24:
-#line 1235 "upb/json/parser.rl"
+#line 1234 "upb/json/parser.rl"
 	{ /* null value */ }
 	break;
 	case 25:
-#line 1237 "upb/json/parser.rl"
+#line 1236 "upb/json/parser.rl"
 	{ CHECK_RETURN_TOP(start_subobject(parser)); }
 	break;
 	case 26:
-#line 1238 "upb/json/parser.rl"
+#line 1237 "upb/json/parser.rl"
 	{ end_subobject(parser); }
 	break;
 	case 27:
-#line 1243 "upb/json/parser.rl"
+#line 1242 "upb/json/parser.rl"
 	{ p--; {cs = stack[--top]; goto _again;} }
 	break;
-#line 1515 "upb/json/parser.c"
+#line 1514 "upb/json/parser.c"
 		}
 	}
 
@@ -11621,7 +11811,7 @@
 	_out: {}
 	}
 
-#line 1270 "upb/json/parser.rl"
+#line 1269 "upb/json/parser.rl"
 
   if (p != pe) {
     upb_status_seterrf(&parser->status, "Parse error at '%.*s'\n", pe - p, p);
@@ -11662,13 +11852,13 @@
 
   /* Emit Ragel initialization of the parser. */
   
-#line 1569 "upb/json/parser.c"
+#line 1568 "upb/json/parser.c"
 	{
 	cs = json_start;
 	top = 0;
 	}
 
-#line 1310 "upb/json/parser.rl"
+#line 1309 "upb/json/parser.rl"
   p->current_state = cs;
   p->parser_top = top;
   accumulate_clear(p);
@@ -11694,12 +11884,12 @@
     upb_value val = upb_inttable_iter_value(&i);
     upb_strtable *t = upb_value_getptr(val);
     upb_strtable_uninit(t);
-    free(t);
+    upb_gfree(t);
   }
 
   upb_inttable_uninit(&method->name_tables);
 
-  free(r);
+  upb_gfree(r);
 }
 
 static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
@@ -11716,7 +11906,7 @@
   }
 
   /* TODO(haberman): handle malloc failure. */
-  t = malloc(sizeof(*t));
+  t = upb_gmalloc(sizeof(*t));
   upb_strtable_init(t, UPB_CTYPE_CONSTPTR);
   upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t));
 
@@ -11729,7 +11919,7 @@
     size_t field_len = upb_fielddef_getjsonname(f, buf, len);
     if (field_len > len) {
       size_t len2;
-      buf = realloc(buf, field_len);
+      buf = upb_grealloc(buf, 0, field_len);
       len = field_len;
       len2 = upb_fielddef_getjsonname(f, buf, len);
       UPB_ASSERT_VAR(len2, len == len2);
@@ -11748,7 +11938,7 @@
     }
   }
 
-  free(buf);
+  upb_gfree(buf);
 }
 
 /* Public API *****************************************************************/
@@ -11788,7 +11978,7 @@
                                                  const void* owner) {
   static const struct upb_refcounted_vtbl vtbl = {visit_json_parsermethod,
                                                   free_json_parsermethod};
-  upb_json_parsermethod *ret = malloc(sizeof(*ret));
+  upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret));
   upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner);
 
   ret->msg = md;
@@ -11815,8 +12005,6 @@
 */
 
 
-#include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
 #include <stdint.h>
 
@@ -11849,22 +12037,22 @@
 
 void freestrpc(void *ptr) {
   strpc *pc = ptr;
-  free(pc->ptr);
-  free(pc);
+  upb_gfree(pc->ptr);
+  upb_gfree(pc);
 }
 
 /* Convert fielddef name to JSON name and return as a string piece. */
 strpc *newstrpc(upb_handlers *h, const upb_fielddef *f,
                 bool preserve_fieldnames) {
   /* TODO(haberman): handle malloc failure. */
-  strpc *ret = malloc(sizeof(*ret));
+  strpc *ret = upb_gmalloc(sizeof(*ret));
   if (preserve_fieldnames) {
-    ret->ptr = upb_strdup(upb_fielddef_name(f));
+    ret->ptr = upb_gstrdup(upb_fielddef_name(f));
     ret->len = strlen(ret->ptr);
   } else {
     size_t len;
     ret->len = upb_fielddef_getjsonname(f, NULL, 0);
-    ret->ptr = malloc(ret->len);
+    ret->ptr = upb_gmalloc(ret->len);
     len = upb_fielddef_getjsonname(f, ret->ptr, ret->len);
     UPB_ASSERT_VAR(len, len == ret->len);
     ret->len--;  /* NULL */
@@ -12376,10 +12564,10 @@
                         const upb_fielddef *f,
                         bool preserve_fieldnames,
                         upb_handlerattr *attr) {
-  EnumHandlerData *hd = malloc(sizeof(EnumHandlerData));
+  EnumHandlerData *hd = upb_gmalloc(sizeof(EnumHandlerData));
   hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f);
   hd->keyname = newstrpc(h, f, preserve_fieldnames);
-  upb_handlers_addcleanup(h, hd, free);
+  upb_handlers_addcleanup(h, hd, upb_gfree);
   upb_handlerattr_sethandlerdata(attr, hd);
 }
 
diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h
index b005816..2faf74e 100644
--- a/ruby/ext/google/protobuf_c/upb.h
+++ b/ruby/ext/google/protobuf_c/upb.h
@@ -80,6 +80,18 @@
 #include <stdbool.h>
 #include <stddef.h>
 
+#ifdef __cplusplus
+namespace upb {
+class Allocator;
+class Arena;
+class Environment;
+class ErrorSpace;
+class Status;
+template <int N> class InlinedArena;
+template <int N> class InlinedEnvironment;
+}
+#endif
+
 /* UPB_INLINE: inline if possible, emit standalone code if required. */
 #ifdef __cplusplus
 #define UPB_INLINE inline
@@ -147,6 +159,7 @@
 #define UPB_ASSERT_STDLAYOUT(type) \
   static_assert(std::is_standard_layout<type>::value, \
                 #type " must be standard layout");
+#define UPB_FINAL final
 #else  /* !defined(UPB_CXX11) */
 #define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \
   class_name(const class_name&); \
@@ -156,6 +169,7 @@
   ~class_name(); \
   UPB_DISALLOW_COPY_AND_ASSIGN(class_name)
 #define UPB_ASSERT_STDLAYOUT(type)
+#define UPB_FINAL
 #endif
 
 /* UPB_DECLARE_TYPE()
@@ -257,6 +271,7 @@
 /* Generic function type. */
 typedef void upb_func();
 
+
 /* C++ Casts ******************************************************************/
 
 #ifdef __cplusplus
@@ -334,134 +349,18 @@
 #endif
 
 
-/* upb::reffed_ptr ************************************************************/
+/* upb::ErrorSpace ************************************************************/
 
-#ifdef __cplusplus
-
-#include <algorithm>  /* For std::swap(). */
-
-namespace upb {
-
-/* Provides RAII semantics for upb refcounted objects.  Each reffed_ptr owns a
- * ref on whatever object it points to (if any). */
-template <class T> class reffed_ptr {
- public:
-  reffed_ptr() : ptr_(NULL) {}
-
-  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
-  template <class U>
-  reffed_ptr(U* val, const void* ref_donor = NULL)
-      : ptr_(upb::upcast(val)) {
-    if (ref_donor) {
-      assert(ptr_);
-      ptr_->DonateRef(ref_donor, this);
-    } else if (ptr_) {
-      ptr_->Ref(this);
-    }
-  }
-
-  template <class U>
-  reffed_ptr(const reffed_ptr<U>& other)
-      : ptr_(upb::upcast(other.get())) {
-    if (ptr_) ptr_->Ref(this);
-  }
-
-  reffed_ptr(const reffed_ptr& other)
-      : ptr_(upb::upcast(other.get())) {
-    if (ptr_) ptr_->Ref(this);
-  }
-
-  ~reffed_ptr() { if (ptr_) ptr_->Unref(this); }
-
-  template <class U>
-  reffed_ptr& operator=(const reffed_ptr<U>& other) {
-    reset(other.get());
-    return *this;
-  }
-
-  reffed_ptr& operator=(const reffed_ptr& other) {
-    reset(other.get());
-    return *this;
-  }
-
-  /* TODO(haberman): add C++11 move construction/assignment for greater
-   * efficiency. */
-
-  void swap(reffed_ptr& other) {
-    if (ptr_ == other.ptr_) {
-      return;
-    }
-
-    if (ptr_) ptr_->DonateRef(this, &other);
-    if (other.ptr_) other.ptr_->DonateRef(&other, this);
-    std::swap(ptr_, other.ptr_);
-  }
-
-  T& operator*() const {
-    assert(ptr_);
-    return *ptr_;
-  }
-
-  T* operator->() const {
-    assert(ptr_);
-    return ptr_;
-  }
-
-  T* get() const { return ptr_; }
-
-  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
-  template <class U>
-  void reset(U* ptr = NULL, const void* ref_donor = NULL) {
-    reffed_ptr(ptr, ref_donor).swap(*this);
-  }
-
-  template <class U>
-  reffed_ptr<U> down_cast() {
-    return reffed_ptr<U>(upb::down_cast<U*>(get()));
-  }
-
-  template <class U>
-  reffed_ptr<U> dyn_cast() {
-    return reffed_ptr<U>(upb::dyn_cast<U*>(get()));
-  }
-
-  /* Plain release() is unsafe; if we were the only owner, it would leak the
-   * object.  Instead we provide this: */
-  T* ReleaseTo(const void* new_owner) {
-    T* ret = NULL;
-    ptr_->DonateRef(this, new_owner);
-    std::swap(ret, ptr_);
-    return ret;
-  }
-
- private:
-  T* ptr_;
-};
-
-}  /* namespace upb */
-
-#endif  /* __cplusplus */
-
-
-/* upb::Status ****************************************************************/
-
-#ifdef __cplusplus
-namespace upb {
-class ErrorSpace;
-class Status;
-}
-#endif
+/* A upb::ErrorSpace represents some domain of possible error values.  This lets
+ * upb::Status attach specific error codes to operations, like POSIX/C errno,
+ * Win32 error codes, etc.  Clients who want to know the very specific error
+ * code can check the error space and then know the type of the integer code.
+ *
+ * NOTE: upb::ErrorSpace is currently not used and should be considered
+ * experimental.  It is important primarily in cases where upb is performing
+ * I/O, but upb doesn't currently have any components that do this. */
 
 UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace)
-UPB_DECLARE_TYPE(upb::Status, upb_status)
-
-/* The maximum length of an error message before it will get truncated. */
-#define UPB_STATUS_MAX_MESSAGE 128
-
-/* An error callback function is used to report errors from some component.
- * The function can return "true" to indicate that the component should try
- * to recover and proceed, but this is not always possible. */
-typedef bool upb_errcb_t(void *closure, const upb_status* status);
 
 #ifdef __cplusplus
 class upb::ErrorSpace {
@@ -469,67 +368,21 @@
 struct upb_errorspace {
 #endif
   const char *name;
-  /* Should the error message in the status object according to this code. */
-  void (*set_message)(upb_status* status, int code);
 };
 
-#ifdef __cplusplus
 
-/* Object representing a success or failure status.
+/* upb::Status ****************************************************************/
+
+/* upb::Status represents a success or failure status and error message.
  * It owns no resources and allocates no memory, so it should work
  * even in OOM situations. */
+UPB_DECLARE_TYPE(upb::Status, upb_status)
 
-class upb::Status {
- public:
-  Status();
+/* The maximum length of an error message before it will get truncated. */
+#define UPB_STATUS_MAX_MESSAGE 128
 
-  /* Returns true if there is no error. */
-  bool ok() const;
+UPB_BEGIN_EXTERN_C
 
-  /* Optional error space and code, useful if the caller wants to
-   * programmatically check the specific kind of error. */
-  ErrorSpace* error_space();
-  int code() const;
-
-  const char *error_message() const;
-
-  /* The error message will be truncated if it is longer than
-   * UPB_STATUS_MAX_MESSAGE-4. */
-  void SetErrorMessage(const char* msg);
-  void SetFormattedErrorMessage(const char* fmt, ...);
-
-  /* If there is no error message already, this will use the ErrorSpace to
-   * populate the error message for this code.  The caller can still call
-   * SetErrorMessage() to give a more specific message. */
-  void SetErrorCode(ErrorSpace* space, int code);
-
-  /* Resets the status to a successful state with no message. */
-  void Clear();
-
-  void CopyFrom(const Status& other);
-
- private:
-  UPB_DISALLOW_COPY_AND_ASSIGN(Status)
-#else
-struct upb_status {
-#endif
-  bool ok_;
-
-  /* Specific status code defined by some error space (optional). */
-  int code_;
-  upb_errorspace *error_space_;
-
-  /* Error message; NULL-terminated. */
-  char msg[UPB_STATUS_MAX_MESSAGE];
-};
-
-#define UPB_STATUS_INIT {true, 0, NULL, {0}}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* The returned string is invalidated by any other call into the status. */
 const char *upb_status_errmsg(const upb_status *status);
 bool upb_ok(const upb_status *status);
 upb_errorspace *upb_status_errspace(const upb_status *status);
@@ -542,40 +395,384 @@
 void upb_status_seterrmsg(upb_status *status, const char *msg);
 void upb_status_seterrf(upb_status *status, const char *fmt, ...);
 void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args);
-void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code);
 void upb_status_copy(upb_status *to, const upb_status *from);
 
+UPB_END_EXTERN_C
+
 #ifdef __cplusplus
-}  /* extern "C" */
 
-namespace upb {
+class upb::Status {
+ public:
+  Status() { upb_status_clear(this); }
 
-/* C++ Wrappers */
-inline Status::Status() { Clear(); }
-inline bool Status::ok() const { return upb_ok(this); }
-inline const char* Status::error_message() const {
-  return upb_status_errmsg(this);
-}
-inline void Status::SetErrorMessage(const char* msg) {
-  upb_status_seterrmsg(this, msg);
-}
-inline void Status::SetFormattedErrorMessage(const char* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  upb_status_vseterrf(this, fmt, args);
-  va_end(args);
-}
-inline void Status::SetErrorCode(ErrorSpace* space, int code) {
-  upb_status_seterrcode(this, space, code);
-}
-inline void Status::Clear() { upb_status_clear(this); }
-inline void Status::CopyFrom(const Status& other) {
-  upb_status_copy(this, &other);
-}
+  /* Returns true if there is no error. */
+  bool ok() const { return upb_ok(this); }
 
-}  /* namespace upb */
+  /* Optional error space and code, useful if the caller wants to
+   * programmatically check the specific kind of error. */
+  ErrorSpace* error_space() { return upb_status_errspace(this); }
+  int error_code() const { return upb_status_errcode(this); }
 
+  /* The returned string is invalidated by any other call into the status. */
+  const char *error_message() const { return upb_status_errmsg(this); }
+
+  /* The error message will be truncated if it is longer than
+   * UPB_STATUS_MAX_MESSAGE-4. */
+  void SetErrorMessage(const char* msg) { upb_status_seterrmsg(this, msg); }
+  void SetFormattedErrorMessage(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    upb_status_vseterrf(this, fmt, args);
+    va_end(args);
+  }
+
+  /* Resets the status to a successful state with no message. */
+  void Clear() { upb_status_clear(this); }
+
+  void CopyFrom(const Status& other) { upb_status_copy(this, &other); }
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Status)
+#else
+struct upb_status {
 #endif
+  bool ok_;
+
+  /* Specific status code defined by some error space (optional). */
+  int code_;
+  upb_errorspace *error_space_;
+
+  /* TODO(haberman): add file/line of error? */
+
+  /* Error message; NULL-terminated. */
+  char msg[UPB_STATUS_MAX_MESSAGE];
+};
+
+#define UPB_STATUS_INIT {true, 0, NULL, {0}}
+
+
+/** Built-in error spaces. ****************************************************/
+
+/* Errors raised by upb that we want to be able to detect programmatically. */
+typedef enum {
+  UPB_NOMEM   /* Can't reuse ENOMEM because it is POSIX, not ISO C. */
+} upb_errcode_t;
+
+extern upb_errorspace upb_upberr;
+
+void upb_upberr_setoom(upb_status *s);
+
+/* Since errno is defined by standard C, we define an error space for it in
+ * core upb.  Other error spaces should be defined in other, platform-specific
+ * modules. */
+
+extern upb_errorspace upb_errnoerr;
+
+
+/** upb::Allocator ************************************************************/
+
+/* A upb::Allocator 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. */
+UPB_DECLARE_TYPE(upb::Allocator, 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);
+
+#ifdef __cplusplus
+
+class upb::Allocator UPB_FINAL {
+ public:
+  Allocator() {}
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Allocator)
+
+ public:
+#else
+struct upb_alloc {
+#endif  /* __cplusplus */
+  upb_alloc_func *func;
+};
+
+UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) {
+  assert(size > 0);
+  return alloc->func(alloc, NULL, 0, size);
+}
+
+UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                             size_t size) {
+  assert(size > 0);
+  return alloc->func(alloc, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) {
+  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::Allocator interface, but it would not be as efficient for the
+ * single-threaded case. */
+UPB_DECLARE_TYPE(upb::Arena, upb_arena)
+
+typedef void upb_cleanup_func(void *ud);
+
+#define UPB_ARENA_BLOCK_OVERHEAD (sizeof(size_t)*4)
+
+UPB_BEGIN_EXTERN_C
+
+void upb_arena_init(upb_arena *a);
+void upb_arena_init2(upb_arena *a, void *mem, size_t n, upb_alloc *alloc);
+void upb_arena_uninit(upb_arena *a);
+upb_alloc *upb_arena_alloc(upb_arena *a);
+bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud);
+size_t upb_arena_bytesallocated(const upb_arena *a);
+void upb_arena_setnextblocksize(upb_arena *a, size_t size);
+void upb_arena_setmaxblocksize(upb_arena *a, size_t size);
+
+UPB_END_EXTERN_C
+
+#ifdef __cplusplus
+
+class upb::Arena {
+ public:
+  /* A simple arena with no initial memory block and the default allocator. */
+  Arena() { upb_arena_init(this); }
+
+  /* Constructs an arena with the given initial block which allocates blocks
+   * with the given allocator.  The given allocator must outlive the Arena.
+   *
+   * If you pass NULL for the allocator it will default to the global allocator
+   * upb_alloc_global, and NULL/0 for the initial block will cause there to be
+   * no initial block. */
+  Arena(void *mem, size_t len, Allocator* a) {
+    upb_arena_init2(this, mem, len, a);
+  }
+
+  ~Arena() { upb_arena_uninit(this); }
+
+  /* Sets the size of the next block the Arena will request (unless the
+   * requested allocation is larger).  Each block will double in size until the
+   * max limit is reached. */
+  void SetNextBlockSize(size_t size) { upb_arena_setnextblocksize(this, size); }
+
+  /* Sets the maximum block size.  No blocks larger than this will be requested
+   * from the underlying allocator unless individual arena allocations are
+   * larger. */
+  void SetMaxBlockSize(size_t size) { upb_arena_setmaxblocksize(this, size); }
+
+  /* Allows this arena to be used as a generic allocator.
+   *
+   * The arena does not need free() calls so when using Arena as an allocator
+   * it is safe to skip them.  However they are no-ops so there is no harm in
+   * calling free() either. */
+  Allocator* allocator() { return upb_arena_alloc(this); }
+
+  /* Add a cleanup function to run when the arena is destroyed.
+   * Returns false on out-of-memory. */
+  bool AddCleanup(upb_cleanup_func* func, void* ud) {
+    return upb_arena_addcleanup(this, func, ud);
+  }
+
+  /* Total number of bytes that have been allocated.  It is undefined what
+   * Realloc() does to this counter. */
+  size_t BytesAllocated() const {
+    return upb_arena_bytesallocated(this);
+  }
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Arena)
+
+#else
+struct upb_arena {
+#endif  /* __cplusplus */
+  /* We implement the allocator interface.
+   * This must be the first member of upb_arena! */
+  upb_alloc alloc;
+
+  /* Allocator to allocate arena blocks.  We are responsible for freeing these
+   * when we are destroyed. */
+  upb_alloc *block_alloc;
+
+  size_t bytes_allocated;
+  size_t next_block_size;
+  size_t max_block_size;
+
+  /* Linked list of blocks.  Points to an arena_block, defined in env.c */
+  void *block_head;
+
+  /* Cleanup entries.  Pointer to a cleanup_ent, defined in env.c */
+  void *cleanup_head;
+
+  /* For future expansion, since the size of this struct is exposed to users. */
+  void *future1;
+  void *future2;
+};
+
+
+/* upb::Environment ***********************************************************/
+
+/* A upb::Environment provides a means for injecting malloc and an
+ * error-reporting callback into encoders/decoders.  This allows them to be
+ * independent of nearly all assumptions about their actual environment.
+ *
+ * It is also a container for allocating the encoders/decoders themselves that
+ * insulates clients from knowing their actual size.  This provides ABI
+ * compatibility even if the size of the objects change.  And this allows the
+ * structure definitions to be in the .c files instead of the .h files, making
+ * the .h files smaller and more readable.
+ *
+ * We might want to consider renaming this to "Pipeline" if/when the concept of
+ * a pipeline element becomes more formalized. */
+UPB_DECLARE_TYPE(upb::Environment, upb_env)
+
+/* A function that receives an error report from an encoder or decoder.  The
+ * callback can return true to request that the error should be recovered, but
+ * if the error is not recoverable this has no effect. */
+typedef bool upb_error_func(void *ud, const upb_status *status);
+
+UPB_BEGIN_EXTERN_C
+
+void upb_env_init(upb_env *e);
+void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc);
+void upb_env_uninit(upb_env *e);
+
+void upb_env_initonly(upb_env *e);
+
+upb_arena *upb_env_arena(upb_env *e);
+bool upb_env_ok(const upb_env *e);
+void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud);
+
+/* Convenience wrappers around the methods of the contained arena. */
+void upb_env_reporterrorsto(upb_env *e, upb_status *s);
+bool upb_env_reporterror(upb_env *e, const upb_status *s);
+void *upb_env_malloc(upb_env *e, size_t size);
+void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size);
+void upb_env_free(upb_env *e, void *ptr);
+bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud);
+size_t upb_env_bytesallocated(const upb_env *e);
+
+UPB_END_EXTERN_C
+
+#ifdef __cplusplus
+
+class upb::Environment {
+ public:
+  /* The given Arena must outlive this environment. */
+  Environment() { upb_env_initonly(this); }
+
+  Environment(void *mem, size_t len, Allocator *a) : arena_(mem, len, a) {
+    upb_env_initonly(this);
+  }
+
+  Arena* arena() { return upb_env_arena(this); }
+
+  /* Set a custom error reporting function. */
+  void SetErrorFunction(upb_error_func* func, void* ud) {
+    upb_env_seterrorfunc(this, func, ud);
+  }
+
+  /* Set the error reporting function to simply copy the status to the given
+   * status and abort. */
+  void ReportErrorsTo(Status* status) { upb_env_reporterrorsto(this, status); }
+
+  /* Returns true if all allocations and AddCleanup() calls have succeeded,
+   * and no errors were reported with ReportError() (except ones that recovered
+   * successfully). */
+  bool ok() const { return upb_env_ok(this); }
+
+  /* Reports an error to this environment's callback, returning true if
+   * the caller should try to recover. */
+  bool ReportError(const Status* status) {
+    return upb_env_reporterror(this, status);
+  }
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(Environment)
+
+#else
+struct upb_env {
+#endif  /* __cplusplus */
+  upb_arena arena_;
+  upb_error_func *error_func_;
+  void *error_ud_;
+  bool ok_;
+};
+
+
+/* upb::InlinedArena **********************************************************/
+/* upb::InlinedEnvironment ****************************************************/
+
+/* upb::InlinedArena and upb::InlinedEnvironment seed their arenas with a
+ * predefined amount of memory.  No heap memory will be allocated until the
+ * initial block is exceeded.
+ *
+ * These types only exist in C++ */
+
+#ifdef __cplusplus
+
+template <int N> class upb::InlinedArena : public upb::Arena {
+ public:
+  InlinedArena() : Arena(initial_block_, N, NULL) {}
+  explicit InlinedArena(Allocator* a) : Arena(initial_block_, N, a) {}
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(InlinedArena)
+
+  char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD];
+};
+
+template <int N> class upb::InlinedEnvironment : public upb::Environment {
+ public:
+  InlinedEnvironment() : Environment(initial_block_, N, NULL) {}
+  explicit InlinedEnvironment(Allocator *a)
+      : Environment(initial_block_, N, a) {}
+
+ private:
+  UPB_DISALLOW_COPY_AND_ASSIGN(InlinedEnvironment)
+
+  char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD];
+};
+
+#endif  /* __cplusplus */
+
+
 
 #endif  /* UPB_H_ */
 
@@ -617,10 +814,14 @@
 #endif
 
 /* Like strdup(), which isn't always available since it's not ANSI C. */
-char *upb_strdup(const char *s);
+char *upb_strdup(const char *s, upb_alloc *a);
 /* 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);
+char *upb_strdup2(const char *s, size_t len, upb_alloc *a);
+
+UPB_INLINE char *upb_gstrdup(const char *s) {
+  return upb_strdup(s, &upb_alloc_global);
+}
 
 UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val,
                                   upb_ctype_t ctype) {
@@ -797,14 +998,40 @@
    * initialize const hash tables.  Then we cast away const when we have to.
    */
   const upb_tabent *entries;
+
+#ifndef NDEBUG
+  /* This table's allocator.  We make the user pass it in to every relevant
+   * function and only use this to check it in debug mode.  We do this solely
+   * to keep upb_table as small as possible.  This might seem slightly paranoid
+   * but the plan is to use upb_table for all map fields and extension sets in
+   * a forthcoming message representation, so there could be a lot of these.
+   * If this turns out to be too annoying later, we can change it (since this
+   * is an internal-only header file). */
+  upb_alloc *alloc;
+#endif
 } upb_table;
 
+#ifdef NDEBUG
+#  define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
+     {count, mask, ctype, size_lg2, entries}
+#else
+#  ifdef UPB_DEBUG_REFS
+/* At the moment the only mutable tables we statically initialize are debug
+ * ref tables. */
+#    define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
+       {count, mask, ctype, size_lg2, entries, &upb_alloc_debugrefs}
+#  else
+#    define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
+       {count, mask, ctype, size_lg2, entries, NULL}
+#  endif
+#endif
+
 typedef struct {
   upb_table t;
 } upb_strtable;
 
 #define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \
-  {{count, mask, ctype, size_lg2, entries}}
+  {UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries)}
 
 #define UPB_EMPTY_STRTABLE_INIT(ctype)                           \
   UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL)
@@ -817,7 +1044,7 @@
 } upb_inttable;
 
 #define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \
-  {{count, mask, ctype, size_lg2, ent}, a, asize, acount}
+  {UPB_TABLE_INIT(count, mask, ctype, size_lg2, ent), a, asize, acount}
 
 #define UPB_EMPTY_INTTABLE_INIT(ctype) \
   UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0)
@@ -857,10 +1084,26 @@
 
 /* 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_ctype_t ctype);
-bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype);
-void upb_inttable_uninit(upb_inttable *table);
-void upb_strtable_uninit(upb_strtable *table);
+bool upb_inttable_init2(upb_inttable *table, upb_ctype_t ctype, upb_alloc *a);
+bool upb_strtable_init2(upb_strtable *table, upb_ctype_t ctype, upb_alloc *a);
+void upb_inttable_uninit2(upb_inttable *table, upb_alloc *a);
+void upb_strtable_uninit2(upb_strtable *table, upb_alloc *a);
+
+UPB_INLINE bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype) {
+  return upb_inttable_init2(table, ctype, &upb_alloc_global);
+}
+
+UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype) {
+  return upb_strtable_init2(table, ctype, &upb_alloc_global);
+}
+
+UPB_INLINE void upb_inttable_uninit(upb_inttable *table) {
+  upb_inttable_uninit2(table, &upb_alloc_global);
+}
+
+UPB_INLINE void upb_strtable_uninit(upb_strtable *table) {
+  upb_strtable_uninit2(table, &upb_alloc_global);
+}
 
 /* Returns the number of values in the table. */
 size_t upb_inttable_count(const upb_inttable *t);
@@ -875,9 +1118,20 @@
  *
  * 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);
-bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len,
-                          upb_value val);
+bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
+                          upb_alloc *a);
+bool upb_strtable_insert3(upb_strtable *t, const char *key, size_t len,
+                          upb_value val, upb_alloc *a);
+
+UPB_INLINE bool upb_inttable_insert(upb_inttable *t, uintptr_t key,
+                                    upb_value val) {
+  return upb_inttable_insert2(t, key, val, &upb_alloc_global);
+}
+
+UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key,
+                                     size_t len, upb_value val) {
+  return upb_strtable_insert3(t, key, len, val, &upb_alloc_global);
+}
 
 /* For NULL-terminated strings. */
 UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key,
@@ -900,8 +1154,13 @@
 /* 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);
+bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
+                          upb_value *val, upb_alloc *alloc);
+
+UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key,
+                                     size_t len, upb_value *val) {
+  return upb_strtable_remove3(t, key, len, val, &upb_alloc_global);
+}
 
 /* For NULL-terminated strings. */
 UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,
@@ -916,19 +1175,33 @@
 
 /* Handy routines for treating an inttable like a stack.  May not be mixed with
  * other insert/remove calls. */
-bool upb_inttable_push(upb_inttable *t, upb_value val);
+bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a);
 upb_value upb_inttable_pop(upb_inttable *t);
 
+UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) {
+  return upb_inttable_push2(t, val, &upb_alloc_global);
+}
+
 /* Convenience routines for inttables with pointer keys. */
-bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val);
+bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
+                             upb_alloc *a);
 bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val);
 bool upb_inttable_lookupptr(
     const upb_inttable *t, const void *key, upb_value *val);
 
+UPB_INLINE bool upb_inttable_insertptr(upb_inttable *t, const void *key,
+                                       upb_value val) {
+  return upb_inttable_insertptr2(t, key, val, &upb_alloc_global);
+}
+
 /* 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);
+void upb_inttable_compact2(upb_inttable *t, upb_alloc *a);
+
+UPB_INLINE void upb_inttable_compact(upb_inttable *t) {
+  upb_inttable_compact2(t, &upb_alloc_global);
+}
 
 /* A special-case inlinable version of the lookup routine for 32-bit
  * integers. */
@@ -957,7 +1230,7 @@
 }
 
 /* Exposed for testing only. */
-bool upb_strtable_resize(upb_strtable *t, size_t size_lg2);
+bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a);
 
 /* Iterators ******************************************************************/
 
@@ -1002,8 +1275,8 @@
 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);
-const char *upb_strtable_iter_key(upb_strtable_iter *i);
-size_t upb_strtable_iter_keylength(upb_strtable_iter *i);
+const char *upb_strtable_iter_key(const upb_strtable_iter *i);
+size_t upb_strtable_iter_keylength(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,
@@ -1056,7 +1329,10 @@
 /* #define UPB_DEBUG_REFS */
 
 #ifdef __cplusplus
-namespace upb { class RefCounted; }
+namespace upb {
+class RefCounted;
+template <class T> class reffed_ptr;
+}
 #endif
 
 UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted)
@@ -1124,10 +1400,12 @@
 };
 
 #ifdef UPB_DEBUG_REFS
-#define UPB_REFCOUNT_INIT(refs, ref2s) \
-    {&static_refcount, NULL, NULL, 0, true, refs, ref2s}
+extern upb_alloc upb_alloc_debugrefs;
+#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \
+    {&static_refcount, NULL, vtbl, 0, true, refs, ref2s}
 #else
-#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true}
+#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \
+    {&static_refcount, NULL, vtbl, 0, true}
 #endif
 
 UPB_BEGIN_EXTERN_C
@@ -1260,6 +1538,111 @@
 }  /* namespace upb */
 #endif
 
+
+/* upb::reffed_ptr ************************************************************/
+
+#ifdef __cplusplus
+
+#include <algorithm>  /* For std::swap(). */
+
+/* Provides RAII semantics for upb refcounted objects.  Each reffed_ptr owns a
+ * ref on whatever object it points to (if any). */
+template <class T> class upb::reffed_ptr {
+ public:
+  reffed_ptr() : ptr_(NULL) {}
+
+  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
+  template <class U>
+  reffed_ptr(U* val, const void* ref_donor = NULL)
+      : ptr_(upb::upcast(val)) {
+    if (ref_donor) {
+      assert(ptr_);
+      ptr_->DonateRef(ref_donor, this);
+    } else if (ptr_) {
+      ptr_->Ref(this);
+    }
+  }
+
+  template <class U>
+  reffed_ptr(const reffed_ptr<U>& other)
+      : ptr_(upb::upcast(other.get())) {
+    if (ptr_) ptr_->Ref(this);
+  }
+
+  reffed_ptr(const reffed_ptr& other)
+      : ptr_(upb::upcast(other.get())) {
+    if (ptr_) ptr_->Ref(this);
+  }
+
+  ~reffed_ptr() { if (ptr_) ptr_->Unref(this); }
+
+  template <class U>
+  reffed_ptr& operator=(const reffed_ptr<U>& other) {
+    reset(other.get());
+    return *this;
+  }
+
+  reffed_ptr& operator=(const reffed_ptr& other) {
+    reset(other.get());
+    return *this;
+  }
+
+  /* TODO(haberman): add C++11 move construction/assignment for greater
+   * efficiency. */
+
+  void swap(reffed_ptr& other) {
+    if (ptr_ == other.ptr_) {
+      return;
+    }
+
+    if (ptr_) ptr_->DonateRef(this, &other);
+    if (other.ptr_) other.ptr_->DonateRef(&other, this);
+    std::swap(ptr_, other.ptr_);
+  }
+
+  T& operator*() const {
+    assert(ptr_);
+    return *ptr_;
+  }
+
+  T* operator->() const {
+    assert(ptr_);
+    return ptr_;
+  }
+
+  T* get() const { return ptr_; }
+
+  /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
+  template <class U>
+  void reset(U* ptr = NULL, const void* ref_donor = NULL) {
+    reffed_ptr(ptr, ref_donor).swap(*this);
+  }
+
+  template <class U>
+  reffed_ptr<U> down_cast() {
+    return reffed_ptr<U>(upb::down_cast<U*>(get()));
+  }
+
+  template <class U>
+  reffed_ptr<U> dyn_cast() {
+    return reffed_ptr<U>(upb::dyn_cast<U*>(get()));
+  }
+
+  /* Plain release() is unsafe; if we were the only owner, it would leak the
+   * object.  Instead we provide this: */
+  T* ReleaseTo(const void* new_owner) {
+    T* ret = NULL;
+    ptr_->DonateRef(this, new_owner);
+    std::swap(ret, ptr_);
+    return ret;
+  }
+
+ private:
+  T* ptr_;
+};
+
+#endif  /* __cplusplus */
+
 #endif  /* UPB_REFCOUNT_H_ */
 
 #ifdef __cplusplus
@@ -1511,6 +1894,11 @@
   UPB_DESCRIPTOR_TYPE_SINT64   = 18
 } upb_descriptortype_t;
 
+typedef enum {
+  UPB_SYNTAX_PROTO2 = 2,
+  UPB_SYNTAX_PROTO3 = 3
+} upb_syntax_t;
+
 /* Maximum field number allowed for FieldDefs.  This is an inherent limit of the
  * protobuf wire format. */
 #define UPB_MAX_FIELDNUMBER ((1 << 29) - 1)
@@ -1902,6 +2290,10 @@
 typedef upb_inttable_iter upb_msg_field_iter;
 typedef upb_strtable_iter upb_msg_oneof_iter;
 
+/* Well-known field tag numbers for map-entry messages. */
+#define UPB_MAPENTRY_KEY   1
+#define UPB_MAPENTRY_VALUE 2
+
 #ifdef __cplusplus
 
 /* Structure that describes a single .proto message type.
@@ -1960,6 +2352,11 @@
   bool AddOneof(OneofDef* o, Status* s);
   bool AddOneof(const reffed_ptr<OneofDef>& o, Status* s);
 
+  upb_syntax_t syntax() const;
+
+  /* Returns false if we don't support this syntax value. */
+  bool set_syntax(upb_syntax_t syntax);
+
   /* Set this to false to indicate that primitive fields should not have
    * explicit presence information associated with them.  This will affect all
    * fields added to this message.  Defaults to true. */
@@ -2150,15 +2547,20 @@
 
 bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status);
 
+upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner);
 const char *upb_msgdef_fullname(const upb_msgdef *m);
 const char *upb_msgdef_name(const upb_msgdef *m);
-bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s);
+int upb_msgdef_numoneofs(const upb_msgdef *m);
+upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m);
 
-upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner);
 bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor,
                          upb_status *s);
 bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor,
                          upb_status *s);
+bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s);
+void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry);
+bool upb_msgdef_mapentry(const upb_msgdef *m);
+bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax);
 
 /* Field lookup in a couple of different variations:
  *   - itof = int to field
@@ -2200,18 +2602,21 @@
   return (upb_oneofdef *)upb_msgdef_ntoo(m, name, len);
 }
 
-void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry);
-bool upb_msgdef_mapentry(const upb_msgdef *m);
+/* Lookup of either field or oneof by name.  Returns whether either was found.
+ * If the return is true, then the found def will be set, and the non-found
+ * one set to NULL. */
+bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len,
+                           const upb_fielddef **f, const upb_oneofdef **o);
 
-/* Well-known field tag numbers for map-entry messages. */
-#define UPB_MAPENTRY_KEY   1
-#define UPB_MAPENTRY_VALUE 2
+UPB_INLINE bool upb_msgdef_lookupnamez(const upb_msgdef *m, const char *name,
+                                       const upb_fielddef **f,
+                                       const upb_oneofdef **o) {
+  return upb_msgdef_lookupname(m, name, strlen(name), f, o);
+}
 
-const upb_oneofdef *upb_msgdef_findoneof(const upb_msgdef *m,
-                                          const char *name);
-int upb_msgdef_numoneofs(const upb_msgdef *m);
-
-/* upb_msg_field_iter i;
+/* Iteration over fields and oneofs.  For example:
+ *
+ * upb_msg_field_iter i;
  * for(upb_msg_field_begin(&i, m);
  *     !upb_msg_field_done(&i);
  *     upb_msg_field_next(&i)) {
@@ -2523,11 +2928,6 @@
 
 /* upb::FileDef ***************************************************************/
 
-typedef enum {
-  UPB_SYNTAX_PROTO2 = 2,
-  UPB_SYNTAX_PROTO3 = 3
-} upb_syntax_t;
-
 #ifdef __cplusplus
 
 /* Class that represents a .proto file with some things defined in it.
@@ -2890,12 +3290,18 @@
 inline const char *MessageDef::name() const {
   return upb_msgdef_name(this);
 }
+inline upb_syntax_t MessageDef::syntax() const {
+  return upb_msgdef_syntax(this);
+}
 inline bool MessageDef::set_full_name(const char* fullname, Status* s) {
   return upb_msgdef_setfullname(this, fullname, s);
 }
 inline bool MessageDef::set_full_name(const std::string& fullname, Status* s) {
   return upb_msgdef_setfullname(this, upb_safecstr(fullname), s);
 }
+inline bool MessageDef::set_syntax(upb_syntax_t syntax) {
+  return upb_msgdef_setsyntax(this, syntax);
+}
 inline bool MessageDef::Freeze(Status* status) {
   return upb_msgdef_freeze(this, status);
 }
@@ -3309,8 +3715,8 @@
   bool came_from_user;
 };
 
-#define UPB_DEF_INIT(name, type, refs, ref2s) \
-    { UPB_REFCOUNT_INIT(refs, ref2s), name, NULL, type, false }
+#define UPB_DEF_INIT(name, type, vtbl, refs, ref2s) \
+    { UPB_REFCOUNT_INIT(vtbl, refs, ref2s), name, NULL, type, false }
 
 
 /* upb_fielddef ***************************************************************/
@@ -3350,12 +3756,14 @@
   uint32_t index_;
 };
 
+extern const struct upb_refcounted_vtbl upb_fielddef_vtbl;
+
 #define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy,   \
                           packed, name, num, msgdef, subdef, selector_base,    \
                           index, defaultval, refs, ref2s)                      \
   {                                                                            \
-    UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef},      \
-        {subdef}, NULL, false, false,                                          \
+    UPB_DEF_INIT(name, UPB_DEF_FIELD, &upb_fielddef_vtbl, refs, ref2s),        \
+        defaultval, {msgdef}, {subdef}, NULL, false, false,                    \
         type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES, true, is_extension, \
         lazy, packed, intfmt, tagdelim, type, label, num, selector_base, index \
   }
@@ -3371,32 +3779,26 @@
 
   /* Tables for looking up fields by number and name. */
   upb_inttable itof;  /* int to field */
-  upb_strtable ntof;  /* name to field */
+  upb_strtable ntof;  /* name to field/oneof */
 
-  /* Tables for looking up oneofs by name. */
-  upb_strtable ntoo;  /* name to oneof */
-
-  /* Is this a map-entry message?
-   * TODO: set this flag properly for static descriptors; regenerate
-   * descriptor.upb.c. */
+  /* Is this a map-entry message? */
   bool map_entry;
 
-  /* Whether this message has proto2 or proto3 semantics.
-   * TODO: set this flag properly for static descriptors; regenerate
-   * descriptor.upb.c. */
+  /* Whether this message has proto2 or proto3 semantics. */
   upb_syntax_t syntax;
 
   /* TODO(haberman): proper extension ranges (there can be multiple). */
 };
 
+extern const struct upb_refcounted_vtbl upb_msgdef_vtbl;
+
 /* TODO: also support static initialization of the oneofs table. This will be
  * needed if we compile in descriptors that contain oneofs. */
 #define UPB_MSGDEF_INIT(name, selector_count, submsg_field_count, itof, ntof, \
-                        refs, ref2s)                                          \
+                        map_entry, syntax, refs, ref2s)                       \
   {                                                                           \
-    UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count,             \
-        submsg_field_count, itof, ntof,                                       \
-        UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false, true                   \
+    UPB_DEF_INIT(name, UPB_DEF_MSG, &upb_fielddef_vtbl, refs, ref2s),         \
+        selector_count, submsg_field_count, itof, ntof, map_entry, syntax     \
   }
 
 
@@ -3410,8 +3812,11 @@
   int32_t defaultval;
 };
 
+extern const struct upb_refcounted_vtbl upb_enumdef_vtbl;
+
 #define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \
-  { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval }
+  { UPB_DEF_INIT(name, UPB_DEF_ENUM, &upb_enumdef_vtbl, refs, ref2s), ntoi,    \
+    iton, defaultval }
 
 
 /* upb_oneofdef ***************************************************************/
@@ -3425,8 +3830,10 @@
   const upb_msgdef *parent;
 };
 
+extern const struct upb_refcounted_vtbl upb_oneofdef_vtbl;
+
 #define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \
-  { UPB_REFCOUNT_INIT(refs, ref2s), name, ntof, itof }
+  { UPB_REFCOUNT_INIT(&upb_oneofdef_vtbl, refs, ref2s), name, ntof, itof }
 
 
 /* upb_symtab *****************************************************************/
@@ -3437,9 +3844,6 @@
   upb_strtable symtab;
 };
 
-#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \
-  { UPB_REFCOUNT_INIT(refs, ref2s), symtab }
-
 struct upb_filedef {
   upb_refcounted base;
 
@@ -3451,6 +3855,8 @@
   upb_inttable deps;
 };
 
+extern const struct upb_refcounted_vtbl upb_filedef_vtbl;
+
 #endif  /* UPB_STATICINIT_H_ */
 /*
 ** upb::Handlers (upb_handlers)
@@ -5392,267 +5798,6 @@
 
 #endif  /* UPB_HANDLERS_H */
 /*
-** upb::Environment (upb_env)
-**
-** A upb::Environment provides a means for injecting malloc and an
-** error-reporting callback into encoders/decoders.  This allows them to be
-** independent of nearly all assumptions about their actual environment.
-**
-** It is also a container for allocating the encoders/decoders themselves that
-** insulates clients from knowing their actual size.  This provides ABI
-** compatibility even if the size of the objects change.  And this allows the
-** structure definitions to be in the .c files instead of the .h files, making
-** the .h files smaller and more readable.
-*/
-
-
-#ifndef UPB_ENV_H_
-#define UPB_ENV_H_
-
-#ifdef __cplusplus
-namespace upb {
-class Environment;
-class SeededAllocator;
-}
-#endif
-
-UPB_DECLARE_TYPE(upb::Environment, upb_env)
-UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc)
-
-typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size);
-typedef void upb_cleanup_func(void *ud);
-typedef bool upb_error_func(void *ud, const upb_status *status);
-
-#ifdef __cplusplus
-
-/* An environment is *not* thread-safe. */
-class upb::Environment {
- public:
-  Environment();
-  ~Environment();
-
-  /* Set a custom memory allocation function for the environment.  May ONLY
-   * be called before any calls to Malloc()/Realloc()/AddCleanup() below.
-   * If this is not called, the system realloc() function will be used.
-   * The given user pointer "ud" will be passed to the allocation function.
-   *
-   * The allocation function will not receive corresponding "free" calls.  it
-   * must ensure that the memory is valid for the lifetime of the Environment,
-   * but it may be reclaimed any time thereafter.  The likely usage is that
-   * "ud" points to a stateful allocator, and that the allocator frees all
-   * memory, arena-style, when it is destroyed.  In this case the allocator must
-   * outlive the Environment.  Another possibility is that the allocation
-   * function returns GC-able memory that is guaranteed to be GC-rooted for the
-   * life of the Environment. */
-  void SetAllocationFunction(upb_alloc_func* alloc, void* ud);
-
-  template<class T>
-  void SetAllocator(T* allocator) {
-    SetAllocationFunction(allocator->GetAllocationFunction(), allocator);
-  }
-
-  /* Set a custom error reporting function. */
-  void SetErrorFunction(upb_error_func* func, void* ud);
-
-  /* Set the error reporting function to simply copy the status to the given
-   * status and abort. */
-  void ReportErrorsTo(Status* status);
-
-  /* Returns true if all allocations and AddCleanup() calls have succeeded,
-   * and no errors were reported with ReportError() (except ones that recovered
-   * successfully). */
-  bool ok() const;
-
-  /* Functions for use by encoders/decoders. **********************************/
-
-  /* Reports an error to this environment's callback, returning true if
-   * the caller should try to recover. */
-  bool ReportError(const Status* status);
-
-  /* Allocate memory.  Uses the environment's allocation function.
-   *
-   * There is no need to free(). All memory will be freed automatically, but is
-   * guaranteed to outlive the Environment. */
-  void* Malloc(size_t size);
-
-  /* Reallocate memory.  Preserves "oldsize" bytes from the existing buffer
-   * Requires: oldsize <= existing_size.
-   *
-   * TODO(haberman): should we also enforce that oldsize <= size? */
-  void* Realloc(void* ptr, size_t oldsize, size_t size);
-
-  /* Add a cleanup function to run when the environment is destroyed.
-   * Returns false on out-of-memory.
-   *
-   * The first call to AddCleanup() after SetAllocationFunction() is guaranteed
-   * to return true -- this makes it possible to robustly set a cleanup handler
-   * for a custom allocation function. */
-  bool AddCleanup(upb_cleanup_func* func, void* ud);
-
-  /* Total number of bytes that have been allocated.  It is undefined what
-   * Realloc() does to this counter. */
-  size_t BytesAllocated() const;
-
- private:
-  UPB_DISALLOW_COPY_AND_ASSIGN(Environment)
-
-#else
-struct upb_env {
-#endif  /* __cplusplus */
-
-  bool ok_;
-  size_t bytes_allocated;
-
-  /* Alloc function. */
-  upb_alloc_func *alloc;
-  void *alloc_ud;
-
-  /* Error-reporting function. */
-  upb_error_func *err;
-  void *err_ud;
-
-  /* Userdata for default alloc func. */
-  void *default_alloc_ud;
-
-  /* Cleanup entries.  Pointer to a cleanup_ent, defined in env.c */
-  void *cleanup_head;
-
-  /* For future expansion, since the size of this struct is exposed to users. */
-  void *future1;
-  void *future2;
-};
-
-UPB_BEGIN_EXTERN_C
-
-void upb_env_init(upb_env *e);
-void upb_env_uninit(upb_env *e);
-void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud);
-void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud);
-void upb_env_reporterrorsto(upb_env *e, upb_status *status);
-bool upb_env_ok(const upb_env *e);
-bool upb_env_reporterror(upb_env *e, const upb_status *status);
-void *upb_env_malloc(upb_env *e, size_t size);
-void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size);
-bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud);
-size_t upb_env_bytesallocated(const upb_env *e);
-
-UPB_END_EXTERN_C
-
-#ifdef __cplusplus
-
-/* An allocator that allocates from an initial memory region (likely the stack)
- * before falling back to another allocator. */
-class upb::SeededAllocator {
- public:
-  SeededAllocator(void *mem, size_t len);
-  ~SeededAllocator();
-
-  /* Set a custom fallback memory allocation function for the allocator, to use
-   * once the initial region runs out.
-   *
-   * May ONLY be called before GetAllocationFunction().  If this is not
-   * called, the system realloc() will be the fallback allocator. */
-  void SetFallbackAllocator(upb_alloc_func *alloc, void *ud);
-
-  /* Gets the allocation function for this allocator. */
-  upb_alloc_func* GetAllocationFunction();
-
- private:
-  UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator)
-
-#else
-struct upb_seededalloc {
-#endif  /* __cplusplus */
-
-  /* Fallback alloc function.  */
-  upb_alloc_func *alloc;
-  upb_cleanup_func *alloc_cleanup;
-  void *alloc_ud;
-  bool need_cleanup;
-  bool returned_allocfunc;
-
-  /* Userdata for default alloc func. */
-  void *default_alloc_ud;
-
-  /* Pointers for the initial memory region. */
-  char *mem_base;
-  char *mem_ptr;
-  char *mem_limit;
-
-  /* For future expansion, since the size of this struct is exposed to users. */
-  void *future1;
-  void *future2;
-};
-
-UPB_BEGIN_EXTERN_C
-
-void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len);
-void upb_seededalloc_uninit(upb_seededalloc *a);
-void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func,
-                                      void *ud);
-upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a);
-
-UPB_END_EXTERN_C
-
-#ifdef __cplusplus
-
-namespace upb {
-
-inline Environment::Environment() {
-  upb_env_init(this);
-}
-inline Environment::~Environment() {
-  upb_env_uninit(this);
-}
-inline void Environment::SetAllocationFunction(upb_alloc_func *alloc,
-                                               void *ud) {
-  upb_env_setallocfunc(this, alloc, ud);
-}
-inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) {
-  upb_env_seterrorfunc(this, func, ud);
-}
-inline void Environment::ReportErrorsTo(Status* status) {
-  upb_env_reporterrorsto(this, status);
-}
-inline bool Environment::ok() const {
-  return upb_env_ok(this);
-}
-inline bool Environment::ReportError(const Status* status) {
-  return upb_env_reporterror(this, status);
-}
-inline void *Environment::Malloc(size_t size) {
-  return upb_env_malloc(this, size);
-}
-inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) {
-  return upb_env_realloc(this, ptr, oldsize, size);
-}
-inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) {
-  return upb_env_addcleanup(this, func, ud);
-}
-inline size_t Environment::BytesAllocated() const {
-  return upb_env_bytesallocated(this);
-}
-
-inline SeededAllocator::SeededAllocator(void *mem, size_t len) {
-  upb_seededalloc_init(this, mem, len);
-}
-inline SeededAllocator::~SeededAllocator() {
-  upb_seededalloc_uninit(this);
-}
-inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc,
-                                                  void *ud) {
-  upb_seededalloc_setfallbackalloc(this, alloc, ud);
-}
-inline upb_alloc_func *SeededAllocator::GetAllocationFunction() {
-  return upb_seededalloc_getallocfunc(this);
-}
-
-}  /* namespace upb */
-
-#endif  /* __cplusplus */
-
-#endif  /* UPB_ENV_H_ */
-/*
 ** upb::Sink (upb_sink)
 ** upb::BytesSink (upb_bytessink)
 **
@@ -6529,7 +6674,11 @@
  * actually storing protobufs.  It only contains *defs* which
  * let you reflect over a protobuf *schema*.
  */
-/* This file was generated by upbc (the upb compiler).
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     upb/descriptor/descriptor.proto
+ *
  * Do not edit -- your changes will be discarded when the file is
  * regenerated. */
 
@@ -6818,347 +6967,347 @@
 namespace google {
 namespace protobuf {
 
-class DescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class DescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  DescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  DescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_DescriptorProto_is(m));
   }
 
   static DescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_get(&m);
     return DescriptorProto(m, &m);
   }
 
-  class ExtensionRange : public upb::reffed_ptr<const upb::MessageDef> {
+  class ExtensionRange : public ::upb::reffed_ptr<const ::upb::MessageDef> {
    public:
-    ExtensionRange(const upb::MessageDef* m, const void *ref_donor = NULL)
+    ExtensionRange(const ::upb::MessageDef* m, const void *ref_donor = NULL)
         : reffed_ptr(m, ref_donor) {
       assert(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m));
     }
 
     static ExtensionRange get() {
-      const upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(&m);
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(&m);
       return ExtensionRange(m, &m);
     }
   };
 
-  class ReservedRange : public upb::reffed_ptr<const upb::MessageDef> {
+  class ReservedRange : public ::upb::reffed_ptr<const ::upb::MessageDef> {
    public:
-    ReservedRange(const upb::MessageDef* m, const void *ref_donor = NULL)
+    ReservedRange(const ::upb::MessageDef* m, const void *ref_donor = NULL)
         : reffed_ptr(m, ref_donor) {
       assert(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m));
     }
 
     static ReservedRange get() {
-      const upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(&m);
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(&m);
       return ReservedRange(m, &m);
     }
   };
 };
 
-class EnumDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class EnumDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  EnumDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  EnumDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_EnumDescriptorProto_is(m));
   }
 
   static EnumDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_EnumDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumDescriptorProto_get(&m);
     return EnumDescriptorProto(m, &m);
   }
 };
 
-class EnumOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class EnumOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  EnumOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  EnumOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_EnumOptions_is(m));
   }
 
   static EnumOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_EnumOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumOptions_get(&m);
     return EnumOptions(m, &m);
   }
 };
 
-class EnumValueDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class EnumValueDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  EnumValueDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  EnumValueDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m));
   }
 
   static EnumValueDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_EnumValueDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueDescriptorProto_get(&m);
     return EnumValueDescriptorProto(m, &m);
   }
 };
 
-class EnumValueOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class EnumValueOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  EnumValueOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  EnumValueOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_EnumValueOptions_is(m));
   }
 
   static EnumValueOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_EnumValueOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueOptions_get(&m);
     return EnumValueOptions(m, &m);
   }
 };
 
-class FieldDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class FieldDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  FieldDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  FieldDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m));
   }
 
   static FieldDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_FieldDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldDescriptorProto_get(&m);
     return FieldDescriptorProto(m, &m);
   }
 
-  class Label : public upb::reffed_ptr<const upb::EnumDef> {
+  class Label : public ::upb::reffed_ptr<const ::upb::EnumDef> {
    public:
-    Label(const upb::EnumDef* e, const void *ref_donor = NULL)
+    Label(const ::upb::EnumDef* e, const void *ref_donor = NULL)
         : reffed_ptr(e, ref_donor) {
       assert(upbdefs_google_protobuf_FieldDescriptorProto_Label_is(e));
     }
     static Label get() {
-      const upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Label_get(&e);
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Label_get(&e);
       return Label(e, &e);
     }
   };
 
-  class Type : public upb::reffed_ptr<const upb::EnumDef> {
+  class Type : public ::upb::reffed_ptr<const ::upb::EnumDef> {
    public:
-    Type(const upb::EnumDef* e, const void *ref_donor = NULL)
+    Type(const ::upb::EnumDef* e, const void *ref_donor = NULL)
         : reffed_ptr(e, ref_donor) {
       assert(upbdefs_google_protobuf_FieldDescriptorProto_Type_is(e));
     }
     static Type get() {
-      const upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Type_get(&e);
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Type_get(&e);
       return Type(e, &e);
     }
   };
 };
 
-class FieldOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class FieldOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  FieldOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  FieldOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_FieldOptions_is(m));
   }
 
   static FieldOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_FieldOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldOptions_get(&m);
     return FieldOptions(m, &m);
   }
 
-  class CType : public upb::reffed_ptr<const upb::EnumDef> {
+  class CType : public ::upb::reffed_ptr<const ::upb::EnumDef> {
    public:
-    CType(const upb::EnumDef* e, const void *ref_donor = NULL)
+    CType(const ::upb::EnumDef* e, const void *ref_donor = NULL)
         : reffed_ptr(e, ref_donor) {
       assert(upbdefs_google_protobuf_FieldOptions_CType_is(e));
     }
     static CType get() {
-      const upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_CType_get(&e);
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_CType_get(&e);
       return CType(e, &e);
     }
   };
 
-  class JSType : public upb::reffed_ptr<const upb::EnumDef> {
+  class JSType : public ::upb::reffed_ptr<const ::upb::EnumDef> {
    public:
-    JSType(const upb::EnumDef* e, const void *ref_donor = NULL)
+    JSType(const ::upb::EnumDef* e, const void *ref_donor = NULL)
         : reffed_ptr(e, ref_donor) {
       assert(upbdefs_google_protobuf_FieldOptions_JSType_is(e));
     }
     static JSType get() {
-      const upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_JSType_get(&e);
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_JSType_get(&e);
       return JSType(e, &e);
     }
   };
 };
 
-class FileDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class FileDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  FileDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  FileDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_FileDescriptorProto_is(m));
   }
 
   static FileDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorProto_get(&m);
     return FileDescriptorProto(m, &m);
   }
 };
 
-class FileDescriptorSet : public upb::reffed_ptr<const upb::MessageDef> {
+class FileDescriptorSet : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  FileDescriptorSet(const upb::MessageDef* m, const void *ref_donor = NULL)
+  FileDescriptorSet(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_FileDescriptorSet_is(m));
   }
 
   static FileDescriptorSet get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorSet_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorSet_get(&m);
     return FileDescriptorSet(m, &m);
   }
 };
 
-class FileOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class FileOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  FileOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  FileOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_FileOptions_is(m));
   }
 
   static FileOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_FileOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_FileOptions_get(&m);
     return FileOptions(m, &m);
   }
 
-  class OptimizeMode : public upb::reffed_ptr<const upb::EnumDef> {
+  class OptimizeMode : public ::upb::reffed_ptr<const ::upb::EnumDef> {
    public:
-    OptimizeMode(const upb::EnumDef* e, const void *ref_donor = NULL)
+    OptimizeMode(const ::upb::EnumDef* e, const void *ref_donor = NULL)
         : reffed_ptr(e, ref_donor) {
       assert(upbdefs_google_protobuf_FileOptions_OptimizeMode_is(e));
     }
     static OptimizeMode get() {
-      const upb::EnumDef* e = upbdefs_google_protobuf_FileOptions_OptimizeMode_get(&e);
+      const ::upb::EnumDef* e = upbdefs_google_protobuf_FileOptions_OptimizeMode_get(&e);
       return OptimizeMode(e, &e);
     }
   };
 };
 
-class MessageOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class MessageOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  MessageOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  MessageOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_MessageOptions_is(m));
   }
 
   static MessageOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_MessageOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_MessageOptions_get(&m);
     return MessageOptions(m, &m);
   }
 };
 
-class MethodDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class MethodDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  MethodDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  MethodDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m));
   }
 
   static MethodDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_MethodDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodDescriptorProto_get(&m);
     return MethodDescriptorProto(m, &m);
   }
 };
 
-class MethodOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class MethodOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  MethodOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  MethodOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_MethodOptions_is(m));
   }
 
   static MethodOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_MethodOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodOptions_get(&m);
     return MethodOptions(m, &m);
   }
 };
 
-class OneofDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class OneofDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  OneofDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  OneofDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_OneofDescriptorProto_is(m));
   }
 
   static OneofDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_OneofDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_OneofDescriptorProto_get(&m);
     return OneofDescriptorProto(m, &m);
   }
 };
 
-class ServiceDescriptorProto : public upb::reffed_ptr<const upb::MessageDef> {
+class ServiceDescriptorProto : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  ServiceDescriptorProto(const upb::MessageDef* m, const void *ref_donor = NULL)
+  ServiceDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_ServiceDescriptorProto_is(m));
   }
 
   static ServiceDescriptorProto get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_ServiceDescriptorProto_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceDescriptorProto_get(&m);
     return ServiceDescriptorProto(m, &m);
   }
 };
 
-class ServiceOptions : public upb::reffed_ptr<const upb::MessageDef> {
+class ServiceOptions : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  ServiceOptions(const upb::MessageDef* m, const void *ref_donor = NULL)
+  ServiceOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_ServiceOptions_is(m));
   }
 
   static ServiceOptions get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_ServiceOptions_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceOptions_get(&m);
     return ServiceOptions(m, &m);
   }
 };
 
-class SourceCodeInfo : public upb::reffed_ptr<const upb::MessageDef> {
+class SourceCodeInfo : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  SourceCodeInfo(const upb::MessageDef* m, const void *ref_donor = NULL)
+  SourceCodeInfo(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_SourceCodeInfo_is(m));
   }
 
   static SourceCodeInfo get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_get(&m);
     return SourceCodeInfo(m, &m);
   }
 
-  class Location : public upb::reffed_ptr<const upb::MessageDef> {
+  class Location : public ::upb::reffed_ptr<const ::upb::MessageDef> {
    public:
-    Location(const upb::MessageDef* m, const void *ref_donor = NULL)
+    Location(const ::upb::MessageDef* m, const void *ref_donor = NULL)
         : reffed_ptr(m, ref_donor) {
       assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m));
     }
 
     static Location get() {
-      const upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_Location_get(&m);
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_Location_get(&m);
       return Location(m, &m);
     }
   };
 };
 
-class UninterpretedOption : public upb::reffed_ptr<const upb::MessageDef> {
+class UninterpretedOption : public ::upb::reffed_ptr<const ::upb::MessageDef> {
  public:
-  UninterpretedOption(const upb::MessageDef* m, const void *ref_donor = NULL)
+  UninterpretedOption(const ::upb::MessageDef* m, const void *ref_donor = NULL)
       : reffed_ptr(m, ref_donor) {
     assert(upbdefs_google_protobuf_UninterpretedOption_is(m));
   }
 
   static UninterpretedOption get() {
-    const upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_get(&m);
+    const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_get(&m);
     return UninterpretedOption(m, &m);
   }
 
-  class NamePart : public upb::reffed_ptr<const upb::MessageDef> {
+  class NamePart : public ::upb::reffed_ptr<const ::upb::MessageDef> {
    public:
-    NamePart(const upb::MessageDef* m, const void *ref_donor = NULL)
+    NamePart(const ::upb::MessageDef* m, const void *ref_donor = NULL)
         : reffed_ptr(m, ref_donor) {
       assert(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m));
     }
 
     static NamePart get() {
-      const upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_NamePart_get(&m);
+      const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_NamePart_get(&m);
       return NamePart(m, &m);
     }
   };
@@ -7178,7 +7327,6 @@
 #ifndef UPB_DECODER_INT_H_
 #define UPB_DECODER_INT_H_
 
-#include <stdlib.h>
 /*
 ** upb::pb::Decoder
 **
@@ -7278,7 +7426,7 @@
  * constructed.  This hint may be an overestimate for some build configurations.
  * But if the decoder library is upgraded without recompiling the application,
  * it may be an underestimate. */
-#define UPB_PB_DECODER_SIZE 4408
+#define UPB_PB_DECODER_SIZE 4416
 
 #ifdef __cplusplus
 
@@ -8097,7 +8245,8 @@
 #endif
 
 /* Loads a binary descriptor and returns a NULL-terminated array of unfrozen
- * filedefs.  The caller owns the returned array. */
+ * filedefs.  The caller owns the returned array, which must be freed with
+ * upb_gfree(). */
 upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner,
                                  upb_status *status);
 
@@ -8244,7 +8393,7 @@
  * constructed.  This hint may be an overestimate for some build configurations.
  * But if the parser library is upgraded without recompiling the application,
  * it may be an underestimate. */
-#define UPB_JSON_PARSER_SIZE 4104
+#define UPB_JSON_PARSER_SIZE 4112
 
 #ifdef __cplusplus
 
@@ -8357,7 +8506,7 @@
 
 /* upb::json::Printer *********************************************************/
 
-#define UPB_JSON_PRINTER_SIZE 168
+#define UPB_JSON_PRINTER_SIZE 176
 
 #ifdef __cplusplus