<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -28,7 +28,7 @@ CC=gcc
 CXX=g++
 CFLAGS=-std=c99
 INCLUDE=-Idescriptor -Isrc -Itests -I.
-CPPFLAGS=-Wall -Wextra -Werror -g $(INCLUDE) $(strip $(shell test -f perf-cppflags &amp;&amp; cat perf-cppflags))
+CPPFLAGS=-Wall -Wextra -g $(INCLUDE) $(strip $(shell test -f perf-cppflags &amp;&amp; cat perf-cppflags))
 LDLIBS=-lpthread
 
 LIBUPB=src/libupb.a
@@ -47,7 +47,8 @@ clean:
 # The core library (src/libupb.a)
 SRC=src/upb.c src/upb_parse.c src/upb_table.c src/upb_msg.c src/upb_mm.c \
     src/upb_enum.c src/upb_context.c src/upb_string.c src/upb_text.c \
-    src/upb_serialize.c descriptor/descriptor.c
+    descriptor/descriptor.c
+    #src/upb_serialize.c descriptor/descriptor.c
 STATICOBJ=$(patsubst %.c,%.o,$(SRC))
 SHAREDOBJ=$(patsubst %.c,%.lo,$(SRC))
 # building shared objects is like building static ones, except -fPIC is added.</diff>
      <filename>Makefile</filename>
    </modified>
    <modified>
      <diff>@@ -9,26 +9,30 @@ static struct upb_context *c;
 static struct upb_string *str;
 static struct upb_msgdef *def;
 static struct upb_msg *msgs[NUM_MESSAGES];
+static struct upb_msgparser *mp;
 
 static bool initialize()
 {
   // Initialize upb state, parse descriptor.
+  struct upb_status status = UPB_STATUS_INIT;
   c = upb_context_new();
   struct upb_string *fds = upb_strreadfile(MESSAGE_DESCRIPTOR_FILE);
   if(!fds) {
-    fprintf(stderr, &quot;Couldn't read &quot; MESSAGE_DESCRIPTOR_FILE &quot;.\n&quot;);
+    fprintf(stderr, &quot;Couldn't read &quot; MESSAGE_DESCRIPTOR_FILE &quot;: %s.\n&quot;,
+            status.msg);
     return false;
   }
-  if(!upb_context_parsefds(c, fds)) {
-    fprintf(stderr, &quot;Error importing &quot; MESSAGE_DESCRIPTOR_FILE &quot;.\n&quot;);
+  upb_context_parsefds(c, fds, &amp;status);
+  if(!upb_ok(&amp;status)) {
+    fprintf(stderr, &quot;Error importing &quot; MESSAGE_DESCRIPTOR_FILE &quot;: %s.\n&quot;,
+            status.msg);
     return false;
   }
   upb_string_unref(fds);
 
   struct upb_string *proto_name = upb_strdupc(MESSAGE_NAME);
   struct upb_symtab_entry e;
-  upb_status_t success = upb_context_lookup(c, proto_name, &amp;e);
-  if(!success || e.type != UPB_SYM_MESSAGE) {
+  if(!upb_context_lookup(c, proto_name, &amp;e) || e.type != UPB_SYM_MESSAGE) {
     fprintf(stderr, &quot;Error finding symbol '&quot; UPB_STRFMT &quot;'.\n&quot;,
             UPB_STRARG(proto_name));
     return false;
@@ -45,6 +49,7 @@ static bool initialize()
     fprintf(stderr, &quot;Error reading &quot; MESSAGE_FILE &quot;\n&quot;);
     return false;
   }
+  mp = upb_msgparser_new(def);
   return true;
 }
 
@@ -54,14 +59,18 @@ static void cleanup()
     upb_msg_unref(msgs[i]);
   upb_string_unref(str);
   upb_context_unref(c);
+  upb_msgparser_free(mp);
 }
 
 static size_t run(int i)
 {
-  upb_status_t status;
-  status = upb_msg_parsestr(msgs[i%NUM_MESSAGES], str-&gt;ptr, str-&gt;byte_len);
-  if(status != UPB_STATUS_OK) {
-    fprintf(stderr, &quot;Error. :(  error=%d\n&quot;, status);
+  struct upb_status status = UPB_STATUS_INIT;
+  struct upb_msg *msg = msgs[i%NUM_MESSAGES];
+  upb_msgparser_reset(mp, msg, false);
+  upb_msg_clear(msg);
+  upb_msgparser_parse(mp, str-&gt;ptr, str-&gt;byte_len, &amp;status);
+  if(!upb_ok(&amp;status)) {
+    fprintf(stderr, &quot;Parse error: %s\n&quot;, status.msg);
     return 0;
   }
   return str-&gt;byte_len;</diff>
      <filename>benchmarks/parsetostruct.upb_table.c</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,8 @@ setup(name='upb',
           Extension('upb.cext', ['definition.c', 'pb.c', 'cext.c'],
               include_dirs=['../../src', '../../descriptor'],
               define_macros=[(&quot;UPB_USE_PTHREADS&quot;, 1),
-                             (&quot;UPB_UNALIGNED_READS_OK&quot;, 1)],
+                             (&quot;UPB_UNALIGNED_READS_OK&quot;, 1),
+                             (&quot;UPB_THREAD_UNSAFE&quot;, 1)],
               library_dirs=['../../src'],
               libraries=['upb_pic'],
           ),</diff>
      <filename>lang_ext/python/setup.py</filename>
    </modified>
    <modified>
      <diff>@@ -5,6 +5,7 @@
  *
  */
 
+#include &lt;stdarg.h&gt;
 #include &lt;stddef.h&gt;
 
 #include &quot;upb.h&quot;
@@ -35,3 +36,14 @@ struct upb_type_info upb_type_info[] = {
   TYPE_INFO(BYTES,    UPB_WIRE_TYPE_DELIMITED,   struct upb_string*)
 };
 
+void upb_seterr(struct upb_status *status, enum upb_status_code code,
+                const char *msg, ...)
+{
+  if(upb_ok(status)) {  // The first error is the most interesting.
+    status-&gt;code = code;
+    va_list args;
+    va_start(args, msg);
+    vsnprintf(status-&gt;msg, UPB_ERRORMSG_MAXLEN, msg, args);
+    va_end(args);
+  }
+}</diff>
      <filename>src/upb.c</filename>
    </modified>
    <modified>
      <diff>@@ -34,7 +34,7 @@ extern &quot;C&quot; {
 
 // Nested type names are separated by periods.
 #define UPB_SYMBOL_SEPARATOR '.'
-#define UPB_SYMBOL_MAX_LENGTH 256
+#define UPB_SYMBOL_MAXLEN 128
 
 #define UPB_INDEX(base, i, m) (void*)((char*)(base) + ((i)*(m)))
 
@@ -230,7 +230,7 @@ union upb_symbol_ref {
 
 // Status codes used as a return value.  Codes &gt;0 are not fatal and can be
 // resumed.
-typedef enum upb_status {
+enum upb_status_code {
   UPB_STATUS_OK = 0,
 
   // The input byte stream ended in the middle of a record.
@@ -239,28 +239,32 @@ typedef enum upb_status {
   // The user value callback opted to stop parsing.
   UPB_STATUS_USER_CANCELLED = 2,
 
-  // A varint did not terminate before hitting 64 bits.
-  UPB_ERROR_UNTERMINATED_VARINT = -1,
+  // An unrecoverable error occurred.
+  UPB_STATUS_ERROR = -1,
 
-  // A submessage or packed array ended in the middle of data.
-  UPB_ERROR_BAD_SUBMESSAGE_END = -2,
+  // A varint went for 10 bytes without terminating.
+  UPB_ERROR_UNTERMINATED_VARINT = -2
+};
 
-  // Input was nested more than UPB_MAX_NESTING deep.
-  UPB_ERROR_STACK_OVERFLOW = -3,
+#define UPB_ERRORMSG_MAXLEN 256
+struct upb_status {
+  enum upb_status_code code;
+  char msg[UPB_ERRORMSG_MAXLEN];
+};
 
-  // The input data caused the pb's offset (a size_t) to overflow.
-  UPB_ERROR_OVERFLOW = -4,
+#define UPB_STATUS_INIT {UPB_STATUS_OK, &quot;&quot;}
 
-  // An &quot;end group&quot; tag was encountered in an inappropriate place.
-  UPB_ERROR_SPURIOUS_END_GROUP = -5,
+INLINE bool upb_ok(struct upb_status *status) {
+  return status-&gt;code == UPB_STATUS_OK;
+}
 
-  UPB_ERROR_ILLEGAL = -6
-} upb_status_t;
+INLINE void upb_reset(struct upb_status *status) {
+  status-&gt;code = UPB_STATUS_OK;
+  status-&gt;msg[0] = '\0';
+}
 
-#define UPB_CHECK(func) do { \
-  upb_status_t status = func; \
-  if(status != UPB_STATUS_OK) return status; \
-  } while (0)
+void upb_seterr(struct upb_status *status, enum upb_status_code code,
+                const char *msg, ...);
 
 #ifdef __cplusplus
 }  /* extern &quot;C&quot; */</diff>
      <filename>src/upb.h</filename>
    </modified>
    <modified>
      <diff>@@ -70,14 +70,13 @@ INLINE uint32_t upb_round_up_to_pow2(uint32_t v)
 INLINE union upb_value_ptr upb_array_append(struct upb_array *arr)
 {
   size_t size = upb_type_info[arr-&gt;fielddef-&gt;type].size;
-  upb_arraylen_t oldlen = arr-&gt;len;
-  if(oldlen == arr-&gt;size) {
-    arr-&gt;size = UPB_MAX(4, upb_round_up_to_pow2(oldlen+1));
+  if(arr-&gt;len == arr-&gt;size) {
+    arr-&gt;size = UPB_MAX(4, upb_round_up_to_pow2(arr-&gt;len + 1));
     arr-&gt;elements._void = realloc(arr-&gt;elements._void, arr-&gt;size * size);
-    memset((char*)arr-&gt;elements._void + (arr-&gt;len*size), 0, (arr-&gt;size - arr-&gt;len) * size);
+    memset((char*)arr-&gt;elements._void + (arr-&gt;len * size), 0,
+           (arr-&gt;size - arr-&gt;len) * size);
   }
-  arr-&gt;len++;
-  return upb_array_getelementptr(arr, oldlen);
+  return upb_array_getelementptr(arr, arr-&gt;len++);
 }
 
 INLINE void upb_array_truncate(struct upb_array *arr)</diff>
      <filename>src/upb_array.h</filename>
    </modified>
    <modified>
      <diff>@@ -20,9 +20,10 @@ static int my_memrchr(char *data, char c, size_t len)
   return off;
 }
 
-bool addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs,
+void addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs,
            google_protobuf_FileDescriptorProto *fd, bool sort,
-           struct upb_context *context);
+           struct upb_context *context,
+           struct upb_status *status);
 
 struct upb_context *upb_context_new()
 {
@@ -34,7 +35,10 @@ struct upb_context *upb_context_new()
   /* Add all the types in descriptor.proto so we can parse descriptors. */
   google_protobuf_FileDescriptorProto *fd =
       upb_file_descriptor_set-&gt;file-&gt;elements[0]; /* We know there is only 1. */
-  if(!addfd(&amp;c-&gt;psymtab, &amp;c-&gt;symtab, fd, false, c)) {
+  struct upb_status status = UPB_STATUS_INIT;
+  addfd(&amp;c-&gt;psymtab, &amp;c-&gt;symtab, fd, false, c, &amp;status);
+  if(!upb_ok(&amp;status)) {
+    fprintf(stderr, &quot;Failed to initialize upb: %s.\n&quot;, status.msg);
     assert(false);
     return NULL;  /* Indicates that upb is buggy or corrupt. */
   }
@@ -109,7 +113,7 @@ static struct upb_symtab_entry *resolve(struct upb_strtable *t,
                                         struct upb_string *base,
                                         struct upb_string *symbol)
 {
-  if(base-&gt;byte_len + symbol-&gt;byte_len + 1 &gt;= UPB_SYMBOL_MAX_LENGTH ||
+  if(base-&gt;byte_len + symbol-&gt;byte_len + 1 &gt;= UPB_SYMBOL_MAXLEN ||
      symbol-&gt;byte_len == 0) return NULL;
 
   if(symbol-&gt;ptr[0] == UPB_SYMBOL_SEPARATOR) {
@@ -119,7 +123,7 @@ static struct upb_symtab_entry *resolve(struct upb_strtable *t,
     return upb_strtable_lookup(t, &amp;sym_str);
   } else {
     /* Remove components from base until we find an entry or run out. */
-    char sym[UPB_SYMBOL_MAX_LENGTH+1];
+    char sym[UPB_SYMBOL_MAXLEN+1];
     struct upb_string sym_str = {.ptr = sym};
     int baselen = base-&gt;byte_len;
     while(1) {
@@ -180,20 +184,28 @@ static struct upb_string join(struct upb_string *base, struct upb_string *name)
   return joined;
 }
 
-static bool insert_enum(struct upb_strtable *t,
+static void insert_enum(struct upb_strtable *t,
                         google_protobuf_EnumDescriptorProto *ed,
                         struct upb_string *base,
-                        struct upb_context *c)
+                        struct upb_context *c,
+                        struct upb_status *status)
 {
-  if(!ed-&gt;set_flags.has.name) return false;
+  if(!ed-&gt;set_flags.has.name) {
+    upb_seterr(status, UPB_STATUS_ERROR,
+               &quot;enum in context '&quot; UPB_STRFMT &quot;' does not have a name&quot;,
+               UPB_STRARG(base));
+    return;
+  }
 
   /* We own this and must free it on destruct. */
   struct upb_string fqname = join(base, ed-&gt;name);
 
-  /* Redefinition within a FileDescriptorProto is not allowed. */
   if(upb_strtable_lookup(t, &amp;fqname)) {
+    upb_seterr(status, UPB_STATUS_ERROR,
+               &quot;attempted to redefine symbol '&quot; UPB_STRFMT &quot;'&quot;,
+               UPB_STRARG(&amp;fqname));
     free(fqname.ptr);
-    return false;
+    return;
   }
 
   struct upb_symtab_entry e;
@@ -202,74 +214,81 @@ static bool insert_enum(struct upb_strtable *t,
   e.ref._enum = malloc(sizeof(*e.ref._enum));
   upb_enum_init(e.ref._enum, ed, c);
   upb_strtable_insert(t, &amp;e.e);
-
-  return true;
 }
 
-static bool insert_message(struct upb_strtable *t,
+static void insert_message(struct upb_strtable *t,
                            google_protobuf_DescriptorProto *d,
                            struct upb_string *base, bool sort,
-                           struct upb_context *c)
+                           struct upb_context *c,
+                           struct upb_status *status)
 {
-  if(!d-&gt;set_flags.has.name) return false;
+  if(!d-&gt;set_flags.has.name) {
+    upb_seterr(status, UPB_STATUS_ERROR,
+               &quot;message in context '&quot; UPB_STRFMT &quot;' does not have a name&quot;,
+               UPB_STRARG(base));
+    return;
+  }
 
   /* We own this and must free it on destruct. */
   struct upb_string fqname = join(base, d-&gt;name);
 
-  /* Redefinition within a FileDescriptorProto is not allowed. */
-  if(upb_strtable_lookup(t, d-&gt;name)) {
+  if(upb_strtable_lookup(t, &amp;fqname)) {
+    upb_seterr(status, UPB_STATUS_ERROR,
+               &quot;attempted to redefine symbol '&quot; UPB_STRFMT &quot;'&quot;,
+               UPB_STRARG(&amp;fqname));
     free(fqname.ptr);
-    return false;
+    return;
   }
 
   struct upb_symtab_entry e;
   e.e.key = fqname;
   e.type = UPB_SYM_MESSAGE;
   e.ref.msg = malloc(sizeof(*e.ref.msg));
-  if(!upb_msgdef_init(e.ref.msg, d, fqname, sort, c)) {
+  upb_msgdef_init(e.ref.msg, d, fqname, sort, c, status);
+  if(!upb_ok(status)) {
     free(fqname.ptr);
-    return false;
+    return;
   }
   upb_strtable_insert(t, &amp;e.e);
 
   /* Add nested messages and enums. */
   if(d-&gt;set_flags.has.nested_type)
     for(unsigned int i = 0; i &lt; d-&gt;nested_type-&gt;len; i++)
-      if(!insert_message(t, d-&gt;nested_type-&gt;elements[i], &amp;fqname, sort, c))
-        return false;
+      insert_message(t, d-&gt;nested_type-&gt;elements[i], &amp;fqname, sort, c, status);
 
   if(d-&gt;set_flags.has.enum_type)
     for(unsigned int i = 0; i &lt; d-&gt;enum_type-&gt;len; i++)
-      if(!insert_enum(t, d-&gt;enum_type-&gt;elements[i], &amp;fqname, c))
-        return false;
-
-  return true;
+      insert_enum(t, d-&gt;enum_type-&gt;elements[i], &amp;fqname, c, status);
 }
 
-bool addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs,
+void addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs,
            google_protobuf_FileDescriptorProto *fd, bool sort,
-           struct upb_context *c)
+           struct upb_context *c, struct upb_status *status)
 {
   struct upb_string pkg = {.byte_len=0};
   if(fd-&gt;set_flags.has.package) pkg = *fd-&gt;package;
 
   if(fd-&gt;set_flags.has.message_type)
     for(unsigned int i = 0; i &lt; fd-&gt;message_type-&gt;len; i++)
-      if(!insert_message(addto, fd-&gt;message_type-&gt;elements[i], &amp;pkg, sort, c))
-        return false;
+      insert_message(addto, fd-&gt;message_type-&gt;elements[i], &amp;pkg, sort, c, status);
 
   if(fd-&gt;set_flags.has.enum_type)
     for(unsigned int i = 0; i &lt; fd-&gt;enum_type-&gt;len; i++)
-      if(!insert_enum(addto, fd-&gt;enum_type-&gt;elements[i], &amp;pkg, c))
-        return false;
+      insert_enum(addto, fd-&gt;enum_type-&gt;elements[i], &amp;pkg, c, status);
+
+  if(!upb_ok(status)) return;
 
   /* TODO: handle extensions and services. */
 
   /* Attempt to resolve all references. */
   struct upb_symtab_entry *e;
   for(e = upb_strtable_begin(addto); e; e = upb_strtable_next(addto, &amp;e-&gt;e)) {
-    if(upb_strtable_lookup(existingdefs, &amp;e-&gt;e.key))
-      return false;  /* Redefinition prohibited. */
+    if(upb_strtable_lookup(existingdefs, &amp;e-&gt;e.key)) {
+      upb_seterr(status, UPB_STATUS_ERROR,
+                 &quot;attempted to redefine symbol '&quot; UPB_STRFMT &quot;'&quot;,
+                 UPB_STRARG(&amp;e-&gt;e.key));
+      return;
+    }
     if(e-&gt;type == UPB_SYM_MESSAGE) {
       struct upb_msgdef *m = e-&gt;ref.msg;
       for(unsigned int i = 0; i &lt; m-&gt;num_fields; i++) {
@@ -285,16 +304,22 @@ bool addfd(struct upb_strtable *addto, struct upb_strtable *existingdefs,
                          UPB_SYM_ENUM);
         else
           continue;  /* No resolving necessary. */
-        if(!ref.msg) return false;  /* Ref. to undefined symbol. */
+        if(!ref.msg) {
+          upb_seterr(status, UPB_STATUS_ERROR,
+                     &quot;could not resolve symbol '&quot; UPB_STRFMT &quot;'&quot;
+                     &quot; in context '&quot; UPB_STRFMT &quot;'&quot;,
+                     UPB_STRARG(fd-&gt;type_name), UPB_STRARG(&amp;e-&gt;e.key));
+          return;
+        }
         upb_msgdef_setref(m, f, ref);
       }
     }
   }
-  return true;
 }
 
-bool upb_context_addfds(struct upb_context *c,
-                        google_protobuf_FileDescriptorSet *fds)
+void upb_context_addfds(struct upb_context *c,
+                        google_protobuf_FileDescriptorSet *fds,
+                        struct upb_status *status)
 {
   if(fds-&gt;set_flags.has.file) {
     /* Insert new symbols into a temporary table until we have verified that
@@ -303,11 +328,11 @@ bool upb_context_addfds(struct upb_context *c,
     upb_strtable_init(&amp;tmp, 0, sizeof(struct upb_symtab_entry));
     upb_rwlock_rdlock(&amp;c-&gt;lock);
     for(uint32_t i = 0; i &lt; fds-&gt;file-&gt;len; i++) {
-      if(!addfd(&amp;tmp, &amp;c-&gt;symtab, fds-&gt;file-&gt;elements[i], true, c)) {
-        printf(&quot;Not added successfully!\n&quot;);
+      addfd(&amp;tmp, &amp;c-&gt;symtab, fds-&gt;file-&gt;elements[i], true, c, status);
+      if(!upb_ok(status)) {
         free_symtab(&amp;tmp);
         upb_rwlock_unlock(&amp;c-&gt;lock);
-        return false;
+        return;
       }
     }
     upb_rwlock_unlock(&amp;c-&gt;lock);
@@ -322,13 +347,17 @@ bool upb_context_addfds(struct upb_context *c,
     }
     upb_strtable_free(&amp;tmp);
   }
-  return true;
+  return;
 }
 
-bool upb_context_parsefds(struct upb_context *c, struct upb_string *fds_str) {
+void upb_context_parsefds(struct upb_context *c, struct upb_string *fds_str,
+                          struct upb_status *status)
+{
   struct upb_msg *fds = upb_msg_new(c-&gt;fds_msg);
-  if(upb_msg_parsestr(fds, fds_str-&gt;ptr, fds_str-&gt;byte_len) != UPB_STATUS_OK) return false;
-  if(!upb_context_addfds(c, (google_protobuf_FileDescriptorSet*)fds)) return false;
+  upb_msg_parsestr(fds, fds_str-&gt;ptr, fds_str-&gt;byte_len, status);
+  if(!upb_ok(status)) return;
+  upb_context_addfds(c, (google_protobuf_FileDescriptorSet*)fds, status);
+  if(!upb_ok(status)) return;
 
   {
     /* We own fds now, need to keep a ref so we can free it later. */
@@ -340,5 +369,5 @@ bool upb_context_parsefds(struct upb_context *c, struct upb_string *fds_str) {
     c-&gt;fds[c-&gt;fds_len++] = (google_protobuf_FileDescriptorSet*)fds;
     upb_rwlock_unlock(&amp;c-&gt;lock);
   }
-  return true;
+  return;
 }</diff>
      <filename>src/upb_context.c</filename>
    </modified>
    <modified>
      <diff>@@ -102,13 +102,16 @@ void upb_context_enumerate(struct upb_context *c, upb_context_enumerator_t,
  * upb_context_addfd only returns true or false; it does not give any hint
  * about what happened in the case of failure.  This is because the descriptor
  * is expected to have been validated at the time it was parsed/generated. */
-bool upb_context_addfds(struct upb_context *c,
-                        struct google_protobuf_FileDescriptorSet *fds);
+void upb_context_addfds(struct upb_context *c,
+                        struct google_protobuf_FileDescriptorSet *fds,
+                        struct upb_status *status);
 
-bool upb_context_addfds(struct upb_context *c,
-                        struct google_protobuf_FileDescriptorSet *fds);
+void upb_context_addfds(struct upb_context *c,
+                        struct google_protobuf_FileDescriptorSet *fds,
+                        struct upb_status *status);
 
-bool upb_context_parsefds(struct upb_context *c, struct upb_string *fds);
+void upb_context_parsefds(struct upb_context *c, struct upb_string *fds,
+                          struct upb_status *status);
 
 #ifdef __cplusplus
 }  /* extern &quot;C&quot; */</diff>
      <filename>src/upb_context.h</filename>
    </modified>
    <modified>
      <diff>@@ -9,14 +9,20 @@
 #include &quot;upb_array.h&quot;
 #include &quot;upb_msg.h&quot;
 
+static void upb_mm_destroy(union upb_value_ptr p, upb_mm_ptrtype type)
+{
+  if(*p.msg) {
+    union upb_mmptr mmptr = upb_mmptr_read(p, type);
+    upb_mm_unref(mmptr, type);
+  }
+}
+
 void upb_msg_destroy(struct upb_msg *msg) {
   uint32_t i;
   for(i = 0; i &lt; msg-&gt;def-&gt;num_fields; i++) {
     struct upb_msg_fielddef *f = &amp;msg-&gt;def-&gt;fields[i];
     if(!upb_msg_isset(msg, f) || !upb_field_ismm(f)) continue;
-    upb_mm_ptrtype type = upb_field_ptrtype(f);
-    union upb_mmptr mmptr = upb_mmptr_read(upb_msg_getptr(msg, f), type);
-    upb_mm_unref(mmptr, type);
+    upb_mm_destroy(upb_msg_getptr(msg, f), upb_field_ptrtype(f));
   }
   free(msg);
 }
@@ -26,11 +32,9 @@ void upb_array_destroy(struct upb_array *arr)
   if(upb_elem_ismm(arr-&gt;fielddef)) {
     upb_arraylen_t i;
     /* Unref elements. */
-    for(i = 0; i &lt; arr-&gt;len; i++) {
+    for(i = 0; i &lt; arr-&gt;size; i++) {
       union upb_value_ptr p = upb_array_getelementptr(arr, i);
-      upb_mm_ptrtype type = upb_elem_ptrtype(arr-&gt;fielddef);
-      union upb_mmptr mmptr = upb_mmptr_read(p, type);
-      upb_mm_unref(mmptr, type);
+      upb_mm_destroy(p, upb_elem_ptrtype(arr-&gt;fielddef));
     }
   }
   if(arr-&gt;size != 0) free(arr-&gt;elements._void);</diff>
      <filename>src/upb_mm.c</filename>
    </modified>
    <modified>
      <diff>@@ -42,9 +42,11 @@ void upb_msgdef_sortfds(google_protobuf_FieldDescriptorProto **fds, size_t num)
   qsort(fds, num, sizeof(void*), compare_fields);
 }
 
-bool upb_msgdef_init(struct upb_msgdef *m, google_protobuf_DescriptorProto *d,
-                     struct upb_string fqname, bool sort, struct upb_context *c)
+void upb_msgdef_init(struct upb_msgdef *m, google_protobuf_DescriptorProto *d,
+                     struct upb_string fqname, bool sort, struct upb_context *c,
+                     struct upb_status *status)
 {
+  (void)status;  // Nothing that can fail at the moment.
   int num_fields = d-&gt;set_flags.has.field ? d-&gt;field-&gt;len : 0;
   upb_inttable_init(&amp;m-&gt;fields_by_num, num_fields,
                     sizeof(struct upb_fieldsbynum_entry));
@@ -97,8 +99,6 @@ bool upb_msgdef_init(struct upb_msgdef *m, google_protobuf_DescriptorProto *d,
 
   if(max_align &gt; 0)
     m-&gt;size = ALIGN_UP(m-&gt;size, max_align);
-
-  return true;
 }
 
 void upb_msgdef_free(struct upb_msgdef *m)
@@ -125,26 +125,17 @@ void upb_msgdef_setref(struct upb_msgdef *m, struct upb_msg_fielddef *f,
 
 /* Parsing.  ******************************************************************/
 
-struct upb_msg_parser_frame {
+struct upb_msgparser_frame {
   struct upb_msg *msg;
 };
 
-struct upb_msg_parser {
+struct upb_msgparser {
   struct upb_cbparser *s;
   bool merge;
   bool byref;
-  struct upb_msg_parser_frame stack[UPB_MAX_NESTING], *top;
+  struct upb_msgparser_frame stack[UPB_MAX_NESTING], *top;
 };
 
-/* Parses protocol buffer data out of data which has length of len.  The data
- * need not be a complete protocol buffer.  The number of bytes parsed is
- * returned in *read, and the next call to upb_msg_parse must supply data that
- * is *read bytes past data in the logical stream. */
-upb_status_t upb_msg_parser_parse(struct upb_msg_parser *p,
-                                  void *data, size_t len, size_t *read);
-
-
-
 /* Helper function that returns a pointer to where the next value for field &quot;f&quot;
  * should be stored, taking into account whether f is an array that may need to
  * be allocated or resized. */
@@ -172,7 +163,7 @@ static union upb_value_ptr get_value_ptr(struct upb_msg *msg,
 static upb_field_type_t tag_cb(void *udata, struct upb_tag *tag,
                                void **user_field_desc)
 {
-  struct upb_msg_parser *mp = udata;
+  struct upb_msgparser *mp = udata;
   struct upb_msg_fielddef *f =
       upb_msg_fieldbynum(mp-&gt;top-&gt;msg-&gt;def, tag-&gt;field_number);
   if(!f || !upb_check_type(tag-&gt;wire_type, f-&gt;type))
@@ -181,23 +172,22 @@ static upb_field_type_t tag_cb(void *udata, struct upb_tag *tag,
   return f-&gt;type;
 }
 
-static upb_status_t value_cb(void *udata, uint8_t *buf, uint8_t *end,
-                             void *user_field_desc, uint8_t **outbuf)
+static void *value_cb(void *udata, uint8_t *buf, uint8_t *end,
+                      void *user_field_desc, struct upb_status *status)
 {
-  struct upb_msg_parser *mp = udata;
+  struct upb_msgparser *mp = udata;
   struct upb_msg_fielddef *f = user_field_desc;
   struct upb_msg *msg = mp-&gt;top-&gt;msg;
   union upb_value_ptr p = get_value_ptr(msg, f);
   upb_msg_set(msg, f);
-  UPB_CHECK(upb_parse_value(buf, end, f-&gt;type, p, outbuf));
-  return UPB_STATUS_OK;
+  return upb_parse_value(buf, end, f-&gt;type, p, status);
 }
 
 static void str_cb(void *udata, uint8_t *str,
                    size_t avail_len, size_t total_len,
                    void *udesc)
 {
-  struct upb_msg_parser *mp = udata;
+  struct upb_msgparser *mp = udata;
   struct upb_msg_fielddef *f = udesc;
   struct upb_msg *msg = mp-&gt;top-&gt;msg;
   union upb_value_ptr p = get_value_ptr(msg, f);
@@ -222,7 +212,7 @@ static void str_cb(void *udata, uint8_t *str,
 
 static void start_cb(void *udata, void *user_field_desc)
 {
-  struct upb_msg_parser *mp = udata;
+  struct upb_msgparser *mp = udata;
   struct upb_msg_fielddef *f = user_field_desc;
   struct upb_msg *oldmsg = mp-&gt;top-&gt;msg;
   union upb_value_ptr p = get_value_ptr(oldmsg, f);
@@ -243,53 +233,48 @@ static void start_cb(void *udata, void *user_field_desc)
 
 static void end_cb(void *udata)
 {
-  struct upb_msg_parser *mp = udata;
-  struct upb_msg *msg = mp-&gt;top-&gt;msg;
-  struct upb_msgdef *def = msg-&gt;def;
-
-  // Free any remaining dynamic storage we did not reuse.
-  for(uint32_t i = 0; i &lt; def-&gt;num_fields; i++) {
-    struct upb_msg_fielddef *f = &amp;def-&gt;fields[i];
-    union upb_value_ptr p = upb_msg_getptr(msg, f);
-    if(upb_msg_isset(msg, f) || !upb_field_ismm(f) || !*p.msg) continue;
-    union upb_mmptr mmptr = upb_mmptr_read(p, f-&gt;type);
-    upb_mm_unref(mmptr, f-&gt;type);
-  }
-
+  struct upb_msgparser *mp = udata;
   mp-&gt;top--;
 }
 
 /* Externally-visible functions for the msg parser. */
 
-void upb_msgparser_init(struct upb_msg_parser *s, struct upb_msg *msg, bool byref)
+struct upb_msgparser *upb_msgparser_new(struct upb_msgdef *def)
+{
+  (void)def;  // Not used atm.
+  struct upb_msgparser *mp = malloc(sizeof(struct upb_msgparser));
+  mp-&gt;s = upb_cbparser_new();
+  return mp;
+}
+
+void upb_msgparser_reset(struct upb_msgparser *s, struct upb_msg *msg, bool byref)
 {
-  s-&gt;s = upb_cbparser_new();
   upb_cbparser_reset(s-&gt;s, s, tag_cb, value_cb, str_cb, start_cb, end_cb);
   s-&gt;byref = byref;
   s-&gt;top = s-&gt;stack;
   s-&gt;top-&gt;msg = msg;
 }
 
-void upb_msgparser_free(struct upb_msg_parser *s)
+void upb_msgparser_free(struct upb_msgparser *s)
 {
   upb_cbparser_free(s-&gt;s);
+  free(s);
 }
 
-upb_status_t upb_msg_parsestr(struct upb_msg *msg, void *buf, size_t len)
+void upb_msg_parsestr(struct upb_msg *msg, void *buf, size_t len,
+                      struct upb_status *status)
 {
-  struct upb_msg_parser mp;
-  upb_msgparser_init(&amp;mp, msg, false);
-  size_t read;
+  struct upb_msgparser *mp = upb_msgparser_new(msg-&gt;def);
+  upb_msgparser_reset(mp, msg, false);
   upb_msg_clear(msg);
-  upb_status_t ret = upb_msg_parser_parse(&amp;mp, buf, len, &amp;read);
-  upb_msgparser_free(&amp;mp);
-  return ret;
+  upb_msgparser_parse(mp, buf, len, status);
+  upb_msgparser_free(mp);
 }
 
-upb_status_t upb_msg_parser_parse(struct upb_msg_parser *s,
-                                  void *data, size_t len, size_t *read)
+size_t upb_msgparser_parse(struct upb_msgparser *s, void *data, size_t len,
+                           struct upb_status *status)
 {
-  return upb_cbparser_parse(s-&gt;s, data, len, read);
+  return upb_cbparser_parse(s-&gt;s, data, len, status);
 }
 
 /* Serialization.  ************************************************************/
@@ -429,12 +414,13 @@ void upb_msg_serialize_init(struct upb_msg_serialize_state *s, struct upb_msg *m
   (void)sizes;
 }
 
-static upb_status_t serialize_tag(uint8_t *buf, uint8_t *end,
-                                  struct upb_msg_fielddef *f, uint8_t **outptr)
+#if 0
+static uint8_t *serialize_tag(uint8_t *buf, uint8_t *end,
+                              struct upb_msg_fielddef *f,
+                              struct upb_status *status)
 {
   /* TODO: need to have the field number also. */
-  UPB_CHECK(upb_put_UINT32(buf, end, f-&gt;type, outptr));
-  return UPB_STATUS_OK;
+  return upb_put_UINT32(buf, end, f-&gt;type, status);
 }
 
 /* Serializes the next set of bytes into buf (which has size len).  Returns
@@ -443,8 +429,8 @@ static upb_status_t serialize_tag(uint8_t *buf, uint8_t *end,
  *
  * The number of bytes written to buf is returned in *read.  This will be
  * equal to len unless we finished serializing. */
-upb_status_t upb_msg_serialize(struct upb_msg_serialize_state *s,
-                               void *_buf, size_t len, size_t *written)
+size_t upb_msg_serialize(struct upb_msg_serialize_state *s,
+                         void *_buf, size_t len, struct upb_status *status)
 {
   uint8_t *buf = _buf;
   uint8_t *end = buf + len;
@@ -456,18 +442,18 @@ upb_status_t upb_msg_serialize(struct upb_msg_serialize_state *s,
 
   while(buf &lt; end) {
     struct upb_msg_fielddef *f = &amp;m-&gt;fields[i];
-    union upb_value_ptr p = upb_msg_getptr(msg, f);
-    serialize_tag(buf, end, f, &amp;buf);
+    //union upb_value_ptr p = upb_msg_getptr(msg, f);
+    buf = serialize_tag(buf, end, f, status);
     if(f-&gt;type == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE) {
     } else if(f-&gt;type == GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP) {
     } else if(upb_isstring(f)) {
     } else {
-      upb_serialize_value(buf, end, f-&gt;type, p, &amp;buf);
+      //upb_serialize_value(buf, end, f-&gt;type, p, status);
     }
   }
-  *written = buf - start;
-  return UPB_STATUS_OK;
+  return buf - start;
 }
+#endif
 
 
 /* Comparison.  ***************************************************************/</diff>
      <filename>src/upb_msg.c</filename>
    </modified>
    <modified>
      <diff>@@ -257,7 +257,17 @@ INLINE struct upb_msg_fielddef *upb_msg_fieldbyname(struct upb_msgdef *m,
 /* Parsing ********************************************************************/
 
 /* TODO: a stream parser. */
-upb_status_t upb_msg_parsestr(struct upb_msg *msg, void *buf, size_t len);
+void upb_msg_parsestr(struct upb_msg *msg, void *buf, size_t len,
+                      struct upb_status *status);
+
+struct upb_msgparser *upb_msgparser_new(struct upb_msgdef *def);
+void upb_msgparser_free(struct upb_msgparser *mp);
+
+void upb_msgparser_reset(struct upb_msgparser *mp, struct upb_msg *m,
+                         bool byref);
+
+size_t upb_msgparser_parse(struct upb_msgparser *mp, void *buf, size_t len,
+                           struct upb_status *status);
 
 /* Serialization  *************************************************************/
 
@@ -298,12 +308,11 @@ void upb_msg_serialize_init(struct upb_msg_serialize_state *s,
  *
  * The number of bytes written to buf is returned in *written.  This will be
  * equal to len unless we finished serializing. */
-upb_status_t upb_msg_serialize(struct upb_msg_serialize_state *s,
-                               void *buf, size_t len, size_t *written);
+size_t upb_msg_serialize(struct upb_msg_serialize_state *s,
+                         void *buf, size_t len, struct upb_status *status);
 
-upb_status_t upb_msg_serialize_all(struct upb_msg *msg,
-                                   struct upb_msgsizes *sizes,
-                                   void *buf);
+void upb_msg_serialize_all(struct upb_msg *msg, struct upb_msgsizes *sizes,
+                           void *buf, struct upb_status *status);
 
 /* Text dump  *****************************************************************/
 
@@ -327,10 +336,10 @@ void upb_msg_print(struct upb_msg *data, bool single_line, FILE *stream);
  * sort indicates whether or not it is safe to reorder the fields from the order
  * they appear in d.  This should be false if code has been compiled against a
  * header for this type that expects the given order. */
-bool upb_msgdef_init(struct upb_msgdef *m,
+void upb_msgdef_init(struct upb_msgdef *m,
                      struct google_protobuf_DescriptorProto *d,
                      struct upb_string fqname, bool sort,
-                     struct upb_context *c);
+                     struct upb_context *c, struct upb_status *status);
 void upb_msgdef_free(struct upb_msgdef *m);
 
 /* Sort the given field descriptors in-place, according to what we think is an</diff>
      <filename>src/upb_msg.h</filename>
    </modified>
    <modified>
      <diff>@@ -13,8 +13,8 @@
  * Parses a 64-bit varint that is known to be &gt;= 2 bytes (the inline version
  * handles 1 and 2 byte varints).
  */
-upb_status_t upb_get_v_uint64_t_full(uint8_t *buf, uint8_t *end, uint64_t *val,
-                                     uint8_t **outbuf)
+uint8_t *upb_get_v_uint64_t_full(uint8_t *buf, uint8_t *end, uint64_t *val,
+                                 struct upb_status *status)
 {
   uint8_t *const maxend = buf + 10;
   uint8_t last = 0x80;
@@ -24,27 +24,33 @@ upb_status_t upb_get_v_uint64_t_full(uint8_t *buf, uint8_t *end, uint64_t *val,
   for(bitpos = 0; buf &lt; (uint8_t*)end &amp;&amp; (last &amp; 0x80); buf++, bitpos += 7)
     *val |= ((uint64_t)((last = *buf) &amp; 0x7F)) &lt;&lt; bitpos;
 
-  if(buf &gt;= end &amp;&amp; buf &lt;= maxend &amp;&amp; (last &amp; 0x80))
-    return UPB_STATUS_NEED_MORE_DATA;
-  if(buf &gt; maxend)
-    return UPB_ERROR_UNTERMINATED_VARINT;
+  if(buf &gt;= end &amp;&amp; buf &lt;= maxend &amp;&amp; (last &amp; 0x80)) {
+    upb_seterr(status, UPB_STATUS_NEED_MORE_DATA,
+               &quot;Provided data ended in the middle of a varint.\n&quot;);
+    buf = end;
+  } else if(buf &gt; maxend) {
+    upb_seterr(status, UPB_ERROR_UNTERMINATED_VARINT,
+               &quot;Varint was unterminated after 10 bytes.\n&quot;);
+    buf = end;
+  }
 
-  *outbuf = buf;
-  return UPB_STATUS_OK;
+  return buf;
 }
 
-upb_status_t upb_parse_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt,
-                                  union upb_wire_value *wv, uint8_t **outbuf)
+uint8_t *upb_parse_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt,
+                              union upb_wire_value *wv,
+                              struct upb_status *status)
 {
   switch(wt) {
     case UPB_WIRE_TYPE_VARINT:
-      return upb_get_v_uint64_t(buf, end, &amp;wv-&gt;varint, outbuf);
+      return upb_get_v_uint64_t(buf, end, &amp;wv-&gt;varint, status);
     case UPB_WIRE_TYPE_64BIT:
-      return upb_get_f_uint64_t(buf, end, &amp;wv-&gt;_64bit, outbuf);
+      return upb_get_f_uint64_t(buf, end, &amp;wv-&gt;_64bit, status);
     case UPB_WIRE_TYPE_32BIT:
-      return upb_get_f_uint32_t(buf, end, &amp;wv-&gt;_32bit, outbuf);
+      return upb_get_f_uint32_t(buf, end, &amp;wv-&gt;_32bit, status);
     default:
-      return UPB_ERROR_ILLEGAL;  // Doesn't handle delimited, groups.
+      status-&gt;code = UPB_STATUS_ERROR;  // Doesn't handle delimited, groups.
+      return end;
   }
 }
 
@@ -52,30 +58,31 @@ upb_status_t upb_parse_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt
  * Advances buf past the current wire value (of type wt), saving the result in
  * outbuf.
  */
-static upb_status_t skip_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt,
-                                    uint8_t **outbuf)
+static uint8_t *skip_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt,
+                                struct upb_status *status)
 {
   switch(wt) {
     case UPB_WIRE_TYPE_VARINT:
-      return upb_skip_v_uint64_t(buf, end, outbuf);
+      return upb_skip_v_uint64_t(buf, end, status);
     case UPB_WIRE_TYPE_64BIT:
-      return upb_skip_f_uint64_t(buf, end, outbuf);
+      return upb_skip_f_uint64_t(buf, end, status);
     case UPB_WIRE_TYPE_32BIT:
-      return upb_skip_f_uint32_t(buf, end, outbuf);
+      return upb_skip_f_uint32_t(buf, end, status);
     case UPB_WIRE_TYPE_START_GROUP:
       // TODO: skip to matching end group.
     case UPB_WIRE_TYPE_END_GROUP:
-      return UPB_STATUS_OK;
+      return buf;
     default:
-      return UPB_ERROR_ILLEGAL;
+      status-&gt;code = UPB_STATUS_ERROR;
+      return end;
   }
 }
 
-upb_status_t upb_parse_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
-                             union upb_value_ptr v, uint8_t **outbuf)
+uint8_t *upb_parse_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
+                         union upb_value_ptr v, struct upb_status *status)
 {
 #define CASE(t, member_name) \
-  case UPB_TYPENUM(t): return upb_get_ ## t(buf, end, v.member_name, outbuf);
+  case UPB_TYPENUM(t): return upb_get_ ## t(buf, end, v.member_name, status);
 
   switch(ft) {
     CASE(DOUBLE,   _double)
@@ -92,7 +99,7 @@ upb_status_t upb_parse_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
     CASE(SFIXED64, int64)
     CASE(BOOL,     _bool)
     CASE(ENUM,     int32)
-    default: return UPB_ERROR_ILLEGAL;
+    default: return end;
   }
 
 #undef CASE
@@ -146,23 +153,26 @@ void upb_cbparser_reset(struct upb_cbparser *p, void *udata,
  * Pushes a new stack frame for a submessage with the given len (which will
  * be zero if the submessage is a group).
  */
-static upb_status_t push(struct upb_cbparser *s, uint8_t *start,
-                         uint32_t submsg_len, void *user_field_desc,
-                         uint8_t **submsg_end)
+static uint8_t *push(struct upb_cbparser *s, uint8_t *start,
+                     uint32_t submsg_len, void *user_field_desc,
+                     struct upb_status *status)
 {
   s-&gt;top++;
-  if(s-&gt;top &gt;= s-&gt;limit)
-    return UPB_ERROR_STACK_OVERFLOW;
+  if(s-&gt;top &gt;= s-&gt;limit) {
+    upb_seterr(status, UPB_STATUS_ERROR,
+               &quot;Nesting exceeded maximum (%d levels)\n&quot;,
+               UPB_MAX_NESTING);
+    return NULL;
+  }
   *s-&gt;top = s-&gt;completed_offset + submsg_len;
 
   if(s-&gt;start_cb)
     s-&gt;start_cb(s-&gt;udata, user_field_desc);
 
   if(*s-&gt;top &gt; 0)
-    *submsg_end = start + (*s-&gt;top - s-&gt;completed_offset);
+    return start + (*s-&gt;top - s-&gt;completed_offset);
   else
-    *submsg_end = (void*)UINTPTR_MAX;
-  return UPB_STATUS_OK;
+    return (void*)UINTPTR_MAX;
 }
 
 /**
@@ -183,15 +193,14 @@ static void *pop(struct upb_cbparser *s, uint8_t *start)
 }
 
 
-upb_status_t upb_cbparser_parse(struct upb_cbparser *s, void *_buf, size_t len,
-                                size_t *read)
+size_t upb_cbparser_parse(struct upb_cbparser *s, void *_buf, size_t len,
+                          struct upb_status *status)
 {
   uint8_t *buf = _buf;
   uint8_t *completed = buf;
   uint8_t *const start = buf;  // ptr equivalent of s-&gt;completed_offset
   uint8_t *end = buf + len;
   uint8_t *submsg_end = *s-&gt;top &gt; 0 ? buf + *s-&gt;top : (uint8_t*)UINTPTR_MAX;
-  upb_status_t status = UPB_STATUS_OK;
 
   // Make local copies so optimizer knows they won't change.
   upb_tag_cb tag_cb = s-&gt;tag_cb;
@@ -199,13 +208,14 @@ upb_status_t upb_cbparser_parse(struct upb_cbparser *s, void *_buf, size_t len,
   upb_value_cb value_cb = s-&gt;value_cb;
   void *udata = s-&gt;udata;
 
-#define CHECK(exp) do { if((status = exp) != UPB_STATUS_OK) goto err; } while(0)
+#define CHECK_STATUS() do { if(!upb_ok(status)) goto err; } while(0)
 
   // Main loop: parse a tag, then handle the value.
   while(buf &lt; end) {
     struct upb_tag tag;
-    CHECK(parse_tag(buf, end, &amp;tag, &amp;buf));
+    buf = parse_tag(buf, end, &amp;tag, status);
     if(tag.wire_type == UPB_WIRE_TYPE_END_GROUP) {
+      CHECK_STATUS();
       submsg_end = pop(s, start);
       completed = buf;
       continue;
@@ -215,10 +225,11 @@ upb_status_t upb_cbparser_parse(struct upb_cbparser *s, void *_buf, size_t len,
     upb_field_type_t ft = tag_cb(udata, &amp;tag, &amp;udesc);
     if(tag.wire_type == UPB_WIRE_TYPE_DELIMITED) {
       int32_t delim_len;
-      CHECK(upb_get_INT32(buf, end, &amp;delim_len, &amp;buf));
+      buf = upb_get_INT32(buf, end, &amp;delim_len, status);
+      CHECK_STATUS();
       uint8_t *delim_end = buf + delim_len;
       if(ft == UPB_TYPENUM(MESSAGE)) {
-        CHECK(push(s, start, delim_end - start, udesc, &amp;submsg_end));
+        submsg_end = push(s, start, delim_end - start, udesc, status);
       } else {
         if(upb_isstringtype(ft)) {
           size_t avail_len = UPB_MIN(delim_end, end) - buf;
@@ -230,20 +241,21 @@ upb_status_t upb_cbparser_parse(struct upb_cbparser *s, void *_buf, size_t len,
       // Scalar (non-delimited) value.
       switch(ft) {
         case 0:  // Client elected to skip.
-          CHECK(skip_wire_value(buf, end, tag.wire_type, &amp;buf));
+          buf = skip_wire_value(buf, end, tag.wire_type, status);
           break;
         case UPB_TYPENUM(GROUP):
-          CHECK(push(s, start, 0, udesc, &amp;submsg_end));
+          submsg_end = push(s, start, 0, udesc, status);
           break;
         default:
-          CHECK(value_cb(udata, buf, end, udesc, &amp;buf));
+          buf = value_cb(udata, buf, end, udesc, status);
           break;
       }
     }
+    CHECK_STATUS();
 
     while(buf &gt;= submsg_end) {
       if(buf &gt; submsg_end) {
-        return UPB_ERROR_BAD_SUBMESSAGE_END;
+        return UPB_STATUS_ERROR;  // Bad submessage end.
       }
       submsg_end = pop(s, start);
     }
@@ -251,8 +263,9 @@ upb_status_t upb_cbparser_parse(struct upb_cbparser *s, void *_buf, size_t len,
     completed = buf;
   }
 
+  size_t read;
 err:
-  *read = (char*)completed - (char*)start;
-  s-&gt;completed_offset += *read;
-  return status;
+  read = (char*)completed - (char*)start;
+  s-&gt;completed_offset += read;
+  return read;
 }</diff>
      <filename>src/upb_parse.c</filename>
    </modified>
    <modified>
      <diff>@@ -45,8 +45,8 @@ typedef upb_field_type_t (*upb_tag_cb)(void *udata, struct upb_tag *tag,
 //
 // Note that this callback can be called several times in a row for a single
 // call to tag_cb in the case of packed arrays.
-typedef upb_status_t (*upb_value_cb)(void *udata, uint8_t *buf, uint8_t *end,
-                                     void *user_field_desc, uint8_t **outbuf);
+typedef void *(*upb_value_cb)(void *udata, uint8_t *buf, uint8_t *end,
+                              void *user_field_desc, struct upb_status *status);
 
 // The string callback is called when a string is parsed.  avail_len is the
 // number of bytes that are currently available at str.  If the client is
@@ -96,8 +96,8 @@ void upb_cbparser_reset(struct upb_cbparser *p, void *udata,
 //
 // TODO: see if we can provide the following guarantee efficiently:
 //   *read will always be &gt;= len. */
-upb_status_t upb_cbparser_parse(struct upb_cbparser *p, void *buf, size_t len,
-                                size_t *read);
+size_t upb_cbparser_parse(struct upb_cbparser *p, void *buf, size_t len,
+                          struct upb_status *status);
 
 extern upb_wire_type_t upb_expected_wire_types[];
 // Returns true if wt is the correct on-the-wire type for ft.
@@ -109,56 +109,59 @@ INLINE bool upb_check_type(upb_wire_type_t wt, upb_field_type_t ft) {
 /* Data-consuming functions (to be called from value cb). *********************/
 
 // Parses and converts a value from the character data starting at buf (but not
-// past end).  *outbuf will be set to one past the data that was read.  The
+// past end).  Returns a pointer that is one past the data that was read.  The
 // caller must have previously checked that the wire type is appropriate for
 // this field type.
-upb_status_t upb_parse_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
-                             union upb_value_ptr v, uint8_t **outbuf);
+uint8_t *upb_parse_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
+                         union upb_value_ptr v, struct upb_status *status);
 
 // Parses a wire value with the given type (which must have been obtained from
-// a tag that was just parsed) and sets *outbuf to one past the data that was
-// read.
-upb_status_t upb_parse_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt,
-                                  union upb_wire_value *wv, uint8_t **outbuf);
+// a tag that was just parsed) and returns a pointer to one past the data that
+// was read.
+uint8_t *upb_parse_wire_value(uint8_t *buf, uint8_t *end, upb_wire_type_t wt,
+                              union upb_wire_value *wv,
+                              struct upb_status *status);
 
 /* Functions to read wire values. *********************************************/
 
 // Most clients will not want to use these directly.
 
-upb_status_t upb_get_v_uint64_t_full(uint8_t *buf, uint8_t *end, uint64_t *val,
-                                     uint8_t **outbuf);
+uint8_t *upb_get_v_uint64_t_full(uint8_t *buf, uint8_t *end, uint64_t *val,
+                                 struct upb_status *status);
 
 // Gets a varint (wire type: UPB_WIRE_TYPE_VARINT).
-INLINE upb_status_t upb_get_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t *val,
-                                       uint8_t **outbuf)
+INLINE uint8_t *upb_get_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t *val,
+                                   struct upb_status *status)
 {
   // We inline this common case (1-byte varints), if that fails we dispatch to
   // the full (non-inlined) version.
   if((*buf &amp; 0x80) == 0) {
     *val = *buf &amp; 0x7f;
-    *outbuf = buf + 1;
-    return UPB_STATUS_OK;
+    return buf + 1;
   } else {
-    return upb_get_v_uint64_t_full(buf, end, val, outbuf);
+    return upb_get_v_uint64_t_full(buf, end, val, status);
   }
 }
 
 // Gets a varint -- called when we only need 32 bits of it.
-INLINE upb_status_t upb_get_v_uint32_t(uint8_t *buf, uint8_t *end,
-                                       uint32_t *val, uint8_t **outbuf)
+INLINE uint8_t *upb_get_v_uint32_t(uint8_t *buf, uint8_t *end,
+                                   uint32_t *val, struct upb_status *status)
 {
   uint64_t val64;
-  UPB_CHECK(upb_get_v_uint64_t(buf, end, &amp;val64, outbuf));
+  uint8_t *ret = upb_get_v_uint64_t(buf, end, &amp;val64, status);
   *val = (uint32_t)val64;  // Discard the high bits.
-  return UPB_STATUS_OK;
+  return ret;
 }
 
 // Gets a fixed-length 32-bit integer (wire type: UPB_WIRE_TYPE_32BIT).
-INLINE upb_status_t upb_get_f_uint32_t(uint8_t *buf, uint8_t *end,
-                                       uint32_t *val, uint8_t **outbuf)
+INLINE uint8_t *upb_get_f_uint32_t(uint8_t *buf, uint8_t *end,
+                                   uint32_t *val, struct upb_status *status)
 {
   uint8_t *uint32_end = buf + sizeof(uint32_t);
-  if(uint32_end &gt; end) return UPB_STATUS_NEED_MORE_DATA;
+  if(uint32_end &gt; end) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    return end;
+  }
 #if UPB_UNALIGNED_READS_OK
   *val = *(uint32_t*)buf;
 #else
@@ -166,16 +169,18 @@ INLINE upb_status_t upb_get_f_uint32_t(uint8_t *buf, uint8_t *end,
   *val = SHL(buf[0], 0) | SHL(buf[1], 8) | SHL(buf[2], 16) | SHL(buf[3], 24);
 #undef SHL
 #endif
-  *outbuf = uint32_end;
-  return UPB_STATUS_OK;
+  return uint32_end;
 }
 
 // Gets a fixed-length 64-bit integer (wire type: UPB_WIRE_TYPE_64BIT).
-INLINE upb_status_t upb_get_f_uint64_t(uint8_t *buf, uint8_t *end,
-                                       uint64_t *val, uint8_t **outbuf)
+INLINE uint8_t *upb_get_f_uint64_t(uint8_t *buf, uint8_t *end,
+                                   uint64_t *val, struct upb_status *status)
 {
   uint8_t *uint64_end = buf + sizeof(uint64_t);
-  if(uint64_end &gt; end) return UPB_STATUS_NEED_MORE_DATA;
+  if(uint64_end &gt; end) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    return end;
+  }
 #if UPB_UNALIGNED_READS_OK
   *val = *(uint64_t*)buf;
 #else
@@ -184,39 +189,47 @@ INLINE upb_status_t upb_get_f_uint64_t(uint8_t *buf, uint8_t *end,
          SHL(buf[4], 32) | SHL(buf[5], 40) | SHL(buf[6], 48) | SHL(buf[7], 56);
 #undef SHL
 #endif
-  *outbuf = uint64_end;
-  return UPB_STATUS_OK;
+  return uint64_end;
 }
 
-INLINE upb_status_t upb_skip_v_uint64_t(uint8_t *buf, uint8_t *end,
-                                        uint8_t **outbuf)
+INLINE uint8_t *upb_skip_v_uint64_t(uint8_t *buf, uint8_t *end,
+                                    struct upb_status *status)
 {
   uint8_t *const maxend = buf + 10;
   uint8_t last = 0x80;
   for(; buf &lt; (uint8_t*)end &amp;&amp; (last &amp; 0x80); buf++)
     last = *buf;
-  if(buf &gt;= end &amp;&amp; buf &lt;= maxend &amp;&amp; (last &amp; 0x80)) return UPB_STATUS_NEED_MORE_DATA;
-  if(buf &gt; maxend) return UPB_ERROR_UNTERMINATED_VARINT;
-  *outbuf = buf;
-  return UPB_STATUS_OK;
+
+  if(buf &gt;= end &amp;&amp; buf &lt;= maxend &amp;&amp; (last &amp; 0x80)) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    buf = end;
+  } else if(buf &gt; maxend) {
+    status-&gt;code = UPB_ERROR_UNTERMINATED_VARINT;
+    buf = end;
+  }
+  return buf;
 }
 
-INLINE upb_status_t upb_skip_f_uint32_t(uint8_t *buf, uint8_t *end,
-                                        uint8_t **outbuf)
+INLINE uint8_t *upb_skip_f_uint32_t(uint8_t *buf, uint8_t *end,
+                                    struct upb_status *status)
 {
   uint8_t *uint32_end = buf + sizeof(uint32_t);
-  if(uint32_end &gt; end) return UPB_STATUS_NEED_MORE_DATA;
-  *outbuf = uint32_end;
-  return UPB_STATUS_OK;
+  if(uint32_end &gt; end) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    return end;
+  }
+  return uint32_end;
 }
 
-INLINE upb_status_t upb_skip_f_uint64_t(uint8_t *buf, uint8_t *end,
-                                        uint8_t **outbuf)
+INLINE uint8_t *upb_skip_f_uint64_t(uint8_t *buf, uint8_t *end,
+                                    struct upb_status *status)
 {
   uint8_t *uint64_end = buf + sizeof(uint64_t);
-  if(uint64_end &gt; end) return UPB_STATUS_NEED_MORE_DATA;
-  *outbuf = uint64_end;
-  return UPB_STATUS_OK;
+  if(uint64_end &gt; end) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    return end;
+  }
+  return uint64_end;
 }
 
 
@@ -232,9 +245,10 @@ INLINE int64_t upb_zzdec_64(uint64_t n) { return (n &gt;&gt; 1) ^ -(int64_t)(n &amp; 1); }
 //  // Reads and converts a .proto value from buf, placing it in d.
 //  // &quot;end&quot; indicates the end of the current buffer (if the buffer does
 //  // not contain the entire value UPB_STATUS_NEED_MORE_DATA is returned).
-//  // On success, *outbuf will point to the first byte that was not consumed.
-//  upb_status_t upb_get_INT32(uint8_t *buf, uint8_t *end, int32_t *d,
-//                             uint8_t **outbuf);
+//  // On success, a pointer will be returned to the first byte that was
+//  // not consumed.
+//  uint8_t *upb_get_INT32(uint8_t *buf, uint8_t *end, int32_t *d,
+//                         struct upb_status *status);
 //
 //  // Given an already read wire value s (source), convert it to a .proto
 //  // value and return it.
@@ -247,12 +261,12 @@ INLINE int64_t upb_zzdec_64(uint64_t n) { return (n &gt;&gt; 1) ^ -(int64_t)(n &amp; 1); }
   INLINE val_t upb_wvtov_ ## type(wire_t s)
 
 #define GET(type, v_or_f, wire_t, val_t, member_name) \
-  INLINE upb_status_t upb_get_ ## type(uint8_t *buf, uint8_t *end, val_t *d, \
-                                       uint8_t **outbuf) { \
-    wire_t tmp; \
-    UPB_CHECK(upb_get_ ## v_or_f ## _ ## wire_t(buf, end, &amp;tmp, outbuf)); \
+  INLINE uint8_t *upb_get_ ## type(uint8_t *buf, uint8_t *end, val_t *d, \
+                                   struct upb_status *status) { \
+    wire_t tmp = 0; \
+    uint8_t *ret = upb_get_ ## v_or_f ## _ ## wire_t(buf, end, &amp;tmp, status); \
     *d = upb_wvtov_ ## type(tmp); \
-    return UPB_STATUS_OK; \
+    return ret; \
   }
 
 #define T(type, v_or_f, wire_t, val_t, member_name) \
@@ -288,14 +302,14 @@ T(FLOAT,    f, uint32_t, float,    _float)  {
 #undef T
 
 // Parses a tag, places the result in *tag.
-INLINE upb_status_t parse_tag(uint8_t *buf, uint8_t *end, struct upb_tag *tag,
-                              uint8_t **outbuf)
+INLINE uint8_t *parse_tag(uint8_t *buf, uint8_t *end, struct upb_tag *tag,
+                          struct upb_status *status)
 {
   uint32_t tag_int;
-  UPB_CHECK(upb_get_v_uint32_t(buf, end, &amp;tag_int, outbuf));
+  uint8_t *ret = upb_get_v_uint32_t(buf, end, &amp;tag_int, status);
   tag-&gt;wire_type    = (upb_wire_type_t)(tag_int &amp; 0x07);
   tag-&gt;field_number = tag_int &gt;&gt; 3;
-  return UPB_STATUS_OK;
+  return ret;
 }
 
 #ifdef __cplusplus</diff>
      <filename>src/upb_parse.h</filename>
    </modified>
    <modified>
      <diff>@@ -30,33 +30,35 @@ extern &quot;C&quot; {
 /* Functions to write wire values. ********************************************/
 
 /* Puts a varint (wire type: UPB_WIRE_TYPE_VARINT). */
-INLINE upb_status_t upb_put_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t val,
-                                       uint8_t **outbuf)
+INLINE uint8_t *upb_put_v_uint64_t(uint8_t *buf, uint8_t *end, uint64_t val,
+                                   struct upb_status *status)
 {
   do {
     uint8_t byte = val &amp; 0x7f;
     val &gt;&gt;= 7;
     if(val) byte |= 0x80;
-    if(buf &gt;= end) return UPB_STATUS_NEED_MORE_DATA;
+    if(buf &gt;= end) {
+      status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+      return end;
+    }
     *buf++ = byte;
   } while(val);
-  *outbuf = buf;
-  return UPB_STATUS_OK;
+  return buf;
 }
 
 /* Puts an unsigned 32-bit varint, verbatim.  Never uses the high 64 bits. */
-INLINE upb_status_t upb_put_v_uint32_t(uint8_t *buf, uint8_t *end,
-                                       uint32_t val, uint8_t **outbuf)
+INLINE uint8_t *upb_put_v_uint32_t(uint8_t *buf, uint8_t *end,
+                                   uint32_t val, struct upb_status *status)
 {
-  return upb_put_v_uint64_t(buf, end, val, outbuf);
+  return upb_put_v_uint64_t(buf, end, val, status);
 }
 
 /* Puts a signed 32-bit varint, first sign-extending to 64-bits.  We do this to
  * maintain wire-compatibility with 64-bit signed integers. */
-INLINE upb_status_t upb_put_v_int32_t(uint8_t *buf, uint8_t *end,
-                                      int32_t val, uint8_t **outbuf)
+INLINE uint8_t *upb_put_v_int32_t(uint8_t *buf, uint8_t *end,
+                                  int32_t val, struct upb_status *status)
 {
-  return upb_put_v_uint64_t(buf, end, (int64_t)val, outbuf);
+  return upb_put_v_uint64_t(buf, end, (int64_t)val, status);
 }
 
 INLINE void upb_put32(uint8_t *buf, uint32_t val) {
@@ -67,34 +69,38 @@ INLINE void upb_put32(uint8_t *buf, uint32_t val) {
 }
 
 /* Puts a fixed-length 32-bit integer (wire type: UPB_WIRE_TYPE_32BIT). */
-INLINE upb_status_t upb_put_f_uint32_t(uint8_t *buf, uint8_t *end,
-                                       uint32_t val, uint8_t **outbuf)
+INLINE uint8_t *upb_put_f_uint32_t(uint8_t *buf, uint8_t *end,
+                                   uint32_t val, struct upb_status *status)
 {
   uint8_t *uint32_end = buf + sizeof(uint32_t);
-  if(uint32_end &gt; end) return UPB_STATUS_NEED_MORE_DATA;
+  if(uint32_end &gt; end) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    return end;
+  }
 #if UPB_UNALIGNED_READS_OK
   *(uint32_t*)buf = val;
 #else
   upb_put32(buf, val);
 #endif
-  *outbuf = uint32_end;
-  return UPB_STATUS_OK;
+  return uint32_end;
 }
 
 /* Puts a fixed-length 64-bit integer (wire type: UPB_WIRE_TYPE_64BIT). */
-INLINE upb_status_t upb_put_f_uint64_t(uint8_t *buf, uint8_t *end,
-                                       uint64_t val, uint8_t **outbuf)
+INLINE uint8_t *upb_put_f_uint64_t(uint8_t *buf, uint8_t *end,
+                                   uint64_t val, struct upb_status *status)
 {
   uint8_t *uint64_end = buf + sizeof(uint64_t);
-  if(uint64_end &gt; end) return UPB_STATUS_NEED_MORE_DATA;
+  if(uint64_end &gt; end) {
+    status-&gt;code = UPB_STATUS_NEED_MORE_DATA;
+    return end;
+  }
 #if UPB_UNALIGNED_READS_OK
   *(uint64_t*)buf = val;
 #else
   upb_put32(buf, (uint32_t)val);
   upb_put32(buf, (uint32_t)(val &gt;&gt; 32));
 #endif
-  *outbuf = uint64_end;
-  return UPB_STATUS_OK;
+  return uint64_end;
 }
 
 INLINE size_t upb_v_uint64_t_size(uint64_t val) {
@@ -136,8 +142,8 @@ INLINE uint64_t upb_zzenc_64(int64_t n) { return (n &lt;&lt; 1) ^ (n &gt;&gt; 63); }
  *  // of the current available buffer (if the buffer does not contain enough
  *  // space UPB_STATUS_NEED_MORE_DATA is returned).  On success, *outbuf will
  *  // point one past the data that was written.
- *  upb_status_t upb_put_INT32(uint8_t *buf, uint8_t *end, int32_t val,
- *                             uint8_t **outbuf);
+ *  uint8_t *upb_put_INT32(uint8_t *buf, uint8_t *end, int32_t val,
+ *                         struct upb_status *status);
  *
  *  // Returns the number of bytes required to serialize val.
  *  size_t upb_get_INT32_size(int32_t val);
@@ -150,11 +156,10 @@ INLINE uint64_t upb_zzenc_64(int64_t n) { return (n &lt;&lt; 1) ^ (n &gt;&gt; 63); }
   INLINE wire_t upb_vtowv_ ## type(val_t s)
 
 #define PUT(type, v_or_f, wire_t, val_t, member_name) \
-  INLINE upb_status_t upb_put_ ## type(uint8_t *buf, uint8_t *end, val_t val, \
-                                       uint8_t **outbuf) { \
+  INLINE uint8_t *upb_put_ ## type(uint8_t *buf, uint8_t *end, val_t val, \
+                                   struct upb_status *status) { \
     wire_t tmp = upb_vtowv_ ## type(val); \
-    UPB_CHECK(upb_put_ ## v_or_f ## _ ## wire_t(buf, end, tmp, outbuf)); \
-    return UPB_STATUS_OK; \
+    return upb_put_ ## v_or_f ## _ ## wire_t(buf, end, tmp, status); \
   }
 
 #define T(type, v_or_f, wire_t, val_t, member_name) \
@@ -195,8 +200,8 @@ INLINE size_t upb_get_tag_size(uint32_t fieldnum) {
   return upb_v_uint64_t_size((uint64_t)fieldnum &lt;&lt; 3);
 }
 
-upb_status_t upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
-                                 union upb_value_ptr v, uint8_t **outbuf);
+uint8_t *upb_serialize_value(uint8_t *buf, uint8_t *end, upb_field_type_t ft,
+                             union upb_value_ptr v, struct upb_status *status);
 
 #ifdef __cplusplus
 }  /* extern &quot;C&quot; */</diff>
      <filename>src/upb_serialize.h</filename>
    </modified>
    <modified>
      <diff>@@ -168,7 +168,9 @@ void parse_and_compare(MESSAGE_CIDENT *proto2_msg, struct upb_msg *upb_msg,
 {
   // Parse to both proto2 and upb.
   ASSERT(proto2_msg-&gt;ParseFromArray(str-&gt;ptr, str-&gt;byte_len));
-  ASSERT(upb_msg_parsestr(upb_msg, str-&gt;ptr, str-&gt;byte_len) == UPB_STATUS_OK);
+  struct upb_status status = UPB_STATUS_INIT;
+  upb_msg_parsestr(upb_msg, str-&gt;ptr, str-&gt;byte_len, &amp;status);
+  ASSERT(upb_ok(&amp;status));
   compare(*proto2_msg, upb_msg);
 }
 
@@ -189,14 +191,17 @@ int main(int argc, char *argv[])
   }
 
   // Initialize upb state, parse descriptor.
+  struct upb_status status = UPB_STATUS_INIT;
   struct upb_context *c = upb_context_new();
   struct upb_string *fds = upb_strreadfile(MESSAGE_DESCRIPTOR_FILE);
   if(!fds) {
     fprintf(stderr, &quot;Couldn't read &quot; MESSAGE_DESCRIPTOR_FILE &quot;.\n&quot;);
     return 1;
   }
-  if(!upb_context_parsefds(c, fds)) {
-    fprintf(stderr, &quot;Error importing &quot; MESSAGE_DESCRIPTOR_FILE &quot;.\n&quot;);
+  upb_context_parsefds(c, fds, &amp;status);
+  if(!upb_ok(&amp;status)) {
+    fprintf(stderr, &quot;Error importing &quot; MESSAGE_DESCRIPTOR_FILE &quot;: %s.\n&quot;,
+            status.msg);
     return 1;
   }
   upb_string_unref(fds);</diff>
      <filename>tests/test_vs_proto2.cc</filename>
    </modified>
    <modified>
      <diff>@@ -15,18 +15,18 @@ int num_assertions = 0;
 static void test_get_v_uint64_t()
 {
 #define TEST(name, bytes, val) {\
-    upb_status_t status; \
+    struct upb_status status = UPB_STATUS_INIT; \
     uint8_t name[] = bytes; \
     uint8_t *name ## _buf = name; \
     uint64_t name ## _val = 0; \
-    status = upb_get_v_uint64_t(name, name + sizeof(name) - 1, &amp;name ## _val, &amp;name ## _buf); \
-    ASSERT(status == UPB_STATUS_OK); \
+    name ## _buf = upb_get_v_uint64_t(name, name + sizeof(name) - 1, &amp;name ## _val, &amp;status); \
+    ASSERT(upb_ok(&amp;status)); \
     ASSERT(name ## _val == val); \
     ASSERT(name ## _buf == name + sizeof(name) - 1);  /* - 1 for NULL */ \
     /* Test NEED_MORE_DATA. */ \
     if(sizeof(name) &gt; 2) { \
-      status = upb_get_v_uint64_t(name, name + sizeof(name) - 2, &amp;name ## _val, &amp;name ## _buf); \
-      ASSERT(status == UPB_STATUS_NEED_MORE_DATA); \
+      name ## _buf = upb_get_v_uint64_t(name, name + sizeof(name) - 2, &amp;name ## _val, &amp;status); \
+      ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA); \
     } \
   }
 
@@ -44,18 +44,18 @@ static void test_get_v_uint64_t()
 #undef TEST
 
   uint8_t twelvebyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01};
-  uint8_t *twelvebyte_buf = twelvebyte;
   uint64_t twelvebyte_val = 0;
-  upb_status_t status;
+  struct upb_status status = UPB_STATUS_INIT;
   /* A varint that terminates before hitting the end of the provided buffer,
    * but in too many bytes (11 instead of 10). */
-  status = upb_get_v_uint64_t(twelvebyte_buf, twelvebyte + 12, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT);
+  upb_get_v_uint64_t(twelvebyte, twelvebyte + 12, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
 
   /* A varint that terminates simultaneously with the end of the provided
    * buffer, but in too many bytes (11 instead of 10). */
-  status = upb_get_v_uint64_t(twelvebyte_buf, twelvebyte + 11, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT);
+  upb_reset(&amp;status);
+  upb_get_v_uint64_t(twelvebyte, twelvebyte + 11, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
 
   /* A varint whose buffer ends on exactly the byte where the varint must
    * terminate, but the final byte does not terminate.  The absolutely most
@@ -66,29 +66,31 @@ static void test_get_v_uint64_t()
    * then receive a UPB_ERROR_UNTERMINATED_VARINT error; clients who have no
    * more data to supply will (rightly) conclude that their protobuf is corrupt.
    */
-  status = upb_get_v_uint64_t(twelvebyte_buf, twelvebyte + 10, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT ||
-         status == UPB_STATUS_NEED_MORE_DATA);
+  upb_reset(&amp;status);
+  upb_get_v_uint64_t(twelvebyte, twelvebyte + 10, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT ||
+         status.code == UPB_STATUS_NEED_MORE_DATA);
 
-  status = upb_get_v_uint64_t(twelvebyte_buf, twelvebyte + 9, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_STATUS_NEED_MORE_DATA);
+  upb_reset(&amp;status);
+  upb_get_v_uint64_t(twelvebyte, twelvebyte + 9, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
 }
 
 static void test_get_v_uint32_t()
 {
 #define TEST(name, bytes, val) {\
-    upb_status_t status; \
+    struct upb_status status = UPB_STATUS_INIT; \
     uint8_t name[] = bytes; \
     uint8_t *name ## _buf = name; \
     uint32_t name ## _val = 0; \
-    status = upb_get_v_uint32_t(name, name + sizeof(name), &amp;name ## _val, &amp;name ## _buf); \
-    ASSERT(status == UPB_STATUS_OK); \
+    name ## _buf = upb_get_v_uint32_t(name, name + sizeof(name), &amp;name ## _val, &amp;status); \
+    ASSERT(upb_ok(&amp;status)); \
     ASSERT(name ## _val == val); \
     ASSERT(name ## _buf == name + sizeof(name) - 1);  /* - 1 for NULL */ \
     /* Test NEED_MORE_DATA. */ \
     if(sizeof(name) &gt; 2) { \
-      status = upb_get_v_uint32_t(name, name + sizeof(name) - 2, &amp;name ## _val, &amp;name ## _buf); \
-      ASSERT(status == UPB_STATUS_NEED_MORE_DATA); \
+      name ## _buf = upb_get_v_uint32_t(name, name + sizeof(name) - 2, &amp;name ## _val, &amp;status); \
+      ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA); \
     } \
   }
 
@@ -107,18 +109,18 @@ static void test_get_v_uint32_t()
 #undef TEST
 
   uint8_t twelvebyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01};
-  uint8_t *twelvebyte_buf = twelvebyte;
   uint32_t twelvebyte_val = 0;
-  upb_status_t status;
+  struct upb_status status = UPB_STATUS_INIT;
   /* A varint that terminates before hitting the end of the provided buffer,
    * but in too many bytes (11 instead of 10). */
-  status = upb_get_v_uint32_t(twelvebyte_buf, twelvebyte + 12, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT);
+  upb_get_v_uint32_t(twelvebyte, twelvebyte + 12, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
 
   /* A varint that terminates simultaneously with the end of the provided
    * buffer, but in too many bytes (11 instead of 10). */
-  status = upb_get_v_uint32_t(twelvebyte_buf, twelvebyte + 11, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT);
+  upb_reset(&amp;status);
+  upb_get_v_uint32_t(twelvebyte, twelvebyte + 11, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
 
   /* A varint whose buffer ends on exactly the byte where the varint must
    * terminate, but the final byte does not terminate.  The absolutely most
@@ -129,27 +131,29 @@ static void test_get_v_uint32_t()
    * then receive a UPB_ERROR_UNTERMINATED_VARINT error; clients who have no
    * more data to supply will (rightly) conclude that their protobuf is corrupt.
    */
-  status = upb_get_v_uint32_t(twelvebyte_buf, twelvebyte + 10, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT ||
-         status == UPB_STATUS_NEED_MORE_DATA);
+  upb_reset(&amp;status);
+  upb_get_v_uint32_t(twelvebyte, twelvebyte + 10, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT ||
+         status.code == UPB_STATUS_NEED_MORE_DATA);
 
-  status = upb_get_v_uint32_t(twelvebyte_buf, twelvebyte + 9, &amp;twelvebyte_val, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_STATUS_NEED_MORE_DATA);
+  upb_reset(&amp;status);
+  upb_get_v_uint32_t(twelvebyte, twelvebyte + 9, &amp;twelvebyte_val, &amp;status);
+  ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
 }
 
 static void test_skip_v_uint64_t()
 {
 #define TEST(name, bytes) {\
-    upb_status_t status; \
+    struct upb_status status = UPB_STATUS_INIT; \
     uint8_t name[] = bytes; \
     uint8_t *name ## _buf = name; \
-    status = upb_skip_v_uint64_t(name ## _buf, name + sizeof(name), &amp;name ## _buf); \
-    ASSERT(status == UPB_STATUS_OK); \
+    name ## _buf = upb_skip_v_uint64_t(name ## _buf, name + sizeof(name), &amp;status); \
+    ASSERT(upb_ok(&amp;status)); \
     ASSERT(name ## _buf == name + sizeof(name) - 1);  /* - 1 for NULL */ \
     /* Test NEED_MORE_DATA. */ \
     if(sizeof(name) &gt; 2) { \
-      status = upb_skip_v_uint64_t(name, name + sizeof(name) - 2, &amp;name ## _buf); \
-      ASSERT(status == UPB_STATUS_NEED_MORE_DATA); \
+      name ## _buf = upb_skip_v_uint64_t(name, name + sizeof(name) - 2, &amp;status); \
+      ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA); \
     } \
   }
 
@@ -167,17 +171,17 @@ static void test_skip_v_uint64_t()
 #undef TEST
 
   uint8_t twelvebyte[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x01};
-  uint8_t *twelvebyte_buf = twelvebyte;
-  upb_status_t status;
+  struct upb_status status = UPB_STATUS_INIT;
   /* A varint that terminates before hitting the end of the provided buffer,
    * but in too many bytes (11 instead of 10). */
-  status = upb_skip_v_uint64_t(twelvebyte_buf, twelvebyte + 12, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT);
+  upb_skip_v_uint64_t(twelvebyte, twelvebyte + 12, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
 
   /* A varint that terminates simultaneously with the end of the provided
    * buffer, but in too many bytes (11 instead of 10). */
-  status = upb_skip_v_uint64_t(twelvebyte_buf, twelvebyte + 11, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT);
+  upb_reset(&amp;status);
+  upb_skip_v_uint64_t(twelvebyte, twelvebyte + 11, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT);
 
   /* A varint whose buffer ends on exactly the byte where the varint must
    * terminate, but the final byte does not terminate.  The absolutely most
@@ -188,23 +192,25 @@ static void test_skip_v_uint64_t()
    * then receive a UPB_ERROR_UNTERMINATED_VARINT error; clients who have no
    * more data to supply will (rightly) conclude that their protobuf is corrupt.
    */
-  status = upb_skip_v_uint64_t(twelvebyte_buf, twelvebyte + 10, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_ERROR_UNTERMINATED_VARINT ||
-         status == UPB_STATUS_NEED_MORE_DATA);
+  upb_reset(&amp;status);
+  upb_skip_v_uint64_t(twelvebyte, twelvebyte + 10, &amp;status);
+  ASSERT(status.code == UPB_ERROR_UNTERMINATED_VARINT ||
+         status.code == UPB_STATUS_NEED_MORE_DATA);
 
-  status = upb_skip_v_uint64_t(twelvebyte_buf, twelvebyte + 9, &amp;twelvebyte_buf);
-  ASSERT(status == UPB_STATUS_NEED_MORE_DATA);
+  upb_reset(&amp;status);
+  upb_skip_v_uint64_t(twelvebyte, twelvebyte + 9, &amp;status);
+  ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
 }
 
 static void test_get_f_uint32_t()
 {
 #define TEST(name, bytes, val) {\
-    upb_status_t status; \
+    struct upb_status status = UPB_STATUS_INIT; \
     uint8_t name[] = bytes; \
     uint8_t *name ## _buf = name; \
     uint32_t name ## _val = 0; \
-    status = upb_get_f_uint32_t(name ## _buf, name + sizeof(name), &amp;name ## _val, &amp;name ## _buf); \
-    ASSERT(status == UPB_STATUS_OK); \
+    name ## _buf = upb_get_f_uint32_t(name ## _buf, name + sizeof(name), &amp;name ## _val, &amp;status); \
+    ASSERT(upb_ok(&amp;status)); \
     ASSERT(name ## _val == val); \
     ASSERT(name ## _buf == name + sizeof(name) - 1);  /* - 1 for NULL */ \
   }
@@ -213,10 +219,10 @@ static void test_get_f_uint32_t()
   TEST(one,   &quot;\x01\x00\x00\x00&quot;,                                0x1UL);
 
   uint8_t threeb[] = {0x00, 0x00, 0x00};
-  uint8_t *threeb_buf = threeb;
   uint32_t threeb_val;
-  upb_status_t status = upb_get_f_uint32_t(threeb, threeb + sizeof(threeb), &amp;threeb_val, &amp;threeb_buf);
-  ASSERT(status == UPB_STATUS_NEED_MORE_DATA);
+  struct upb_status status = UPB_STATUS_INIT;
+  upb_get_f_uint32_t(threeb, threeb + sizeof(threeb), &amp;threeb_val, &amp;status);
+  ASSERT(status.code == UPB_STATUS_NEED_MORE_DATA);
 
 #undef TEST
 }
@@ -243,7 +249,9 @@ static void test_cbparser()
   ASSERT(p);
   upb_cbparser_reset(p, NULL, tag_cb, NULL, NULL, NULL, NULL);
   size_t read;
-  ASSERT(upb_cbparser_parse(p, NULL, 0, &amp;read) == UPB_STATUS_OK);
+  struct upb_status status = UPB_STATUS_INIT;
+  read = upb_cbparser_parse(p, NULL, 0, &amp;status);
+  ASSERT(upb_ok(&amp;status));
   ASSERT(read == 0);
 }
 </diff>
      <filename>tests/tests.c</filename>
    </modified>
    <modified>
      <diff>@@ -10,6 +10,7 @@
 
 #include &lt;ctype.h&gt;
 #include &lt;inttypes.h&gt;
+#include &lt;stdarg.h&gt;
 #include &quot;descriptor.h&quot;
 #include &quot;upb_context.h&quot;
 #include &quot;upb_enum.h&quot;
@@ -632,9 +633,13 @@ void usage_err(char *err)
   exit(1);
 }
 
-void error(char *err)
+void error(char *err, ...)
 {
-  fprintf(stderr, &quot;upbc: %s\n\n&quot;, err);
+  va_list args;
+  va_start(args, err);
+  fprintf(stderr, &quot;upbc: &quot;);
+  vfprintf(stderr, err, args);
+  va_end(args);
   exit(1);
 }
 
@@ -680,12 +685,15 @@ int main(int argc, char *argv[])
   /* Parse input file. */
   struct upb_context *c = upb_context_new();
   struct upb_msg *fds_msg = upb_msg_new(c-&gt;fds_msg);
-  if(upb_msg_parsestr(fds_msg, descriptor-&gt;ptr, descriptor-&gt;byte_len) != UPB_STATUS_OK)
-    error(&quot;Failed to parse input file descriptor.&quot;);
+  struct upb_status status = UPB_STATUS_INIT;
+  upb_msg_parsestr(fds_msg, descriptor-&gt;ptr, descriptor-&gt;byte_len, &amp;status);
+  if(!upb_ok(&amp;status))
+    error(&quot;Failed to parse input file descriptor: %s&quot;, status.msg);
   //upb_msg_print(fds_msg, false, stderr);
   google_protobuf_FileDescriptorSet *fds = (void*)fds_msg;
-  if(!upb_context_addfds(c, fds))
-    error(&quot;Failed to resolve symbols in descriptor.\n&quot;);
+  upb_context_addfds(c, fds, &amp;status);
+  if(!upb_ok(&amp;status))
+    error(&quot;Failed to resolve symbols in descriptor: %s&quot;, status.msg);
 
   /* We need to sort the fields of all the descriptors.  They will already be
    * sorted in the upb_msgs that we base our header file output on, so we must</diff>
      <filename>tools/upbc.c</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>4b47002198f2c0404e16d2f02786845d6d3a0d3b</id>
    </parent>
  </parents>
  <author>
    <name>Joshua Haberman</name>
    <email>joshua@reverberate.org</email>
  </author>
  <url>http://github.com/haberman/upb/commit/33a68acb14759cb6fcf796b41ad001c93de4b8e4</url>
  <id>33a68acb14759cb6fcf796b41ad001c93de4b8e4</id>
  <committed-date>2009-09-26T11:46:38-07:00</committed-date>
  <authored-date>2009-09-26T11:46:38-07:00</authored-date>
  <message>Use a status object for errors so a message can be returned.

Also delay deletion of subfields until the entire message is
deleted.</message>
  <tree>f03543b04c0e5b7ce7f2650ff1330919d4d6e055</tree>
  <committer>
    <name>Joshua Haberman</name>
    <email>joshua@reverberate.org</email>
  </committer>
</commit>
