New PHP C Extension based on upb_msg (#7614)

* Added code for the new PHP extension.

* Removed a bunch of unused variables shown by compiler warnings.

* Test protobuf2 in the tests.

* Updated upb version to fix a goldenfile test.

* Added pure-PHP changes. Extension now passes all tests.

* Enabled protobuf2 for all C extension tests.

* Fixed pure=PHP lib: full names no longer start with '.'.

* Added files for new extension to Makefile.am.

* Downgraded make-preload.py to python 2, since python3 isn't available in the php_all Kokoro image.

* Disable tests of new C extension with PHP 5.x.

* Also do not compile the extension for PHP5.

* Accept version 5.*.*, and use /usr/bin/python.

* Addressed PR comments.

* Addressed PR comments.

* Added "const" to a parameter and fixed a memory leak seen in Valgrind.

* Stop testing the C extension for PHP5.

The next release of protobuf will deprecate the C extension
for PHP5, see:
https://github.com/protocolbuffers/protobuf/issues/7525

* Made the PHP5.6 Mac test only exercise pure-PHP.

* Build protoc for PHP 5.6 test.

* Rewrote bundling script in PHP to avoid dependency on Python.

* A few more fixes.

* Fixed int32/int64 behavior for 32-bit builds.

* Match more PHP versions in testing script.

* Use phpize --clean before building extension.

* Force-delete configure.in to avoid phpize problems cross-version.

* Delete both configure.ac and configure.in.
diff --git a/Makefile.am b/Makefile.am
index 46d44d9..569c0ef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -775,6 +775,27 @@
   php/ext/google/protobuf/upb.h                                       \
   php/ext/google/protobuf/utf8.c                                      \
   php/ext/google/protobuf/utf8.h                                      \
+  php/ext/google/protobuf2/arena.c                                    \
+  php/ext/google/protobuf2/arena.h                                    \
+  php/ext/google/protobuf2/array.c                                    \
+  php/ext/google/protobuf2/array.h                                    \
+  php/ext/google/protobuf2/bundled_php.h                              \
+  php/ext/google/protobuf2/config.m4                                  \
+  php/ext/google/protobuf2/convert.c                                  \
+  php/ext/google/protobuf2/convert.h                                  \
+  php/ext/google/protobuf2/def.c                                      \
+  php/ext/google/protobuf2/def.h                                      \
+  php/ext/google/protobuf2/make-preload.php                           \
+  php/ext/google/protobuf2/map.c                                      \
+  php/ext/google/protobuf2/map.h                                      \
+  php/ext/google/protobuf2/message.c                                  \
+  php/ext/google/protobuf2/message.h                                  \
+  php/ext/google/protobuf2/names.c                                    \
+  php/ext/google/protobuf2/names.h                                    \
+  php/ext/google/protobuf2/php-upb.c                                  \
+  php/ext/google/protobuf2/php-upb.h                                  \
+  php/ext/google/protobuf2/protobuf.c                                 \
+  php/ext/google/protobuf2/protobuf.h                                 \
   php/generate_descriptor_protos.sh                                   \
   php/phpunit.xml                                                     \
   php/release.sh                                                      \
diff --git a/php/ext/google/protobuf2/arena.c b/php/ext/google/protobuf2/arena.c
new file mode 100644
index 0000000..035dfca
--- /dev/null
+++ b/php/ext/google/protobuf2/arena.c
@@ -0,0 +1,95 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <Zend/zend_API.h>
+
+#include "php-upb.h"
+
+// -----------------------------------------------------------------------------
+// Arena
+// -----------------------------------------------------------------------------
+
+typedef struct Arena {
+  zend_object std;
+  upb_arena* arena;
+} Arena;
+
+zend_class_entry *Arena_class_entry;
+static zend_object_handlers Arena_object_handlers;
+
+// PHP Object Handlers /////////////////////////////////////////////////////////
+
+static zend_object* Arena_Create(zend_class_entry *class_type) {
+  Arena *intern = emalloc(sizeof(Arena));
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &Arena_object_handlers;
+  intern->arena = upb_arena_new();
+  // Skip object_properties_init(), we don't allow derived classes.
+  return &intern->std;
+}
+
+static void Arena_Free(zend_object* obj) {
+  Arena* intern = (Arena*)obj;
+  upb_arena_free(intern->arena);
+  zend_object_std_dtor(&intern->std);
+}
+
+// C Functions from arena.h ////////////////////////////////////////////////////
+
+void Arena_Init(zval* val) {
+  ZVAL_OBJ(val, Arena_Create(Arena_class_entry));
+}
+
+upb_arena *Arena_Get(zval *val) {
+  Arena *a = (Arena*)Z_OBJ_P(val);
+  return a->arena;
+}
+
+// -----------------------------------------------------------------------------
+// Module init.
+// -----------------------------------------------------------------------------
+
+// No public methods.
+static const zend_function_entry Arena_methods[] = {
+  ZEND_FE_END
+};
+
+void Arena_ModuleInit() {
+  zend_class_entry tmp_ce;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Arena", Arena_methods);
+  Arena_class_entry = zend_register_internal_class(&tmp_ce);
+  Arena_class_entry->create_object = Arena_Create;
+  Arena_class_entry->ce_flags |= ZEND_ACC_FINAL;
+
+  memcpy(&Arena_object_handlers, &std_object_handlers,
+         sizeof(zend_object_handlers));
+  Arena_object_handlers.free_obj = Arena_Free;
+}
diff --git a/php/ext/google/protobuf2/arena.h b/php/ext/google/protobuf2/arena.h
new file mode 100644
index 0000000..67e165d
--- /dev/null
+++ b/php/ext/google/protobuf2/arena.h
@@ -0,0 +1,47 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_ARENA_H_
+#define PHP_PROTOBUF_ARENA_H_
+
+#include <php.h>
+
+#include "php-upb.h"
+
+// Registers the PHP Arena class.
+void Arena_ModuleInit();
+
+// Creates and returns a new arena object that wraps a new upb_arena*.
+void Arena_Init(zval *val);
+
+// Gets the underlying upb_arena from this arena object.
+upb_arena *Arena_Get(zval *arena);
+
+#endif  // PHP_PROTOBUF_ARENA_H_
diff --git a/php/ext/google/protobuf2/array.c b/php/ext/google/protobuf2/array.c
new file mode 100644
index 0000000..571d3e4
--- /dev/null
+++ b/php/ext/google/protobuf2/array.c
@@ -0,0 +1,602 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "array.h"
+
+#include <Zend/zend_API.h>
+#include <Zend/zend_interfaces.h>
+
+#include <ext/spl/spl_iterators.h>
+
+// This is not self-contained: it must be after other Zend includes.
+#include <Zend/zend_exceptions.h>
+
+#include "arena.h"
+#include "convert.h"
+#include "def.h"
+#include "php-upb.h"
+#include "protobuf.h"
+
+static void RepeatedFieldIter_make(zval *val, zval *repeated_field);
+
+// -----------------------------------------------------------------------------
+// RepeatedField
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  zval arena;
+  upb_array *array;
+  upb_fieldtype_t type;
+  const Descriptor* desc;  // When values are messages.
+} RepeatedField;
+
+zend_class_entry *RepeatedField_class_entry;
+static zend_object_handlers RepeatedField_object_handlers;
+
+// PHP Object Handlers /////////////////////////////////////////////////////////
+
+/**
+ * RepeatedField_create()
+ *
+ * PHP class entry function to allocate and initialize a new RepeatedField
+ * object.
+ */
+static zend_object* RepeatedField_create(zend_class_entry *class_type) {
+  RepeatedField *intern = emalloc(sizeof(RepeatedField));
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &RepeatedField_object_handlers;
+  Arena_Init(&intern->arena);
+  intern->array = NULL;
+  intern->desc = NULL;
+  // Skip object_properties_init(), we don't allow derived classes.
+  return &intern->std;
+}
+
+/**
+ * RepeatedField_dtor()
+ *
+ * Object handler to destroy a RepeatedField. This releases all resources
+ * associated with the message. Note that it is possible to access a destroyed
+ * object from PHP in rare cases.
+ */
+static void RepeatedField_destructor(zend_object* obj) {
+  RepeatedField* intern = (RepeatedField*)obj;
+  ObjCache_Delete(intern->array);
+  zval_ptr_dtor(&intern->arena);
+  zend_object_std_dtor(&intern->std);
+}
+
+static HashTable *RepeatedField_GetProperties(zval *object TSRMLS_DC) {
+  return NULL;  // We do not have a properties table.
+}
+
+static zval *RepeatedField_GetPropertyPtrPtr(zval *object, zval *member,
+                                             int type, void **cache_slot) {
+  return NULL;  // We don't offer direct references to our properties.
+}
+
+// C Functions from array.h ////////////////////////////////////////////////////
+
+// These are documented in the header file.
+
+void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr,
+                                 const upb_fielddef *f, zval *arena) {
+  if (!arr) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(arr, val)) {
+    RepeatedField *intern = emalloc(sizeof(RepeatedField));
+    zend_object_std_init(&intern->std, RepeatedField_class_entry);
+    intern->std.handlers = &RepeatedField_object_handlers;
+    ZVAL_COPY(&intern->arena, arena);
+    intern->array = arr;
+    intern->type = upb_fielddef_type(f);
+    intern->desc = Descriptor_GetFromFieldDef(f);
+    // Skip object_properties_init(), we don't allow derived classes.
+    ObjCache_Add(intern->array, &intern->std);
+    ZVAL_OBJ(val, &intern->std);
+  }
+}
+
+upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f,
+                                     upb_arena *arena) {
+  if (Z_ISREF_P(val)) {
+    ZVAL_DEREF(val);
+  }
+
+  if (Z_TYPE_P(val) == IS_ARRAY) {
+    // Auto-construct, eg. [1, 2, 3] -> upb_array([1, 2, 3]).
+    upb_array *arr = upb_array_new(arena, upb_fielddef_type(f));
+    HashTable *table = HASH_OF(val);
+    HashPosition pos;
+    upb_fieldtype_t type = upb_fielddef_type(f);
+    const Descriptor *desc = Descriptor_GetFromFieldDef(f);
+
+    zend_hash_internal_pointer_reset_ex(table, &pos);
+
+    while (true) {
+      zval *zv = zend_hash_get_current_data_ex(table, &pos);
+      upb_msgval val;
+
+      if (!zv) return arr;
+
+      if (!Convert_PhpToUpbAutoWrap(zv, &val, type, desc, arena)) {
+        return NULL;
+      }
+
+      upb_array_append(arr, val, arena);
+      zend_hash_move_forward_ex(table, &pos);
+    }
+  } else if (Z_TYPE_P(val) == IS_OBJECT &&
+             Z_OBJCE_P(val) == RepeatedField_class_entry) {
+    // Unwrap existing RepeatedField object to get the upb_array* inside.
+    RepeatedField *intern = (RepeatedField*)Z_OBJ_P(val);
+    const Descriptor *desc = Descriptor_GetFromFieldDef(f);
+
+    if (intern->type != upb_fielddef_type(f) || intern->desc != desc) {
+      php_error_docref(NULL, E_USER_ERROR,
+                       "Wrong type for this repeated field.");
+    }
+
+    upb_arena_fuse(arena, Arena_Get(&intern->arena));
+    return intern->array;
+  } else {
+    php_error_docref(NULL, E_USER_ERROR, "Must be a repeated field");
+    return NULL;
+  }
+}
+
+// RepeatedField PHP methods ///////////////////////////////////////////////////
+
+/**
+ * RepeatedField::__construct()
+ *
+ * Constructs an instance of RepeatedField.
+ * @param long Type of the stored element.
+ * @param string Message/Enum class.
+ */
+PHP_METHOD(RepeatedField, __construct) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  zend_long type;
+  zend_class_entry* klass = NULL;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|C", &type, &klass) != SUCCESS) {
+    return;
+  }
+
+  intern->type = pbphp_dtype_to_type(type);
+  intern->desc = Descriptor_GetFromClassEntry(klass);
+
+  if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) {
+    php_error_docref(NULL, E_USER_ERROR,
+                     "Message/enum type must have concrete class.");
+    return;
+  }
+
+  intern->array = upb_array_new(arena, intern->type);
+  ObjCache_Add(intern->array, &intern->std);
+}
+
+/**
+ * RepeatedField::append()
+ *
+ * Append element to the end of the repeated field.
+ * @param object The element to be added.
+ */
+PHP_METHOD(RepeatedField, append) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  zval *php_val;
+  upb_msgval msgval;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &php_val) != SUCCESS ||
+      !Convert_PhpToUpb(php_val, &msgval, intern->type, intern->desc, arena)) {
+    return;
+  }
+
+  upb_array_append(intern->array, msgval, arena);
+}
+
+/**
+ * RepeatedField::offsetExists()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   isset($arr[$idx]);
+ *   empty($arr[$idx]);
+ *
+ * @param long The index to be checked.
+ * @return bool True if the element at the given index exists.
+ */
+PHP_METHOD(RepeatedField, offsetExists) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+  zend_long index;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
+    return;
+  }
+
+  RETURN_BOOL(index >= 0 && index < upb_array_size(intern->array));
+}
+
+/**
+ * RepeatedField::offsetGet()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   $x = $arr[$idx];
+ *
+ * @param long The index of the element to be fetched.
+ * @return object The stored element at given index.
+ * @exception Invalid type for index.
+ * @exception Non-existing index.
+ */
+PHP_METHOD(RepeatedField, offsetGet) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+  zend_long index;
+  upb_msgval msgval;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
+    return;
+  }
+
+  if (index < 0 || index >= upb_array_size(intern->array)) {
+    zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index);
+    return;
+  }
+
+  msgval = upb_array_get(intern->array, index);
+  Convert_UpbToPhp(msgval, &ret, intern->type, intern->desc, &intern->arena);
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/**
+ * RepeatedField::offsetSet()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   $arr[$idx] = $x;
+ *   $arr []= $x;  // Append
+ *
+ * @param long The index of the element to be assigned.
+ * @param object The element to be assigned.
+ * @exception Invalid type for index.
+ * @exception Non-existing index.
+ * @exception Incorrect type of the element.
+ */
+PHP_METHOD(RepeatedField, offsetSet) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  size_t size = upb_array_size(intern->array);
+  zval *offset, *val;
+  int64_t index;
+  upb_msgval msgval;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &offset, &val) != SUCCESS) {
+    return;
+  }
+
+  if (Z_TYPE_P(offset) == IS_NULL) {
+    index = size;
+  } else if (!Convert_PhpToInt64(offset, &index)) {
+    return;
+  }
+
+  if (!Convert_PhpToUpb(val, &msgval, intern->type, intern->desc, arena)) {
+    return;
+  }
+
+  if (index > size) {
+    zend_error(E_USER_ERROR, "Element at index %ld doesn't exist.\n", index);
+  } else if (index == size) {
+    upb_array_append(intern->array, msgval, Arena_Get(&intern->arena));
+  } else {
+    upb_array_set(intern->array, index, msgval);
+  }
+}
+
+/**
+ * RepeatedField::offsetUnset()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   unset($arr[$idx]);
+ *
+ * @param long The index of the element to be removed.
+ * @exception Invalid type for index.
+ * @exception The element to be removed is not at the end of the RepeatedField.
+ */
+PHP_METHOD(RepeatedField, offsetUnset) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+  zend_long index;
+  zend_long size = upb_array_size(intern->array);
+
+  // Only the element at the end of the array can be removed.
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) != SUCCESS) {
+    return;
+  }
+
+  if (size == 0 || index != size - 1) {
+    php_error_docref(NULL, E_USER_ERROR, "Cannot remove element at %ld.\n",
+                     index);
+    return;
+  }
+
+  upb_array_resize(intern->array, size - 1, Arena_Get(&intern->arena));
+}
+
+/**
+ * RepeatedField::count()
+ *
+ * Implements the Countable interface. Invoked when PHP code calls:
+ *
+ *   $len = count($arr);
+ * Return the number of stored elements.
+ * This will also be called for: count($arr)
+ * @return long The number of stored elements.
+ */
+PHP_METHOD(RepeatedField, count) {
+  RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis());
+
+  if (zend_parse_parameters_none() == FAILURE) {
+    return;
+  }
+
+  RETURN_LONG(upb_array_size(intern->array));
+}
+
+/**
+ * RepeatedField::getIterator()
+ *
+ * Implements the IteratorAggregate interface. Invoked when PHP code calls:
+ *
+ *   foreach ($arr) {}
+ *
+ * @return object Beginning iterator.
+ */
+PHP_METHOD(RepeatedField, getIterator) {
+  zval ret;
+  RepeatedFieldIter_make(&ret, getThis());
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
+  ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
+  ZEND_ARG_INFO(0, index)
+  ZEND_ARG_INFO(0, newval)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
+ZEND_END_ARG_INFO()
+
+static zend_function_entry repeated_field_methods[] = {
+  PHP_ME(RepeatedField, __construct,  NULL,              ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, append,       NULL,              ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedField, getIterator,  arginfo_void,      ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// PHP RepeatedFieldIter
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  zval repeated_field;
+  zend_long position;
+} RepeatedFieldIter;
+
+zend_class_entry *RepeatedFieldIter_class_entry;
+static zend_object_handlers repeated_field_iter_object_handlers;
+
+/**
+ * RepeatedFieldIter_create()
+ *
+ * PHP class entry function to allocate and initialize a new RepeatedFieldIter
+ * object.
+ */
+zend_object* RepeatedFieldIter_create(zend_class_entry *class_type) {
+  RepeatedFieldIter *intern = emalloc(sizeof(RepeatedFieldIter));
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &repeated_field_iter_object_handlers;
+  ZVAL_NULL(&intern->repeated_field);
+  intern->position = 0;
+  // Skip object_properties_init(), we don't allow derived classes.
+  return &intern->std;
+}
+
+/**
+ * RepeatedFieldIter_dtor()
+ *
+ * Object handler to destroy a RepeatedFieldIter. This releases all resources
+ * associated with the message. Note that it is possible to access a destroyed
+ * object from PHP in rare cases.
+ */
+static void RepeatedFieldIter_dtor(zend_object* obj) {
+  RepeatedFieldIter* intern = (RepeatedFieldIter*)obj;
+  zval_ptr_dtor(&intern->repeated_field);
+  zend_object_std_dtor(&intern->std);
+}
+
+/**
+ * RepeatedFieldIter_make()
+ *
+ * C function to create a RepeatedFieldIter.
+ */
+static void RepeatedFieldIter_make(zval *val, zval *repeated_field) {
+  RepeatedFieldIter *iter;
+  ZVAL_OBJ(val, RepeatedFieldIter_class_entry->create_object(
+                    RepeatedFieldIter_class_entry));
+  iter = (RepeatedFieldIter*)Z_OBJ_P(val);
+  ZVAL_COPY(&iter->repeated_field, repeated_field);
+}
+
+/*
+ * When a user writes:
+ *
+ *   foreach($arr as $key => $val) {}
+ *
+ * PHP's iterator protocol is:
+ *
+ *   $iter = $arr->getIterator();
+ *   for ($iter->rewind(); $iter->valid(); $iter->next()) {
+ *     $key = $iter->key();
+ *     $val = $iter->current();
+ *   }
+ */
+
+/**
+ * RepeatedFieldIter::rewind()
+ *
+ * Implements the Iterator interface. Sets the iterator to the first element.
+ */
+PHP_METHOD(RepeatedFieldIter, rewind) {
+  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
+  intern->position = 0;
+}
+
+/**
+ * RepeatedFieldIter::current()
+ *
+ * Implements the Iterator interface. Returns the current value.
+ */
+PHP_METHOD(RepeatedFieldIter, current) {
+  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
+  RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field);
+  upb_array *array = field->array;
+  zend_long index = intern->position;
+  upb_msgval msgval;
+  zval ret;
+
+  if (index < 0 || index >= upb_array_size(array)) {
+    zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index);
+  }
+
+  msgval = upb_array_get(array, index);
+
+  Convert_UpbToPhp(msgval, &ret, field->type, field->desc, &field->arena);
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/**
+ * RepeatedFieldIter::key()
+ *
+ * Implements the Iterator interface. Returns the current key.
+ */
+PHP_METHOD(RepeatedFieldIter, key) {
+  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
+  RETURN_LONG(intern->position);
+}
+
+/**
+ * RepeatedFieldIter::next()
+ *
+ * Implements the Iterator interface. Advances to the next element.
+ */
+PHP_METHOD(RepeatedFieldIter, next) {
+  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
+  ++intern->position;
+}
+
+/**
+ * RepeatedFieldIter::valid()
+ *
+ * Implements the Iterator interface. Returns true if this is a valid element.
+ */
+PHP_METHOD(RepeatedFieldIter, valid) {
+  RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis());
+  RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field);
+  RETURN_BOOL(intern->position < upb_array_size(field->array));
+}
+
+static zend_function_entry repeated_field_iter_methods[] = {
+  PHP_ME(RepeatedFieldIter, rewind,      arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedFieldIter, current,     arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedFieldIter, key,         arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedFieldIter, next,        arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(RepeatedFieldIter, valid,       arginfo_void, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// Module init.
+// -----------------------------------------------------------------------------
+
+/**
+ * Array_ModuleInit()
+ *
+ * Called when the C extension is loaded to register all types.
+ */
+void Array_ModuleInit() {
+  zend_class_entry tmp_ce;
+  zend_object_handlers *h;
+
+  // RepeatedField.
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedField",
+                   repeated_field_methods);
+
+  RepeatedField_class_entry = zend_register_internal_class(&tmp_ce);
+  zend_class_implements(RepeatedField_class_entry, 3, spl_ce_ArrayAccess,
+                        zend_ce_aggregate, spl_ce_Countable);
+  RepeatedField_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  RepeatedField_class_entry->create_object = RepeatedField_create;
+
+  h = &RepeatedField_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = RepeatedField_destructor;
+  h->get_properties = RepeatedField_GetProperties;
+  h->get_property_ptr_ptr = RepeatedField_GetPropertyPtrPtr;
+
+  // RepeatedFieldIter
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedFieldIter",
+                   repeated_field_iter_methods);
+
+  RepeatedFieldIter_class_entry = zend_register_internal_class(&tmp_ce);
+  zend_class_implements(RepeatedFieldIter_class_entry, 1, zend_ce_iterator);
+  RepeatedFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  RepeatedFieldIter_class_entry->create_object = RepeatedFieldIter_create;
+
+  h = &repeated_field_iter_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = RepeatedFieldIter_dtor;
+}
diff --git a/php/ext/google/protobuf2/array.h b/php/ext/google/protobuf2/array.h
new file mode 100644
index 0000000..5cf517c
--- /dev/null
+++ b/php/ext/google/protobuf2/array.h
@@ -0,0 +1,61 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_ARRAY_H_
+#define PHP_PROTOBUF_ARRAY_H_
+
+#include <php.h>
+
+#include "php-upb.h"
+
+// Registers PHP classes for RepeatedField.
+void Array_ModuleInit();
+
+// Gets a upb_array* for the PHP object |val|:
+//  * If |val| is a RepeatedField object, we first check its type and verify
+//    that that the elements have the correct type for |f|. If so, we return the
+//    wrapped upb_array*. We also make sure that this array's arena is fused to
+//    |arena|, so the returned upb_array is guaranteed to live as long as
+//    |arena|.
+//  * If |val| is a PHP Array, we attempt to create a new upb_array using
+//    |arena| and add all of the PHP elements to it.
+//
+// If an error occurs, we raise a PHP error and return NULL.
+upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, upb_arena *arena);
+
+// Creates a PHP RepeatedField object for the given upb_array* and |f| and
+// returns it in |val|. The PHP object will keep a reference to this |arena| to
+// ensure the underlying array data stays alive.
+//
+// If |arr| is NULL, this will return a PHP null object.
+void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr,
+                                 const upb_fielddef *f, zval *arena);
+
+#endif  // PHP_PROTOBUF_ARRAY_H_
diff --git a/php/ext/google/protobuf2/bundled_php.h b/php/ext/google/protobuf2/bundled_php.h
new file mode 100644
index 0000000..7614767
--- /dev/null
+++ b/php/ext/google/protobuf2/bundled_php.h
@@ -0,0 +1,46 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_BUNDLED_PHP_H_
+#define PHP_PROTOBUF_BUNDLED_PHP_H_
+
+// We embed PHP source code into the binary for things we don't want to
+// implement in C. This struct serves as a table of contents for all of
+// the embedded files.
+typedef struct {
+  const char *filename;
+  const char *contents;
+} BundledPhp_File;
+
+// An array of all the embedded file structs. This array is terminated with a
+// {NULL, NULL} entry.
+extern BundledPhp_File *bundled_files;
+
+#endif  // PHP_PROTOBUF_BUNDLED_PHP_H_
diff --git a/php/ext/google/protobuf2/config.m4 b/php/ext/google/protobuf2/config.m4
new file mode 100644
index 0000000..3fdcb00
--- /dev/null
+++ b/php/ext/google/protobuf2/config.m4
@@ -0,0 +1,10 @@
+PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [  --enable-protobuf   Enable Protobuf extension])
+
+if test "$PHP_PROTOBUF" != "no"; then
+
+  PHP_NEW_EXTENSION(
+    protobuf,
+    arena.c array.c bundled_php.c convert.c def.c map.c message.c names.c php-upb.c protobuf.c,
+    $ext_shared)
+
+fi
diff --git a/php/ext/google/protobuf2/convert.c b/php/ext/google/protobuf2/convert.c
new file mode 100644
index 0000000..dd076c5
--- /dev/null
+++ b/php/ext/google/protobuf2/convert.c
@@ -0,0 +1,478 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "convert.h"
+
+#include <php.h>
+
+// This is not self-contained: it must be after other Zend includes.
+#include <Zend/zend_exceptions.h>
+
+#include "array.h"
+#include "map.h"
+#include "message.h"
+#include "php-upb.h"
+#include "protobuf.h"
+
+// -----------------------------------------------------------------------------
+// GPBUtil
+// -----------------------------------------------------------------------------
+
+static zend_class_entry* GPBUtil_class_entry;
+
+// The implementation of type checking for primitive fields is empty. This is
+// because type checking is done when direct assigning message fields (e.g.,
+// foo->a = 1). Functions defined here are place holders in generated code for
+// pure PHP implementation (c extension and pure PHP share the same generated
+// code).
+
+PHP_METHOD(Util, checkInt32) {}
+PHP_METHOD(Util, checkUint32) {}
+PHP_METHOD(Util, checkInt64) {}
+PHP_METHOD(Util, checkUint64) {}
+PHP_METHOD(Util, checkEnum) {}
+PHP_METHOD(Util, checkFloat) {}
+PHP_METHOD(Util, checkDouble) {}
+PHP_METHOD(Util, checkBool) {}
+PHP_METHOD(Util, checkString) {}
+PHP_METHOD(Util, checkBytes) {}
+PHP_METHOD(Util, checkMessage) {}
+
+// The result of checkMapField() is assigned, so we need to return the first
+// param:
+//   $arr = GPBUtil::checkMapField($var,
+//                                 \Google\Protobuf\Internal\GPBType::INT64,
+//                                 \Google\Protobuf\Internal\GPBType::INT32);
+PHP_METHOD(Util, checkMapField) {
+  zval *val, *key_type, *val_type, *klass;
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z", &val, &key_type,
+                            &val_type, &klass) == FAILURE) {
+    return;
+  }
+  RETURN_ZVAL(val, 1, 0);
+}
+
+// The result of checkRepeatedField() is assigned, so we need to return the
+// first param:
+// $arr = GPBUtil::checkRepeatedField(
+//     $var, \Google\Protobuf\Internal\GPBType::STRING);
+PHP_METHOD(Util, checkRepeatedField) {
+  zval *val, *type, *klass;
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &val, &type, &klass) ==
+      FAILURE) {
+    return;
+  }
+  RETURN_ZVAL(val, 1, 0);
+}
+
+static zend_function_entry util_methods[] = {
+  PHP_ME(Util, checkInt32,  NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkUint32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkInt64,  NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkUint64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkEnum,   NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkFloat,  NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkDouble, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkBool,   NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkBytes,  NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkMapField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(Util, checkRepeatedField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// Conversion functions used from C
+// -----------------------------------------------------------------------------
+
+upb_fieldtype_t pbphp_dtype_to_type(upb_descriptortype_t type) {
+  switch (type) {
+#define CASE(descriptor_type, type)           \
+  case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
+    return UPB_TYPE_##type;
+
+  CASE(FLOAT,    FLOAT);
+  CASE(DOUBLE,   DOUBLE);
+  CASE(BOOL,     BOOL);
+  CASE(STRING,   STRING);
+  CASE(BYTES,    BYTES);
+  CASE(MESSAGE,  MESSAGE);
+  CASE(GROUP,    MESSAGE);
+  CASE(ENUM,     ENUM);
+  CASE(INT32,    INT32);
+  CASE(INT64,    INT64);
+  CASE(UINT32,   UINT32);
+  CASE(UINT64,   UINT64);
+  CASE(SINT32,   INT32);
+  CASE(SINT64,   INT64);
+  CASE(FIXED32,  UINT32);
+  CASE(FIXED64,  UINT64);
+  CASE(SFIXED32, INT32);
+  CASE(SFIXED64, INT64);
+
+#undef CASE
+
+  }
+
+  zend_error(E_ERROR, "Unknown field type.");
+  return 0;
+}
+
+static bool buftouint64(const char *ptr, const char *end, uint64_t *val) {
+  uint64_t u64 = 0;
+  while (ptr < end) {
+    unsigned ch = (unsigned)(*ptr - '0');
+    if (ch >= 10) break;
+    if (u64 > UINT64_MAX / 10 || u64 * 10 > UINT64_MAX - ch) {
+      return false;
+    }
+    u64 *= 10;
+    u64 += ch;
+    ptr++;
+  }
+
+  if (ptr != end) {
+    // In PHP tradition, we allow truncation: "1.1" -> 1.
+    // But we don't allow 'e', eg. '1.1e2' or any other non-numeric chars.
+    if (*ptr++ != '.') return false;
+
+    for (;ptr < end; ptr++) {
+      if (*ptr < '0' || *ptr > '9') {
+        return false;
+      }
+    }
+  }
+
+  *val = u64;
+  return true;
+}
+
+static bool buftoint64(const char *ptr, const char *end, int64_t *val) {
+  bool neg = false;
+  uint64_t u64;
+
+  if (ptr != end && *ptr == '-') {
+    ptr++;
+    neg = true;
+  }
+
+  if (!buftouint64(ptr, end, &u64) ||
+      u64 > (uint64_t)INT64_MAX + neg) {
+    return false;
+  }
+
+  *val = neg ? -u64 : u64;
+  return true;
+}
+
+static void throw_conversion_exception(const char *to, const zval *zv) {
+  zval tmp;
+  ZVAL_COPY(&tmp, zv);
+  convert_to_string(&tmp);
+
+  zend_throw_exception_ex(NULL, 0, "Cannot convert '%s' to %s",
+                          Z_STRVAL_P(&tmp), to);
+
+  zval_ptr_dtor(&tmp);
+}
+
+bool Convert_PhpToInt64(const zval *php_val, int64_t *i64) {
+  switch (Z_TYPE_P(php_val)) {
+    case IS_LONG:
+      *i64 = Z_LVAL_P(php_val);
+      return true;
+    case IS_DOUBLE: {
+      double dbl = Z_DVAL_P(php_val);
+      if (dbl > 9223372036854774784.0 || dbl < -9223372036854775808.0) {
+        zend_throw_exception_ex(NULL, 0, "Out of range");
+        return false;
+      }
+      *i64 = dbl; /* must be guarded, overflow here is UB */
+      return true;
+    }
+    case IS_STRING: {
+      const char *buf = Z_STRVAL_P(php_val);
+      // PHP would accept scientific notation here, but we're going to be a
+      // little more discerning and only accept pure integers.
+      bool ok = buftoint64(buf, buf + Z_STRLEN_P(php_val), i64);
+      if (!ok) {
+        throw_conversion_exception("integer", php_val);
+      }
+      return ok;
+    }
+    default:
+      throw_conversion_exception("integer", php_val);
+      return false;
+  }
+}
+
+static bool to_double(zval *php_val, double *dbl) {
+  switch (Z_TYPE_P(php_val)) {
+    case IS_LONG:
+      *dbl = Z_LVAL_P(php_val);
+      return true;
+    case IS_DOUBLE:
+      *dbl = Z_DVAL_P(php_val);
+      return true;
+    case IS_STRING: {
+      zend_long lval;
+      switch (is_numeric_string(Z_STRVAL_P(php_val), Z_STRLEN_P(php_val), &lval,
+                                dbl, false)) {
+        case IS_LONG:
+          *dbl = lval;
+          return true;
+        case IS_DOUBLE:
+          return true;
+        default:
+          goto fail;
+      }
+    }
+    default:
+     fail:
+      throw_conversion_exception("double", php_val);
+      return false;
+  }
+}
+
+static bool to_bool(zval* from, bool* to) {
+  switch (Z_TYPE_P(from)) {
+    case IS_TRUE:
+      *to = true;
+      return true;
+    case IS_FALSE:
+      *to = false;
+      return true;
+    case IS_LONG:
+      *to = (Z_LVAL_P(from) != 0);
+      return true;
+    case IS_DOUBLE:
+      *to = (Z_LVAL_P(from) != 0);
+      return true;
+    case IS_STRING:
+      if (Z_STRLEN_P(from) == 0 ||
+          (Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) {
+        *to = false;
+      } else {
+        *to = true;
+      }
+      return true;
+    default:
+      throw_conversion_exception("bool", from);
+      return false;
+  }
+}
+
+static bool to_string(zval* from) {
+  if (Z_ISREF_P(from)) {
+    ZVAL_DEREF(from);
+  }
+
+  switch (Z_TYPE_P(from)) {
+    case IS_STRING:
+      return true;
+    case IS_TRUE:
+    case IS_FALSE:
+    case IS_LONG:
+    case IS_DOUBLE: {
+      zval tmp;
+      zend_make_printable_zval(from, &tmp);
+      ZVAL_COPY_VALUE(from, &tmp);
+      return true;
+    }
+    default:
+      throw_conversion_exception("string", from);
+      return false;
+  }
+}
+
+bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type,
+                      const Descriptor *desc, upb_arena *arena) {
+  int64_t i64;
+
+  if (Z_ISREF_P(php_val)) {
+    ZVAL_DEREF(php_val);
+  }
+
+  switch (type) {
+    case UPB_TYPE_INT64:
+      return Convert_PhpToInt64(php_val, &upb_val->int64_val);
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_ENUM:
+      if (!Convert_PhpToInt64(php_val, &i64)) {
+        return false;
+      }
+      upb_val->int32_val = i64;
+      return true;
+    case UPB_TYPE_UINT64:
+      if (!Convert_PhpToInt64(php_val, &i64)) {
+        return false;
+      }
+      upb_val->uint64_val = i64;
+      return true;
+    case UPB_TYPE_UINT32:
+      if (!Convert_PhpToInt64(php_val, &i64)) {
+        return false;
+      }
+      upb_val->uint32_val = i64;
+      return true;
+    case UPB_TYPE_DOUBLE:
+      return to_double(php_val, &upb_val->double_val);
+    case UPB_TYPE_FLOAT:
+      if (!to_double(php_val, &upb_val->double_val)) return false;
+      upb_val->float_val = upb_val->double_val;
+      return true;
+    case UPB_TYPE_BOOL:
+      return to_bool(php_val, &upb_val->bool_val);
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      char *ptr;
+      size_t size;
+
+      if (!to_string(php_val)) return false;
+
+      size = Z_STRLEN_P(php_val);
+
+      // If arena is NULL we reference the input zval.
+      // The resulting upb_strview will only be value while the zval is alive.
+      if (arena) {
+        ptr = upb_arena_malloc(arena, size);
+        memcpy(ptr, Z_STRVAL_P(php_val), size);
+      } else {
+        ptr = Z_STRVAL_P(php_val);
+      }
+
+      upb_val->str_val = upb_strview_make(ptr, size);
+      return true;
+    }
+    case UPB_TYPE_MESSAGE:
+      PBPHP_ASSERT(desc);
+      return Message_GetUpbMessage(php_val, desc, arena,
+                                   (upb_msg **)&upb_val->msg_val);
+  }
+
+  return false;
+}
+
+void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type,
+                      const Descriptor *desc, zval *arena) {
+  switch (type) {
+    case UPB_TYPE_INT64:
+#if SIZEOF_ZEND_LONG == 8
+      ZVAL_LONG(php_val, upb_val.int64_val);
+#else
+      {
+        char buf[20];
+        int size = sprintf(buf, "%lld", upb_val.int64_val);
+        ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0));
+      }
+#endif
+      break;
+    case UPB_TYPE_UINT64:
+#if SIZEOF_ZEND_LONG == 8
+      ZVAL_LONG(php_val, upb_val.uint64_val);
+#else
+      {
+        char buf[20];
+        int size = sprintf(buf, "%lld", (int64_t)upb_val.uint64_val);
+        ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0));
+      }
+#endif
+      break;
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_ENUM:
+      ZVAL_LONG(php_val, upb_val.int32_val);
+      break;
+    case UPB_TYPE_UINT32: {
+      // Sign-extend for consistency between 32/64-bit builds.
+      zend_long val = (int32_t)upb_val.uint32_val;
+      ZVAL_LONG(php_val, val);
+      break;
+    }
+    case UPB_TYPE_DOUBLE:
+      ZVAL_DOUBLE(php_val, upb_val.double_val);
+      break;
+    case UPB_TYPE_FLOAT:
+      ZVAL_DOUBLE(php_val, upb_val.float_val);
+      break;
+    case UPB_TYPE_BOOL:
+      ZVAL_BOOL(php_val, upb_val.bool_val);
+      break;
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES: {
+      upb_strview str = upb_val.str_val;
+      ZVAL_NEW_STR(php_val, zend_string_init(str.data, str.size, 0));
+      break;
+    }
+    case UPB_TYPE_MESSAGE:
+      PBPHP_ASSERT(desc);
+      Message_GetPhpWrapper(php_val, desc, (upb_msg*)upb_val.msg_val, arena);
+      break;
+  }
+}
+
+bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val,
+                              upb_fieldtype_t type, const Descriptor *desc,
+                              upb_arena *arena) {
+  const upb_msgdef *subm = desc ? desc->msgdef : NULL;
+  if (subm && upb_msgdef_iswrapper(subm) && Z_TYPE_P(val) != IS_OBJECT) {
+    // Assigning a scalar to a wrapper-typed value. We will automatically wrap
+    // the value, so the user doesn't need to create a FooWrapper(['value': X])
+    // message manually.
+    upb_msg *wrapper = upb_msg_new(subm, arena);
+    const upb_fielddef *val_f = upb_msgdef_itof(subm, 1);
+    upb_fieldtype_t type_f = upb_fielddef_type(val_f);
+    upb_msgval msgval;
+    if (!Convert_PhpToUpb(val, &msgval, type_f, NULL, arena)) return false;
+    upb_msg_set(wrapper, val_f, msgval, arena);
+    upb_val->msg_val = wrapper;
+    return true;
+  } else {
+    // Convert_PhpToUpb doesn't auto-construct messages. This means that we only
+    // allow:
+    //   ['foo_submsg': new Foo(['a' => 1])]
+    // not:
+    //   ['foo_submsg': ['a' => 1]]
+    return Convert_PhpToUpb(val, upb_val, type, desc, arena);
+  }
+}
+
+void Convert_ModuleInit(void) {
+  const char *prefix_name = "TYPE_URL_PREFIX";
+  zend_class_entry class_type;
+
+  INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil",
+                   util_methods);
+  GPBUtil_class_entry = zend_register_internal_class(&class_type);
+
+  zend_declare_class_constant_string(GPBUtil_class_entry, prefix_name,
+                                     strlen(prefix_name),
+                                     "type.googleapis.com/");
+}
diff --git a/php/ext/google/protobuf2/convert.h b/php/ext/google/protobuf2/convert.h
new file mode 100644
index 0000000..06c05c7
--- /dev/null
+++ b/php/ext/google/protobuf2/convert.h
@@ -0,0 +1,73 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_CONVERT_H_
+#define PHP_PROTOBUF_CONVERT_H_
+
+#include <php.h>
+
+#include "php-upb.h"
+#include "def.h"
+
+upb_fieldtype_t pbphp_dtype_to_type(upb_descriptortype_t type);
+
+// Converts |php_val| to an int64_t. Returns false if the value cannot be
+// converted.
+bool Convert_PhpToInt64(const zval *php_val, int64_t *i64);
+
+// Converts |php_val| to a upb_msgval according to |type|. If type is
+// UPB_TYPE_MESSAGE, then |desc| must be the Descriptor for this message type.
+// If type is string, message, or bytes, then |arena| will be used to copy
+// string data or fuse this arena to the given message's arena.
+bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type,
+                      const Descriptor *desc, upb_arena *arena);
+
+// Similar to Convert_PhpToUpb, but supports automatically wrapping the wrapper
+// types if a primitive is specified:
+//
+//   5 -> Int64Wrapper(value=5)
+//
+// We currently allow this implicit conversion in initializers, but not for
+// assignment.
+bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val,
+                              upb_fieldtype_t type, const Descriptor *desc,
+                              upb_arena *arena);
+
+// Converts |upb_val| to a PHP zval according to |type|. This may involve
+// creating a PHP wrapper object. If type == UPB_TYPE_MESSAGE, then |desc| must
+// be the Descriptor for this message type. Any newly created wrapper object
+// will reference |arena|.
+void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type,
+                      const Descriptor *desc, zval *arena);
+
+// Registers the GPBUtil class.
+void Convert_ModuleInit(void);
+
+#endif  // PHP_PROTOBUF_CONVERT_H_
diff --git a/php/ext/google/protobuf2/def.c b/php/ext/google/protobuf2/def.c
new file mode 100644
index 0000000..c76134d
--- /dev/null
+++ b/php/ext/google/protobuf2/def.c
@@ -0,0 +1,1085 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "def.h"
+
+#include <php.h>
+
+// This is not self-contained: it must be after other Zend includes.
+#include <Zend/zend_exceptions.h>
+
+#include "names.h"
+#include "php-upb.h"
+#include "protobuf.h"
+
+static void CheckUpbStatus(const upb_status* status, const char* msg) {
+  if (!upb_ok(status)) {
+    zend_error(E_ERROR, "%s: %s\n", msg, upb_status_errmsg(status));
+  }
+}
+
+static void FieldDescriptor_FromFieldDef(zval *val, const upb_fielddef *f);
+
+// We use this for objects that should not be created directly from PHP.
+static zend_object *CreateHandler_ReturnNull(zend_class_entry *class_type) {
+  return NULL;  // Nobody should call this.
+}
+
+
+// -----------------------------------------------------------------------------
+// EnumValueDescriptor
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  const char *name;
+  int32_t number;
+} EnumValueDescriptor;
+
+zend_class_entry *EnumValueDescriptor_class_entry;
+static zend_object_handlers EnumValueDescriptor_object_handlers;
+
+/*
+ * EnumValueDescriptor_Make()
+ *
+ * Function to create an EnumValueDescriptor object from C.
+ */
+static void EnumValueDescriptor_Make(zval *val, const char *name,
+                                     int32_t number) {
+  EnumValueDescriptor *intern = emalloc(sizeof(EnumValueDescriptor));
+  zend_object_std_init(&intern->std, EnumValueDescriptor_class_entry);
+  intern->std.handlers = &EnumValueDescriptor_object_handlers;
+  intern->name = name;
+  intern->number = number;
+  // Skip object_properties_init(), we don't allow derived classes.
+  ZVAL_OBJ(val, &intern->std);
+}
+
+/*
+ * EnumValueDescriptor::getName()
+ *
+ * Returns the name for this enum value.
+ */
+PHP_METHOD(EnumValueDescriptor, getName) {
+  EnumValueDescriptor *intern = (EnumValueDescriptor*)Z_OBJ_P(getThis());
+  RETURN_STRING(intern->name);
+}
+
+/*
+ * EnumValueDescriptor::getNumber()
+ *
+ * Returns the number for this enum value.
+ */
+PHP_METHOD(EnumValueDescriptor, getNumber) {
+  EnumValueDescriptor *intern = (EnumValueDescriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(intern->number);
+}
+
+static zend_function_entry EnumValueDescriptor_methods[] = {
+  PHP_ME(EnumValueDescriptor, getName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(EnumValueDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// EnumDescriptor
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  const upb_enumdef *enumdef;
+} EnumDescriptor;
+
+zend_class_entry *EnumDescriptor_class_entry;
+static zend_object_handlers EnumDescriptor_object_handlers;
+
+void EnumDescriptor_FromClassEntry(zval *val, zend_class_entry *ce) {
+  // To differentiate enums from classes, we pointer-tag the class entry.
+  void* key = (void*)((uintptr_t)ce | 1);
+  PBPHP_ASSERT(key != ce);
+
+  if (ce == NULL) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(key, val)) {
+    const upb_enumdef *e = NameMap_GetEnum(ce);
+    if (!e) {
+      ZVAL_NULL(val);
+      return;
+    }
+    EnumDescriptor* ret = emalloc(sizeof(EnumDescriptor));
+    zend_object_std_init(&ret->std, EnumDescriptor_class_entry);
+    ret->std.handlers = &EnumDescriptor_object_handlers;
+    ret->enumdef = e;
+    ObjCache_Add(key, &ret->std);
+
+    // Prevent this from ever being collected (within a request).
+    GC_ADDREF(&ret->std);
+
+    ZVAL_OBJ(val, &ret->std);
+  }
+}
+
+void EnumDescriptor_FromEnumDef(zval *val, const upb_enumdef *m) {
+  if (!m) {
+    ZVAL_NULL(val);
+  } else {
+    char *classname =
+        GetPhpClassname(upb_enumdef_file(m), upb_enumdef_fullname(m));
+    zend_string *str = zend_string_init(classname, strlen(classname), 0);
+    zend_class_entry *ce = zend_lookup_class(str);  // May autoload the class.
+
+    zend_string_release (str);
+
+    if (!ce) {
+      zend_error(E_ERROR, "Couldn't load generated class %s", classname);
+    }
+
+    free(classname);
+    EnumDescriptor_FromClassEntry(val, ce);
+  }
+}
+
+/*
+ * EnumDescriptor::getValue()
+ *
+ * Returns an EnumValueDescriptor for this index. Note: we are not looking
+ * up by numeric enum value, but by the index in the list of enum values.
+ */
+PHP_METHOD(EnumDescriptor, getValue) {
+  EnumDescriptor *intern = (EnumDescriptor*)Z_OBJ_P(getThis());
+  zend_long index;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  int field_num = upb_enumdef_numvals(intern->enumdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_enum_iter iter;
+  int i;
+  for(upb_enum_begin(&iter, intern->enumdef), i = 0;
+      !upb_enum_done(&iter) && i < index;
+      upb_enum_next(&iter), i++);
+
+  EnumValueDescriptor_Make(&ret, upb_enum_iter_name(&iter),
+                           upb_enum_iter_number(&iter));
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/*
+ * EnumDescriptor::getValueCount()
+ *
+ * Returns the number of values in this enum.
+ */
+PHP_METHOD(EnumDescriptor, getValueCount) {
+  EnumDescriptor *intern = (EnumDescriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_enumdef_numvals(intern->enumdef));
+}
+
+/*
+ * EnumDescriptor::getPublicDescriptor()
+ *
+ * Returns this EnumDescriptor. Unlike the pure-PHP descriptor, we do not
+ * have two separate EnumDescriptor classes. We use a single class for both
+ * the public and private descriptor.
+ */
+PHP_METHOD(EnumDescriptor, getPublicDescriptor) {
+  RETURN_ZVAL(getThis(), 1, 0);
+}
+
+static zend_function_entry EnumDescriptor_methods[] = {
+  PHP_ME(EnumDescriptor, getPublicDescriptor, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(EnumDescriptor, getValueCount, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(EnumDescriptor, getValue, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// Oneof
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  const upb_oneofdef *oneofdef;
+} OneofDescriptor;
+
+zend_class_entry *OneofDescriptor_class_entry;
+static zend_object_handlers OneofDescriptor_object_handlers;
+
+static void OneofDescriptor_FromOneofDef(zval *val, const upb_oneofdef *o) {
+  if (o == NULL) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(o, val)) {
+    OneofDescriptor* ret = emalloc(sizeof(OneofDescriptor));
+    zend_object_std_init(&ret->std, OneofDescriptor_class_entry);
+    ret->std.handlers = &OneofDescriptor_object_handlers;
+    ret->oneofdef = o;
+    ObjCache_Add(o, &ret->std);
+
+    // Prevent this from ever being collected (within a request).
+    GC_ADDREF(&ret->std);
+
+    ZVAL_OBJ(val, &ret->std);
+  }
+}
+
+/*
+ * OneofDescriptor::getName()
+ *
+ * Returns the name of this oneof.
+ */
+PHP_METHOD(OneofDescriptor, getName) {
+  OneofDescriptor *intern = (OneofDescriptor*)Z_OBJ_P(getThis());
+  RETURN_STRING(upb_oneofdef_name(intern->oneofdef));
+}
+
+/*
+ * OneofDescriptor::getField()
+ *
+ * Returns a field from this oneof. The given index must be in the range
+ *   [0, getFieldCount() - 1].
+ */
+PHP_METHOD(OneofDescriptor, getField) {
+  OneofDescriptor *intern = (OneofDescriptor*)Z_OBJ_P(getThis());
+  zend_long index;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  int field_num = upb_oneofdef_numfields(intern->oneofdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_oneof_iter iter;
+  int i;
+  for(upb_oneof_begin(&iter, intern->oneofdef), i = 0;
+      !upb_oneof_done(&iter) && i < index;
+      upb_oneof_next(&iter), i++);
+  const upb_fielddef *field = upb_oneof_iter_field(&iter);
+
+  FieldDescriptor_FromFieldDef(&ret, field);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/*
+ * OneofDescriptor::getFieldCount()
+ *
+ * Returns the number of fields in this oneof.
+ */
+PHP_METHOD(OneofDescriptor, getFieldCount) {
+  OneofDescriptor *intern = (OneofDescriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_oneofdef_numfields(intern->oneofdef));
+}
+
+static zend_function_entry OneofDescriptor_methods[] = {
+  PHP_ME(OneofDescriptor, getName,  NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(OneofDescriptor, getField, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(OneofDescriptor, getFieldCount, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// FieldDescriptor
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  const upb_fielddef *fielddef;
+} FieldDescriptor;
+
+zend_class_entry *FieldDescriptor_class_entry;
+static zend_object_handlers FieldDescriptor_object_handlers;
+
+static void FieldDescriptor_FromFieldDef(zval *val, const upb_fielddef *f) {
+  if (f == NULL) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(f, val)) {
+    FieldDescriptor* ret = emalloc(sizeof(FieldDescriptor));
+    zend_object_std_init(&ret->std, FieldDescriptor_class_entry);
+    ret->std.handlers = &FieldDescriptor_object_handlers;
+    ret->fielddef = f;
+    ObjCache_Add(f, &ret->std);
+
+    // Prevent this from ever being collected (within a request).
+    GC_ADDREF(&ret->std);
+
+    ZVAL_OBJ(val, &ret->std);
+  }
+}
+
+upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
+  switch (type) {
+#define CASE(descriptor_type, type)           \
+  case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
+    return UPB_TYPE_##type;
+
+  CASE(FLOAT,    FLOAT);
+  CASE(DOUBLE,   DOUBLE);
+  CASE(BOOL,     BOOL);
+  CASE(STRING,   STRING);
+  CASE(BYTES,    BYTES);
+  CASE(MESSAGE,  MESSAGE);
+  CASE(GROUP,    MESSAGE);
+  CASE(ENUM,     ENUM);
+  CASE(INT32,    INT32);
+  CASE(INT64,    INT64);
+  CASE(UINT32,   UINT32);
+  CASE(UINT64,   UINT64);
+  CASE(SINT32,   INT32);
+  CASE(SINT64,   INT64);
+  CASE(FIXED32,  UINT32);
+  CASE(FIXED64,  UINT64);
+  CASE(SFIXED32, INT32);
+  CASE(SFIXED64, INT64);
+
+#undef CONVERT
+
+  }
+
+  zend_error(E_ERROR, "Unknown field type.");
+  return 0;
+}
+
+/*
+ * FieldDescriptor::getName()
+ *
+ * Returns the name of this field.
+ */
+PHP_METHOD(FieldDescriptor, getName) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  RETURN_STRING(upb_fielddef_name(intern->fielddef));
+}
+
+/*
+ * FieldDescriptor::getNumber()
+ *
+ * Returns the number of this field.
+ */
+PHP_METHOD(FieldDescriptor, getNumber) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_fielddef_number(intern->fielddef));
+}
+
+/*
+ * FieldDescriptor::getLabel()
+ *
+ * Returns the label of this field as an integer.
+ */
+PHP_METHOD(FieldDescriptor, getLabel) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_fielddef_label(intern->fielddef));
+}
+
+/*
+ * FieldDescriptor::getType()
+ *
+ * Returns the type of this field as an integer.
+ */
+PHP_METHOD(FieldDescriptor, getType) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_fielddef_descriptortype(intern->fielddef));
+}
+
+/*
+ * FieldDescriptor::isMap()
+ *
+ * Returns true if this field is a map.
+ */
+PHP_METHOD(FieldDescriptor, isMap) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  RETURN_BOOL(upb_fielddef_ismap(intern->fielddef));
+}
+
+/*
+ * FieldDescriptor::getEnumType()
+ *
+ * Returns the EnumDescriptor for this field, which must be an enum.
+ */
+PHP_METHOD(FieldDescriptor, getEnumType) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  const upb_enumdef *e = upb_fielddef_enumsubdef(intern->fielddef);
+  zval ret;
+
+  if (!e) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Cannot get enum type for non-enum field '%s'",
+                            upb_fielddef_name(intern->fielddef));
+    return;
+  }
+
+  EnumDescriptor_FromEnumDef(&ret, e);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/*
+ * FieldDescriptor::getMessageType()
+ *
+ * Returns the Descriptor for this field, which must be a message.
+ */
+PHP_METHOD(FieldDescriptor, getMessageType) {
+  FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+  Descriptor* desc = Descriptor_GetFromFieldDef(intern->fielddef);
+  zval ret;
+
+  if (!desc) {
+    zend_throw_exception_ex(
+        NULL, 0, "Cannot get message type for non-message field '%s'",
+        upb_fielddef_name(intern->fielddef));
+    return;
+  }
+
+  ZVAL_OBJ(&ret, &desc->std);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+static zend_function_entry FieldDescriptor_methods[] = {
+  PHP_ME(FieldDescriptor, getName,   NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getNumber, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getLabel,  NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getType,   NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, isMap,     NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getEnumType, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(FieldDescriptor, getMessageType, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// Descriptor
+// -----------------------------------------------------------------------------
+
+zend_class_entry *Descriptor_class_entry;
+static zend_object_handlers Descriptor_object_handlers;
+
+static void Descriptor_destructor(zend_object* obj) {
+  // We don't really need to do anything here, we don't allow this to be
+  // collected before the end of the request.
+}
+
+// C Functions from def.h //////////////////////////////////////////////////////
+
+// These are documented in the header file.
+
+void Descriptor_FromClassEntry(zval *val, zend_class_entry *ce) {
+  if (ce == NULL) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(ce, val)) {
+    const upb_msgdef *msgdef = NameMap_GetMessage(ce);
+    if (!msgdef) {
+      ZVAL_NULL(val);
+      return;
+    }
+    Descriptor* ret = emalloc(sizeof(Descriptor));
+    zend_object_std_init(&ret->std, Descriptor_class_entry);
+    ret->std.handlers = &Descriptor_object_handlers;
+    ret->class_entry = ce;
+    ret->msgdef = msgdef;
+    ObjCache_Add(ce, &ret->std);
+
+    // Prevent this from ever being collected (within a request).
+    GC_ADDREF(&ret->std);
+
+    ZVAL_OBJ(val, &ret->std);
+  }
+}
+
+Descriptor* Descriptor_GetFromClassEntry(zend_class_entry *ce) {
+  zval desc;
+  Descriptor_FromClassEntry(&desc, ce);
+  if (Z_TYPE_P(&desc) == IS_NULL) {
+    return NULL;
+  } else {
+    return (Descriptor*)Z_OBJ_P(&desc);
+  }
+}
+
+Descriptor* Descriptor_GetFromMessageDef(const upb_msgdef *m) {
+  if (m) {
+    if (upb_msgdef_mapentry(m)) {
+      // A bit of a hack, since map entries don't have classes.
+      Descriptor* ret = emalloc(sizeof(Descriptor));
+      zend_object_std_init(&ret->std, Descriptor_class_entry);
+      ret->std.handlers = &Descriptor_object_handlers;
+      ret->class_entry = NULL;
+      ret->msgdef = m;
+
+      // Prevent this from ever being collected (within a request).
+      GC_ADDREF(&ret->std);
+
+      return ret;
+    }
+
+    char *classname =
+        GetPhpClassname(upb_msgdef_file(m), upb_msgdef_fullname(m));
+    zend_string *str = zend_string_init(classname, strlen(classname), 0);
+    zend_class_entry *ce = zend_lookup_class(str);  // May autoload the class.
+
+    zend_string_release (str);
+
+    if (!ce) {
+      zend_error(E_ERROR, "Couldn't load generated class %s", classname);
+    }
+
+    free(classname);
+    return Descriptor_GetFromClassEntry(ce);
+  } else {
+    return NULL;
+  }
+}
+
+Descriptor* Descriptor_GetFromFieldDef(const upb_fielddef *f) {
+  return Descriptor_GetFromMessageDef(upb_fielddef_msgsubdef(f));
+}
+
+/*
+ * Descriptor::getPublicDescriptor()
+ *
+ * Returns this EnumDescriptor. Unlike the pure-PHP descriptor, we do not
+ * have two separate EnumDescriptor classes. We use a single class for both
+ * the public and private descriptor.
+ */
+PHP_METHOD(Descriptor, getPublicDescriptor) {
+  RETURN_ZVAL(getThis(), 1, 0);
+}
+
+/*
+ * Descriptor::getFullName()
+ *
+ * Returns the full name for this message type.
+ */
+PHP_METHOD(Descriptor, getFullName) {
+  Descriptor *intern = (Descriptor*)Z_OBJ_P(getThis());
+  RETURN_STRING(upb_msgdef_fullname(intern->msgdef));
+}
+
+/*
+ * Descriptor::getField()
+ *
+ * Returns a FieldDescriptor for the given index, which must be in the range
+ * [0, getFieldCount()-1].
+ */
+PHP_METHOD(Descriptor, getField) {
+  Descriptor *intern = (Descriptor*)Z_OBJ_P(getThis());
+  int count = upb_msgdef_numfields(intern->msgdef);
+  zval ret;
+  zend_long index;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  if (index < 0 || index >= count) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_msg_field_iter iter;
+  int i;
+  for(upb_msg_field_begin(&iter, intern->msgdef), i = 0;
+      !upb_msg_field_done(&iter) && i < index;
+      upb_msg_field_next(&iter), i++);
+  const upb_fielddef *field = upb_msg_iter_field(&iter);
+
+  FieldDescriptor_FromFieldDef(&ret, field);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/*
+ * Descriptor::getFieldCount()
+ *
+ * Returns the number of fields in this message.
+ */
+PHP_METHOD(Descriptor, getFieldCount) {
+  Descriptor *intern = (Descriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_msgdef_numfields(intern->msgdef));
+}
+
+/*
+ * Descriptor::getOneofDecl()
+ *
+ * Returns a OneofDescriptor for the given index, which must be in the range
+ * [0, getOneofDeclCount()].
+ */
+PHP_METHOD(Descriptor, getOneofDecl) {
+  Descriptor *intern = (Descriptor*)Z_OBJ_P(getThis());
+  zend_long index;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
+      FAILURE) {
+    zend_error(E_USER_ERROR, "Expect integer for index.\n");
+    return;
+  }
+
+  int field_num = upb_msgdef_numoneofs(intern->msgdef);
+  if (index < 0 || index >= field_num) {
+    zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index);
+    return;
+  }
+
+  upb_msg_oneof_iter iter;
+  int i;
+  for(upb_msg_oneof_begin(&iter, intern->msgdef), i = 0;
+      !upb_msg_oneof_done(&iter) && i < index;
+      upb_msg_oneof_next(&iter), i++);
+  const upb_oneofdef *oneof = upb_msg_iter_oneof(&iter);
+
+  OneofDescriptor_FromOneofDef(&ret, oneof);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/*
+ * Descriptor::getOneofDeclCount()
+ *
+ * Returns the number of oneofs in this message.
+ */
+PHP_METHOD(Descriptor, getOneofDeclCount) {
+  Descriptor *intern = (Descriptor*)Z_OBJ_P(getThis());
+  RETURN_LONG(upb_msgdef_numoneofs(intern->msgdef));
+}
+
+/*
+ * Descriptor::getClass()
+ *
+ * Returns the name of the PHP class for this message.
+ */
+PHP_METHOD(Descriptor, getClass) {
+  Descriptor *intern = (Descriptor*)Z_OBJ_P(getThis());
+  const char* classname = ZSTR_VAL(intern->class_entry->name);
+  RETURN_STRING(classname);
+}
+
+
+static zend_function_entry Descriptor_methods[] = {
+  PHP_ME(Descriptor, getClass, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getFullName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getField, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getFieldCount, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getOneofDecl, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getOneofDeclCount, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Descriptor, getPublicDescriptor, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// DescriptorPool
+// -----------------------------------------------------------------------------
+
+typedef struct DescriptorPool {
+  zend_object std;
+  upb_symtab *symtab;
+} DescriptorPool;
+
+zend_class_entry *DescriptorPool_class_entry;
+static zend_object_handlers DescriptorPool_object_handlers;
+
+static DescriptorPool *GetPool(const zval* this_ptr) {
+  return (DescriptorPool*)Z_OBJ_P(this_ptr);
+}
+
+/**
+ * Object handler to create an DescriptorPool.
+ */
+static zend_object* DescriptorPool_create(zend_class_entry *class_type) {
+  DescriptorPool *intern = emalloc(sizeof(DescriptorPool));
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &DescriptorPool_object_handlers;
+  intern->symtab = upb_symtab_new();
+  // Skip object_properties_init(), we don't allow derived classes.
+  return &intern->std;
+}
+
+/**
+ * Object handler to free an DescriptorPool.
+ */
+static void DescriptorPool_destructor(zend_object* obj) {
+  DescriptorPool* intern = (DescriptorPool*)obj;
+  if (intern->symtab) {
+    upb_symtab_free(intern->symtab);
+  }
+  intern->symtab = NULL;
+  zend_object_std_dtor(&intern->std);
+}
+
+void DescriptorPool_CreateWithSymbolTable(zval *zv, upb_symtab *symtab) {
+  ZVAL_OBJ(zv, DescriptorPool_create(DescriptorPool_class_entry));
+
+  if (symtab) {
+    DescriptorPool *intern = GetPool(zv);
+    upb_symtab_free(intern->symtab);
+    intern->symtab = symtab;
+  }
+}
+
+upb_symtab *DescriptorPool_Steal(zval *zv) {
+  DescriptorPool *intern = GetPool(zv);
+  upb_symtab *ret = intern->symtab;
+  intern->symtab = NULL;
+  return ret;
+}
+
+upb_symtab *DescriptorPool_GetSymbolTable() {
+  DescriptorPool *intern = GetPool(get_generated_pool());
+  return intern->symtab;
+}
+
+/*
+ * DescriptorPool::getGeneratedPool()
+ *
+ * Returns the generated DescriptorPool.
+ */
+PHP_METHOD(DescriptorPool, getGeneratedPool) {
+  zval ret;
+  ZVAL_COPY(&ret, get_generated_pool());
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/*
+ * DescriptorPool::getDescriptorByClassName()
+ *
+ * Returns a Descriptor object for the given PHP class name.
+ */
+PHP_METHOD(DescriptorPool, getDescriptorByClassName) {
+  char *classname = NULL;
+  zend_long classname_len;
+  zend_class_entry *ce;
+  zend_string *str;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
+                            &classname_len) == FAILURE) {
+    return;
+  }
+
+  str = zend_string_init(classname, strlen(classname), 0);
+  ce = zend_lookup_class(str);  // May autoload the class.
+  zend_string_release (str);
+
+  if (!ce) {
+    RETURN_NULL();
+  }
+
+  Descriptor_FromClassEntry(&ret, ce);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/*
+ * DescriptorPool::getEnumDescriptorByClassName()
+ *
+ * Returns a EnumDescriptor object for the given PHP class name.
+ */
+PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) {
+  char *classname = NULL;
+  zend_long classname_len;
+  zend_class_entry *ce;
+  zend_string *str;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &classname,
+                            &classname_len) == FAILURE) {
+    return;
+  }
+
+  str = zend_string_init(classname, strlen(classname), 0);
+  ce = zend_lookup_class(str);  // May autoload the class.
+  zend_string_release (str);
+
+  if (!ce) {
+    RETURN_NULL();
+  }
+
+  EnumDescriptor_FromClassEntry(&ret, ce);
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/*
+ * DescriptorPool::getEnumDescriptorByProtoName()
+ *
+ * Returns a Descriptor object for the given protobuf message name.
+ */
+PHP_METHOD(DescriptorPool, getDescriptorByProtoName) {
+  DescriptorPool *intern = GetPool(getThis());
+  char *protoname = NULL;
+  zend_long protoname_len;
+  const upb_msgdef *m;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protoname,
+                            &protoname_len) == FAILURE) {
+    return;
+  }
+
+  if (*protoname == '.') protoname++;
+
+  m = upb_symtab_lookupmsg(intern->symtab, protoname);
+
+  if (m) {
+    zval ret;
+    ZVAL_OBJ(&ret, &Descriptor_GetFromMessageDef(m)->std);
+    RETURN_ZVAL(&ret, 1, 0);
+  } else {
+    RETURN_NULL();
+  }
+}
+
+/*
+ * depends_on_descriptor()
+ *
+ * Returns true if this FileDescriptorProto depends on descriptor.proto.
+ */
+bool depends_on_descriptor(const google_protobuf_FileDescriptorProto* file) {
+  const upb_strview *deps;
+  upb_strview name = upb_strview_makez("google/protobuf/descriptor.proto");
+  size_t i, n;
+
+  deps = google_protobuf_FileDescriptorProto_dependency(file, &n);
+  for (i = 0; i < n; i++) {
+    if (upb_strview_eql(deps[i], name)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/*
+ * add_name_mappings()
+ *
+ * Adds the messages and enums in this file to the NameMap.
+ */
+static void add_name_mappings(const upb_filedef *file) {
+  size_t i;
+  for (i = 0; i < upb_filedef_msgcount(file); i++) {
+    NameMap_AddMessage(upb_filedef_msg(file, i));
+  }
+
+  for (i = 0; i < upb_filedef_enumcount(file); i++) {
+    NameMap_AddEnum(upb_filedef_enum(file, i));
+  }
+}
+
+/*
+ * add_name_mappings()
+ *
+ * Adds the given descriptor data to this DescriptorPool.
+ */
+static void add_descriptor(DescriptorPool *pool, const char *data,
+                           int data_len, upb_arena *arena) {
+  size_t i, n;
+  google_protobuf_FileDescriptorSet *set;
+  const google_protobuf_FileDescriptorProto* const* files;
+
+  set = google_protobuf_FileDescriptorSet_parse(data, data_len, arena);
+
+  if (!set) {
+    zend_error(E_ERROR, "Failed to parse binary descriptor\n");
+    return;
+  }
+
+  files = google_protobuf_FileDescriptorSet_file(set, &n);
+
+  for (i = 0; i < n; i++) {
+    const google_protobuf_FileDescriptorProto* file = files[i];
+    upb_strview name = google_protobuf_FileDescriptorProto_name(file);
+    upb_status status;
+    const upb_filedef *file_def;
+    upb_status_clear(&status);
+
+    if (upb_symtab_lookupfile2(pool->symtab, name.data, name.size)) {
+      // Already added.
+      continue;
+    }
+
+    // The PHP code generator currently special-cases descriptor.proto.  It
+    // doesn't add it as a dependency even if the proto file actually does
+    // depend on it.
+    if (depends_on_descriptor(file)) {
+      google_protobuf_FileDescriptorProto_getmsgdef(pool->symtab);
+    }
+
+    file_def = upb_symtab_addfile(pool->symtab, file, &status);
+    CheckUpbStatus(&status, "Unable to load descriptor");
+    add_name_mappings(file_def);
+  }
+}
+
+/*
+ * DescriptorPool::internalAddGeneratedFile()
+ *
+ * Adds the given descriptor data to this DescriptorPool.
+ */
+PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
+  DescriptorPool *intern = GetPool(getThis());
+  char *data = NULL;
+  zend_long data_len;
+  zend_bool use_nested_submsg = false;
+  upb_arena *arena;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len,
+                            &use_nested_submsg) != SUCCESS) {
+    return;
+  }
+
+  arena = upb_arena_new();
+  add_descriptor(intern, data, data_len, arena);
+  upb_arena_free(arena);
+}
+
+static zend_function_entry DescriptorPool_methods[] = {
+  PHP_ME(DescriptorPool, getGeneratedPool, NULL,
+         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+  PHP_ME(DescriptorPool, getDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(DescriptorPool, getDescriptorByProtoName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(DescriptorPool, getEnumDescriptorByClassName, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(DescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// GPBType
+// -----------------------------------------------------------------------------
+
+zend_class_entry* gpb_type_type;
+
+static zend_function_entry gpb_type_methods[] = {
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// Module Init
+// -----------------------------------------------------------------------------
+
+void Def_ModuleInit() {
+  zend_class_entry tmp_ce;
+  zend_object_handlers *h;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\OneofDescriptor",
+                   OneofDescriptor_methods);
+  OneofDescriptor_class_entry = zend_register_internal_class(&tmp_ce);
+  OneofDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  OneofDescriptor_class_entry->create_object = CreateHandler_ReturnNull;
+  h = &OneofDescriptor_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\EnumValueDescriptor",
+                   EnumValueDescriptor_methods);
+  EnumValueDescriptor_class_entry = zend_register_internal_class(&tmp_ce);
+  EnumValueDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  EnumValueDescriptor_class_entry->create_object = CreateHandler_ReturnNull;
+  h = &EnumValueDescriptor_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\EnumDescriptor",
+                   EnumDescriptor_methods);
+  EnumDescriptor_class_entry = zend_register_internal_class(&tmp_ce);
+  EnumDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  EnumDescriptor_class_entry->create_object = CreateHandler_ReturnNull;
+  h = &EnumDescriptor_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Descriptor",
+                   Descriptor_methods);
+
+  Descriptor_class_entry = zend_register_internal_class(&tmp_ce);
+  Descriptor_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  Descriptor_class_entry->create_object = CreateHandler_ReturnNull;
+  h = &Descriptor_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = Descriptor_destructor;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\FieldDescriptor",
+                   FieldDescriptor_methods);
+  FieldDescriptor_class_entry = zend_register_internal_class(&tmp_ce);
+  FieldDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  FieldDescriptor_class_entry->create_object = CreateHandler_ReturnNull;
+  h = &FieldDescriptor_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\DescriptorPool",
+                   DescriptorPool_methods);
+  DescriptorPool_class_entry = zend_register_internal_class(&tmp_ce);
+  DescriptorPool_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  DescriptorPool_class_entry->create_object = DescriptorPool_create;
+  h = &DescriptorPool_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = DescriptorPool_destructor;
+
+  // GPBType.
+#define STR(str) (str), strlen(str)
+  zend_class_entry class_type;
+  INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBType",
+                   gpb_type_methods);
+  gpb_type_type = zend_register_internal_class(&class_type);
+  zend_declare_class_constant_long(gpb_type_type, STR("DOUBLE"), 1);
+  zend_declare_class_constant_long(gpb_type_type, STR("FLOAT"), 2);
+  zend_declare_class_constant_long(gpb_type_type, STR("INT64"), 3);
+  zend_declare_class_constant_long(gpb_type_type, STR("UINT64"), 4);
+  zend_declare_class_constant_long(gpb_type_type, STR("INT32"), 5);
+  zend_declare_class_constant_long(gpb_type_type, STR("FIXED64"), 6);
+  zend_declare_class_constant_long(gpb_type_type, STR("FIXED32"), 7);
+  zend_declare_class_constant_long(gpb_type_type, STR("BOOL"), 8);
+  zend_declare_class_constant_long(gpb_type_type, STR("STRING"), 9);
+  zend_declare_class_constant_long(gpb_type_type, STR("GROUP"), 10);
+  zend_declare_class_constant_long(gpb_type_type, STR("MESSAGE"), 11);
+  zend_declare_class_constant_long(gpb_type_type, STR("BYTES"), 12);
+  zend_declare_class_constant_long(gpb_type_type, STR("UINT32"), 13);
+  zend_declare_class_constant_long(gpb_type_type, STR("ENUM"), 14);
+  zend_declare_class_constant_long(gpb_type_type, STR("SFIXED32"), 15);
+  zend_declare_class_constant_long(gpb_type_type, STR("SFIXED64"), 16);
+  zend_declare_class_constant_long(gpb_type_type, STR("SINT32"), 17);
+  zend_declare_class_constant_long(gpb_type_type, STR("SINT64"), 18);
+#undef STR
+}
diff --git a/php/ext/google/protobuf2/def.h b/php/ext/google/protobuf2/def.h
new file mode 100644
index 0000000..515a83e
--- /dev/null
+++ b/php/ext/google/protobuf2/def.h
@@ -0,0 +1,69 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_DEF_H_
+#define PHP_PROTOBUF_DEF_H_
+
+#include <php.h>
+
+#include "php-upb.h"
+
+// Initializes the Def module, which defines all of the descriptor classes.
+void Def_ModuleInit();
+
+// Creates a new DescriptorPool to wrap the given symtab. The DescriptorPool
+// takes ownership of the given symtab. If symtab is NULL, the DescriptorPool
+// will create an empty symtab instead.
+void DescriptorPool_CreateWithSymbolTable(zval *zv, upb_symtab *symtab);
+
+// Given a zval representing a DescriptorPool, steals and returns its symtab,
+// which is now owned by the caller.
+upb_symtab *DescriptorPool_Steal(zval *zv);
+
+upb_symtab *DescriptorPool_GetSymbolTable();
+
+typedef struct Descriptor {
+  zend_object std;
+  const upb_msgdef *msgdef;
+  zend_class_entry *class_entry;
+} Descriptor;
+
+// Gets or creates a PHP Descriptor object for a |ce| and stores it in |val|.
+// If this is not a protobuf generated class, |val| will be set to null.
+void Descriptor_FromClassEntry(zval *val, zend_class_entry *ce);
+
+// Gets or creates a Descriptor* for the given class entry, upb_msgdef, or
+// upb_fielddef. The returned Descriptor* will live for the entire request,
+// so no ref is necessary to keep it alive.
+Descriptor* Descriptor_GetFromClassEntry(zend_class_entry *ce);
+Descriptor* Descriptor_GetFromMessageDef(const upb_msgdef *m);
+Descriptor* Descriptor_GetFromFieldDef(const upb_fielddef *f);
+
+#endif  // PHP_PROTOBUF_DEF_H_
diff --git a/php/ext/google/protobuf2/make-preload.php b/php/ext/google/protobuf2/make-preload.php
new file mode 100644
index 0000000..c6043e2
--- /dev/null
+++ b/php/ext/google/protobuf2/make-preload.php
@@ -0,0 +1,62 @@
+<?php
+
+$cwd = dirname($argv[0]) . "/../../../src";
+chdir($cwd);
+
+$cmd = "grep -r -l 'Generated by the protocol buffer' * | grep 'php$' | grep -v Internal";
+$handle = popen($cmd, 'r');
+$filenames = explode("\n", stream_get_contents($handle));
+array_pop($filenames);  // empty string after last '\n'
+$filenames[] = "Google/Protobuf/DescriptorPool.php";
+$output = "../ext/google/protobuf2/bundled_php.c";
+
+function stripSuffix($str, $suffix) {
+  return substr($str, 0, strlen($str) - strlen($suffix));
+}
+
+function toClassName($filename) {
+  # Google/Protobuf/BoolValue.php -> Google\\Protobuf\\BoolValue
+  $ret = stripSuffix($filename, ".php");
+  return str_replace("/", "\\\\", $ret);
+}
+
+function toCSymbolName($filename) {
+  # Google/Protobuf/BoolValue.php -> Google__Protobuf__BoolValue
+  $ret = stripSuffix($filename, ".php");
+  return str_replace("/", "__", $ret);
+}
+
+$f = fopen($output, "w");
+
+fwrite($f, "#include \"bundled_php.h\"\n");
+fwrite($f, "#include \"stdlib.h\"\n");
+
+foreach ($filenames as $filename) {
+  print("Reading $filename...\n");
+  $contents = file_get_contents($filename);
+  $contents = substr($contents, 5);  // Strip <?php
+  $c_symbol_name = toCSymbolName($filename);
+  fwrite($f, "static const char {$c_symbol_name}[] = {");
+  for ($i = 0; $i < strlen($contents); $i++) {
+    if ($i % 10 == 0) {
+      fwrite($f, "\n");
+    }
+    fprintf($f, "  0x%02x,", ord($contents[$i]));
+  }
+  fwrite($f, "0};\n");
+}
+
+fwrite($f, "static BundledPhp_File php[] = {\n");
+foreach ($filenames as $filename) {
+  $class_name = toClassName($filename);
+  $c_symbol_name = toCSymbolName($filename);
+  fwrite($f, "  {\"$class_name\", $c_symbol_name},\n");
+}
+
+fwrite($f, "  {NULL, NULL}\n");
+fwrite($f, "};\n");
+fwrite($f, "BundledPhp_File *bundled_files = &php[0];\n");
+fclose($f);
+
+print("Wrote $output\n");
+?>
diff --git a/php/ext/google/protobuf2/map.c b/php/ext/google/protobuf2/map.c
new file mode 100644
index 0000000..0217c64
--- /dev/null
+++ b/php/ext/google/protobuf2/map.c
@@ -0,0 +1,590 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "map.h"
+
+#include <Zend/zend_API.h>
+#include <Zend/zend_interfaces.h>
+
+#include <ext/spl/spl_iterators.h>
+
+#include "arena.h"
+#include "convert.h"
+#include "php-upb.h"
+#include "protobuf.h"
+
+static void MapFieldIter_make(zval *val, zval *map_field);
+
+// -----------------------------------------------------------------------------
+// MapField
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  zval arena;
+  upb_map *map;
+  upb_fieldtype_t key_type;
+  upb_fieldtype_t val_type;
+  const Descriptor* desc;  // When values are messages.
+} MapField;
+
+zend_class_entry *MapField_class_entry;
+static zend_object_handlers MapField_object_handlers;
+
+// PHP Object Handlers /////////////////////////////////////////////////////////
+
+/**
+ * MapField_create()
+ *
+ * PHP class entry function to allocate and initialize a new MapField
+ * object.
+ */
+static zend_object* MapField_create(zend_class_entry *class_type) {
+  MapField *intern = emalloc(sizeof(MapField));
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &MapField_object_handlers;
+  Arena_Init(&intern->arena);
+  intern->map = NULL;
+  // Skip object_properties_init(), we don't allow derived classes.
+  return &intern->std;
+}
+
+/**
+ * MapField_dtor()
+ *
+ * Object handler to destroy a MapField. This releases all resources
+ * associated with the message. Note that it is possible to access a destroyed
+ * object from PHP in rare cases.
+ */
+static void MapField_destructor(zend_object* obj) {
+  MapField* intern = (MapField*)obj;
+  ObjCache_Delete(intern->map);
+  zval_ptr_dtor(&intern->arena);
+  zend_object_std_dtor(&intern->std);
+}
+
+static zval *Map_GetPropertyPtrPtr(zval *object, zval *member, int type,
+                                      void **cache_slot) {
+  return NULL;  // We don't offer direct references to our properties.
+}
+
+static HashTable *map_get_properties(zval *object TSRMLS_DC) {
+  return NULL;  // We do not have a properties table.
+}
+
+// C Functions from map.h //////////////////////////////////////////////////////
+
+// These are documented in the header file.
+
+void MapField_GetPhpWrapper(zval *val, upb_map *map, const upb_fielddef *f,
+                            zval *arena) {
+  if (!map) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(map, val)) {
+    const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
+    const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
+    const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
+    MapField *intern = emalloc(sizeof(MapField));
+    zend_object_std_init(&intern->std, MapField_class_entry);
+    intern->std.handlers = &MapField_object_handlers;
+    ZVAL_COPY(&intern->arena, arena);
+    intern->map = map;
+    intern->key_type = upb_fielddef_type(key_f);
+    intern->val_type = upb_fielddef_type(val_f);
+    intern->desc = Descriptor_GetFromFieldDef(val_f);
+    // Skip object_properties_init(), we don't allow derived classes.
+    ObjCache_Add(intern->map, &intern->std);
+    ZVAL_OBJ(val, &intern->std);
+  }
+}
+
+upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena) {
+  const upb_msgdef *ent = upb_fielddef_msgsubdef(f);
+  const upb_fielddef *key_f = upb_msgdef_itof(ent, 1);
+  const upb_fielddef *val_f = upb_msgdef_itof(ent, 2);
+  upb_fieldtype_t key_type = upb_fielddef_type(key_f);
+  upb_fieldtype_t val_type = upb_fielddef_type(val_f);
+  const Descriptor *desc = Descriptor_GetFromFieldDef(val_f);
+
+  if (Z_ISREF_P(val)) {
+    ZVAL_DEREF(val);
+  }
+
+  if (Z_TYPE_P(val) == IS_ARRAY) {
+    upb_map *map = upb_map_new(arena, key_type, val_type);
+    HashTable *table = HASH_OF(val);
+    HashPosition pos;
+
+    zend_hash_internal_pointer_reset_ex(table, &pos);
+
+    while (true) {
+      zval php_key;
+      zval *php_val;
+      upb_msgval upb_key;
+      upb_msgval upb_val;
+
+      zend_hash_get_current_key_zval_ex(table, &php_key, &pos);
+      php_val = zend_hash_get_current_data_ex(table, &pos);
+
+      if (!php_val) return map;
+
+      if (!Convert_PhpToUpb(&php_key, &upb_key, key_type, NULL, arena) ||
+          !Convert_PhpToUpbAutoWrap(php_val, &upb_val, val_type, desc, arena)) {
+        return NULL;
+      }
+
+      upb_map_set(map, upb_key, upb_val, arena);
+      zend_hash_move_forward_ex(table, &pos);
+      zval_dtor(&php_key);
+    }
+  } else if (Z_TYPE_P(val) == IS_OBJECT &&
+             Z_OBJCE_P(val) == MapField_class_entry) {
+    MapField *intern = (MapField*)Z_OBJ_P(val);
+
+    if (intern->key_type != key_type || intern->val_type != val_type ||
+        intern->desc != desc) {
+      php_error_docref(NULL, E_USER_ERROR, "Wrong type for this map field.");
+      return NULL;
+    }
+
+    upb_arena_fuse(arena, Arena_Get(&intern->arena));
+    return intern->map;
+  } else {
+    php_error_docref(NULL, E_USER_ERROR, "Must be a map");
+    return NULL;
+  }
+}
+
+// MapField PHP methods ////////////////////////////////////////////////////////
+
+/**
+ * MapField::__construct()
+ *
+ * Constructs an instance of MapField.
+ * @param long Key type.
+ * @param long Value type.
+ * @param string Message/Enum class (message/enum value types only).
+ */
+PHP_METHOD(MapField, __construct) {
+  MapField *intern = (MapField*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  zend_long key_type, val_type;
+  zend_class_entry* klass = NULL;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|C", &key_type, &val_type,
+                            &klass) != SUCCESS) {
+    return;
+  }
+
+  intern->key_type = pbphp_dtype_to_type(key_type);
+  intern->val_type = pbphp_dtype_to_type(val_type);
+  intern->desc = Descriptor_GetFromClassEntry(klass);
+
+  // Check that the key type is an allowed type.
+  switch (intern->key_type) {
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_INT64:
+    case UPB_TYPE_UINT32:
+    case UPB_TYPE_UINT64:
+    case UPB_TYPE_BOOL:
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      // These are OK.
+      break;
+    default:
+      zend_error(E_USER_ERROR, "Invalid key type for map.");
+  }
+
+  if (intern->val_type == UPB_TYPE_MESSAGE && klass == NULL) {
+    php_error_docref(NULL, E_USER_ERROR,
+                     "Message/enum type must have concrete class.");
+    return;
+  }
+
+  intern->map = upb_map_new(arena, intern->key_type, intern->val_type);
+  ObjCache_Add(intern->map, &intern->std);
+}
+
+/**
+ * MapField::offsetExists()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   isset($map[$idx]);
+ *   empty($map[$idx]);
+ *
+ * @param long The index to be checked.
+ * @return bool True if the element at the given index exists.
+ */
+PHP_METHOD(MapField, offsetExists) {
+  MapField *intern = (MapField*)Z_OBJ_P(getThis());
+  zval *key;
+  upb_msgval upb_key;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
+      !Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) {
+    return;
+  }
+
+  RETURN_BOOL(upb_map_get(intern->map, upb_key, NULL));
+}
+
+/**
+ * MapField::offsetGet()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   $x = $map[$idx];
+ *
+ * @param long The index of the element to be fetched.
+ * @return object The stored element at given index.
+ * @exception Invalid type for index.
+ * @exception Non-existing index.
+ */
+PHP_METHOD(MapField, offsetGet) {
+  MapField *intern = (MapField*)Z_OBJ_P(getThis());
+  zval *key;
+  zval ret;
+  upb_msgval upb_key, upb_val;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
+      !Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) {
+    return;
+  }
+
+  if (!upb_map_get(intern->map, upb_key, &upb_val)) {
+    zend_error(E_USER_ERROR, "Given key doesn't exist.");
+    return;
+  }
+
+  Convert_UpbToPhp(upb_val, &ret, intern->val_type, intern->desc, &intern->arena);
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/**
+ * MapField::offsetSet()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   $map[$idx] = $x;
+ *
+ * @param long The index of the element to be assigned.
+ * @param object The element to be assigned.
+ * @exception Invalid type for index.
+ * @exception Non-existing index.
+ * @exception Incorrect type of the element.
+ */
+PHP_METHOD(MapField, offsetSet) {
+  MapField *intern = (MapField*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  zval *key, *val;
+  upb_msgval upb_key, upb_val;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &val) != SUCCESS ||
+      !Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL) ||
+      !Convert_PhpToUpb(val, &upb_val, intern->val_type, intern->desc, arena)) {
+    return;
+  }
+
+  upb_map_set(intern->map, upb_key, upb_val, arena);
+}
+
+/**
+ * MapField::offsetUnset()
+ *
+ * Implements the ArrayAccess interface. Invoked when PHP code calls:
+ *
+ *   unset($map[$idx]);
+ *
+ * @param long The index of the element to be removed.
+ * @exception Invalid type for index.
+ * @exception The element to be removed is not at the end of the MapField.
+ */
+PHP_METHOD(MapField, offsetUnset) {
+  MapField *intern = (MapField*)Z_OBJ_P(getThis());
+  zval *key;
+  upb_msgval upb_key;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS ||
+      !Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL)) {
+    return;
+  }
+
+  upb_map_delete(intern->map, upb_key);
+}
+
+/**
+ * MapField::count()
+ *
+ * Implements the Countable interface. Invoked when PHP code calls:
+ *
+ *   $len = count($map);
+ * Return the number of stored elements.
+ * This will also be called for: count($map)
+ * @return long The number of stored elements.
+ */
+PHP_METHOD(MapField, count) {
+  MapField *intern = (MapField*)Z_OBJ_P(getThis());
+
+  if (zend_parse_parameters_none() == FAILURE) {
+    return;
+  }
+
+  RETURN_LONG(upb_map_size(intern->map));
+}
+
+/**
+ * MapField::getIterator()
+ *
+ * Implements the IteratorAggregate interface. Invoked when PHP code calls:
+ *
+ *   foreach ($arr) {}
+ *
+ * @return object Beginning iterator.
+ */
+PHP_METHOD(MapField, getIterator) {
+  zval ret;
+  MapFieldIter_make(&ret, getThis());
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
+  ZEND_ARG_INFO(0, index)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
+  ZEND_ARG_INFO(0, index)
+  ZEND_ARG_INFO(0, newval)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
+ZEND_END_ARG_INFO()
+
+static zend_function_entry MapField_methods[] = {
+  PHP_ME(MapField, __construct,  NULL,              ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetGet,    arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetSet,    arginfo_offsetSet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, offsetUnset,  arginfo_offsetGet, ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, count,        arginfo_void,      ZEND_ACC_PUBLIC)
+  PHP_ME(MapField, getIterator,  arginfo_void,      ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// MapFieldIter
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  zval map_field;
+  size_t position;
+} MapFieldIter;
+
+zend_class_entry *MapFieldIter_class_entry;
+static zend_object_handlers MapFieldIter_object_handlers;
+
+/**
+ * MapFieldIter_create()
+ *
+ * PHP class entry function to allocate and initialize a new MapFieldIter
+ * object.
+ */
+zend_object* MapFieldIter_create(zend_class_entry *class_type) {
+  MapFieldIter *intern = emalloc(sizeof(MapFieldIter));
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &MapFieldIter_object_handlers;
+  ZVAL_NULL(&intern->map_field);
+  intern->position = 0;
+  // Skip object_properties_init(), we don't allow derived classes.
+  return &intern->std;
+}
+
+/**
+ * MapFieldIter_dtor()
+ *
+ * Object handler to destroy a MapFieldIter. This releases all resources
+ * associated with the message. Note that it is possible to access a destroyed
+ * object from PHP in rare cases.
+ */
+static void map_field_iter_dtor(zend_object* obj) {
+  MapFieldIter* intern = (MapFieldIter*)obj;
+  zval_ptr_dtor(&intern->map_field);
+  zend_object_std_dtor(&intern->std);
+}
+
+/**
+ * MapFieldIter_make()
+ *
+ * Function to create a MapFieldIter directly from C.
+ */
+static void MapFieldIter_make(zval *val, zval *map_field) {
+  MapFieldIter *iter;
+  ZVAL_OBJ(val,
+           MapFieldIter_class_entry->create_object(MapFieldIter_class_entry));
+  iter = (MapFieldIter*)Z_OBJ_P(val);
+  ZVAL_COPY(&iter->map_field, map_field);
+}
+
+// -----------------------------------------------------------------------------
+// PHP MapFieldIter Methods
+// -----------------------------------------------------------------------------
+
+/*
+ * When a user writes:
+ *
+ *   foreach($arr as $key => $val) {}
+ *
+ * PHP translates this into:
+ *
+ *   $iter = $arr->getIterator();
+ *   for ($iter->rewind(); $iter->valid(); $iter->next()) {
+ *     $key = $iter->key();
+ *     $val = $iter->current();
+ *   }
+ */
+
+/**
+ * MapFieldIter::rewind()
+ *
+ * Implements the Iterator interface. Sets the iterator to the first element.
+ */
+PHP_METHOD(MapFieldIter, rewind) {
+  MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
+  MapField *map_field = (MapField*)Z_OBJ_P(&intern->map_field);
+  intern->position = UPB_MAP_BEGIN;
+  upb_mapiter_next(map_field->map, &intern->position);
+}
+
+/**
+ * MapFieldIter::current()
+ *
+ * Implements the Iterator interface. Returns the current value.
+ */
+PHP_METHOD(MapFieldIter, current) {
+  MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
+  MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
+  upb_msgval upb_val = upb_mapiter_value(field->map, intern->position);
+  zval ret;
+  Convert_UpbToPhp(upb_val, &ret, field->val_type, field->desc, &field->arena);
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/**
+ * MapFieldIter::key()
+ *
+ * Implements the Iterator interface. Returns the current key.
+ */
+PHP_METHOD(MapFieldIter, key) {
+  MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
+  MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
+  upb_msgval upb_key = upb_mapiter_key(field->map, intern->position);
+  zval ret;
+  Convert_UpbToPhp(upb_key, &ret, field->key_type, NULL, NULL);
+  RETURN_ZVAL(&ret, 0, 1);
+}
+
+/**
+ * MapFieldIter::next()
+ *
+ * Implements the Iterator interface. Advances to the next element.
+ */
+PHP_METHOD(MapFieldIter, next) {
+  MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
+  MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
+  upb_mapiter_next(field->map, &intern->position);
+}
+
+/**
+ * MapFieldIter::valid()
+ *
+ * Implements the Iterator interface. Returns true if this is a valid element.
+ */
+PHP_METHOD(MapFieldIter, valid) {
+  MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis());
+  MapField *field = (MapField*)Z_OBJ_P(&intern->map_field);
+  bool done = upb_mapiter_done(field->map, intern->position);
+  RETURN_BOOL(!done);
+}
+
+static zend_function_entry map_field_iter_methods[] = {
+  PHP_ME(MapFieldIter, rewind,      arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, current,     arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, key,         arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, next,        arginfo_void, ZEND_ACC_PUBLIC)
+  PHP_ME(MapFieldIter, valid,       arginfo_void, ZEND_ACC_PUBLIC)
+  ZEND_FE_END
+};
+
+// -----------------------------------------------------------------------------
+// Module init.
+// -----------------------------------------------------------------------------
+
+/**
+ * Map_ModuleInit()
+ *
+ * Called when the C extension is loaded to register all types.
+ */
+
+void Map_ModuleInit() {
+  zend_class_entry tmp_ce;
+  zend_object_handlers *h;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapField",
+                   MapField_methods);
+
+  MapField_class_entry = zend_register_internal_class(&tmp_ce);
+  zend_class_implements(MapField_class_entry, 3, spl_ce_ArrayAccess,
+                        zend_ce_aggregate, spl_ce_Countable);
+  MapField_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  MapField_class_entry->create_object = MapField_create;
+
+  h = &MapField_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = MapField_destructor;
+  h->get_properties = map_get_properties;
+  h->get_property_ptr_ptr = Map_GetPropertyPtrPtr;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapFieldIter",
+                   map_field_iter_methods);
+
+  MapFieldIter_class_entry = zend_register_internal_class(&tmp_ce);
+  zend_class_implements(MapFieldIter_class_entry, 1, zend_ce_iterator);
+  MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL;
+  MapFieldIter_class_entry->create_object = MapFieldIter_create;
+
+  h = &MapFieldIter_object_handlers;
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = map_field_iter_dtor;
+}
diff --git a/php/ext/google/protobuf2/map.h b/php/ext/google/protobuf2/map.h
new file mode 100644
index 0000000..9b3c9c1
--- /dev/null
+++ b/php/ext/google/protobuf2/map.h
@@ -0,0 +1,60 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_MAP_H_
+#define PHP_PROTOBUF_MAP_H_
+
+#include <php.h>
+
+#include "php-upb.h"
+
+void Map_ModuleInit();
+
+// Gets a upb_map* for the PHP object |val|:
+//  * If |val| is a RepeatedField object, we first check its type and verify
+//    that that the elements have the correct type for |f|. If so, we return the
+//    wrapped upb_map*. We also make sure that this map's arena is fused to
+//    |arena|, so the returned upb_map is guaranteed to live as long as
+//    |arena|.
+//  * If |val| is a PHP Map, we attempt to create a new upb_map using
+//    |arena| and add all of the PHP elements to it.
+//
+// If an error occurs, we raise a PHP error and return NULL.
+upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena);
+
+// Creates a PHP MapField object for the given upb_map* and |f| and returns it
+// in |val|. The PHP object will keep a reference to this |arena| to ensure the
+// underlying array data stays alive.
+//
+// If |map| is NULL, this will return a PHP null object.
+void MapField_GetPhpWrapper(zval *val, upb_map *arr, const upb_fielddef *f,
+                            zval *arena);
+
+#endif  // PHP_PROTOBUF_MAP_H_
diff --git a/php/ext/google/protobuf2/message.c b/php/ext/google/protobuf2/message.c
new file mode 100644
index 0000000..6c28dee
--- /dev/null
+++ b/php/ext/google/protobuf2/message.c
@@ -0,0 +1,841 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "message.h"
+
+#include <inttypes.h>
+#include <php.h>
+#include <stdlib.h>
+
+// This is not self-contained: it must be after other Zend includes.
+#include <Zend/zend_exceptions.h>
+
+#include "arena.h"
+#include "array.h"
+#include "convert.h"
+#include "def.h"
+#include "map.h"
+#include "php-upb.h"
+#include "protobuf.h"
+
+// -----------------------------------------------------------------------------
+// Message
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  zend_object std;
+  zval arena;
+  const Descriptor* desc;
+  upb_msg *msg;
+} Message;
+
+zend_class_entry *message_ce;
+static zend_object_handlers message_object_handlers;
+
+// PHP Object Handlers /////////////////////////////////////////////////////////
+
+/**
+ * Message_create()
+ *
+ * PHP class entry function to allocate and initialize a new Message object.
+ */
+static zend_object* Message_create(zend_class_entry *class_type) {
+  Message *intern = emalloc(sizeof(Message));
+  // XXX(haberman): verify whether we actually want to take this route.
+  class_type->default_properties_count = 0;
+  zend_object_std_init(&intern->std, class_type);
+  intern->std.handlers = &message_object_handlers;
+  Arena_Init(&intern->arena);
+  return &intern->std;
+}
+
+/**
+ * Message_dtor()
+ *
+ * Object handler to destroy a Message. This releases all resources associated
+ * with the message. Note that it is possible to access a destroyed object from
+ * PHP in rare cases.
+ */
+static void Message_dtor(zend_object* obj) {
+  Message* intern = (Message*)obj;
+  ObjCache_Delete(intern->msg);
+  zval_dtor(&intern->arena);
+  zend_object_std_dtor(&intern->std);
+}
+
+/**
+ * get_field()
+ *
+ * Helper function to look up a field given a member name (as a string).
+ */
+static const upb_fielddef *get_field(Message *msg, zval *member) {
+  const upb_msgdef *m = msg->desc->msgdef;
+  const upb_fielddef *f =
+      upb_msgdef_ntof(m, Z_STRVAL_P(member), Z_STRLEN_P(member));
+
+  if (!f) {
+    zend_throw_exception_ex(NULL, 0, "No such property %s.",
+                            ZSTR_VAL(msg->desc->class_entry->name));
+  }
+
+  return f;
+}
+
+/**
+ * Message_read_property()
+ *
+ * Object handler for reading a property in PHP. Called when PHP code does:
+ *
+ *   $x = $message->foobar;
+ *
+ * Note that all properties of generated messages are private, so this should
+ * only be possible to invoke from generated code, which has accessors like:
+ *
+ *   public function getOptionalInt32()
+ *   {
+ *       return $this->optional_int32;
+ *   }
+ *
+ * We lookup the field and return the scalar, RepeatedField, or MapField for
+ * this field.
+ */
+static zval *Message_read_property(zval *obj, zval *member, int type,
+                                   void **cache_slot, zval *rv) {
+  Message* intern = (Message*)Z_OBJ_P(obj);
+  const upb_fielddef *f = get_field(intern, member);
+  upb_arena *arena = Arena_Get(&intern->arena);
+
+  if (!f) return NULL;
+
+  if (upb_fielddef_ismap(f)) {
+    upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
+    MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena);
+  } else if (upb_fielddef_isseq(f)) {
+    upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
+    RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena);
+  } else {
+    upb_msgval msgval = upb_msg_get(intern->msg, f);
+    const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
+    Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena);
+  }
+
+  return rv;
+}
+
+/**
+ * Message_write_property()
+ *
+ * Object handler for writing a property in PHP. Called when PHP code does:
+ *
+ *   $message->foobar = $x;
+ *
+ * Note that all properties of generated messages are private, so this should
+ * only be possible to invoke from generated code, which has accessors like:
+ *
+ *   public function setOptionalInt32($var)
+ *   {
+ *       GPBUtil::checkInt32($var);
+ *       $this->optional_int32 = $var;
+ *
+ *       return $this;
+ *   }
+ *
+ * The C extension version of checkInt32() doesn't actually check anything, so
+ * we perform all checking and conversion in this function.
+ */
+static void Message_write_property(zval *obj, zval *member, zval *val,
+                                   void **cache_slot) {
+  Message* intern = (Message*)Z_OBJ_P(obj);
+  const upb_fielddef *f = get_field(intern, member);
+  upb_arena *arena = Arena_Get(&intern->arena);
+  upb_msgval msgval;
+
+  if (!f) return;
+
+  if (upb_fielddef_ismap(f)) {
+    msgval.map_val = MapField_GetUpbMap(val, f, arena);
+    if (!msgval.map_val) return;
+  } else if (upb_fielddef_isseq(f)) {
+    msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
+    if (!msgval.array_val) return;
+  } else {
+    upb_fieldtype_t type = upb_fielddef_type(f);
+    const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
+    bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena);
+    if (!ok) return;
+  }
+
+  upb_msg_set(intern->msg, f, msgval, arena);
+}
+
+/**
+ * Message_get_property_ptr_ptr()
+ *
+ * Object handler for the get_property_ptr_ptr event in PHP. This returns a
+ * reference to our internal properties. We don't support this, so we return
+ * NULL.
+ */
+static zval *Message_get_property_ptr_ptr(zval *object, zval *member, int type,
+                                          void **cache_slot) {
+  return NULL;  // We do not have a properties table.
+}
+
+/**
+ * Message_get_properties()
+ *
+ * Object handler for the get_properties event in PHP. This returns a HashTable
+ * of our internal properties. We don't support this, so we return NULL.
+ */
+static HashTable* Message_get_properties(zval* object TSRMLS_DC) {
+  return NULL;  // We don't offer direct references to our properties.
+}
+
+// C Functions from message.h. /////////////////////////////////////////////////
+
+// These are documented in the header file.
+
+void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
+                           zval *arena) {
+  if (!msg) {
+    ZVAL_NULL(val);
+    return;
+  }
+
+  if (!ObjCache_Get(msg, val)) {
+    Message *intern = emalloc(sizeof(Message));
+    // XXX(haberman): verify whether we actually want to take this route.
+    desc->class_entry->default_properties_count = 0;
+    zend_object_std_init(&intern->std, desc->class_entry);
+    intern->std.handlers = &message_object_handlers;
+    ZVAL_COPY(&intern->arena, arena);
+    intern->desc = desc;
+    intern->msg = msg;
+    ZVAL_OBJ(val, &intern->std);
+    ObjCache_Add(intern->msg, &intern->std);
+  }
+}
+
+bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena,
+                           upb_msg **msg) {
+  PBPHP_ASSERT(desc);
+
+  if (Z_ISREF_P(val)) {
+    ZVAL_DEREF(val);
+  }
+
+  if (Z_TYPE_P(val) == IS_NULL) {
+    *msg = NULL;
+    return true;
+  }
+
+  if (Z_TYPE_P(val) == IS_OBJECT &&
+      instanceof_function(Z_OBJCE_P(val), desc->class_entry)) {
+    Message *intern = (Message*)Z_OBJ_P(val);
+    upb_arena_fuse(arena, Arena_Get(&intern->arena));
+    *msg = intern->msg;
+    return true;
+  } else {
+    zend_throw_exception_ex(NULL, 0, "Given value is not an instance of %s.",
+                            ZSTR_VAL(desc->class_entry->name));
+    return false;
+  }
+}
+
+// Message PHP methods /////////////////////////////////////////////////////////
+
+/**
+ * Message_InitFromPhp()
+ *
+ * Helper method to handle the initialization of a message from a PHP value, eg.
+ *
+ *   $m = new TestMessage([
+ *       'optional_int32' => -42,
+ *       'optional_bool' => true,
+ *       'optional_string' => 'a',
+ *       'optional_enum' => TestEnum::ONE,
+ *       'optional_message' => new Sub([
+ *           'a' => 33
+ *       ]),
+ *       'repeated_int32' => [-42, -52],
+ *       'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
+ *       'repeated_message' => [new Sub(['a' => 34]),
+ *                              new Sub(['a' => 35])],
+ *       'map_int32_int32' => [-62 => -62],
+ *       'map_int32_enum' => [1 => TestEnum::ONE],
+ *       'map_int32_message' => [1 => new Sub(['a' => 36])],
+ *   ]);
+ *
+ * The initializer must be an array.
+ */
+bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init,
+                         upb_arena *arena) {
+  HashTable* table = HASH_OF(init);
+  HashPosition pos;
+
+  if (Z_ISREF_P(init)) {
+    ZVAL_DEREF(init);
+  }
+
+  if (Z_TYPE_P(init) != IS_ARRAY) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Initializer for a message %s must be an array.",
+                            upb_msgdef_fullname(m));
+    return false;
+  }
+
+  zend_hash_internal_pointer_reset_ex(table, &pos);
+
+  while (true) {  // Iterate over key/value pairs.
+    zval key;
+    zval *val;
+    const upb_fielddef *f;
+    upb_msgval msgval;
+
+    zend_hash_get_current_key_zval_ex(table, &key, &pos);
+    val = zend_hash_get_current_data_ex(table, &pos);
+
+    if (!val) return true;  // Finished iteration.
+
+    if (Z_ISREF_P(val)) {
+      ZVAL_DEREF(val);
+    }
+
+    f = upb_msgdef_ntof(m, Z_STRVAL_P(&key), Z_STRLEN_P(&key));
+
+    if (!f) {
+      zend_throw_exception_ex(NULL, 0,
+                              "No such field %s", Z_STRVAL_P(&key));
+      return false;
+    }
+
+    if (upb_fielddef_ismap(f)) {
+      msgval.map_val = MapField_GetUpbMap(val, f, arena);
+      if (!msgval.map_val) return false;
+    } else if (upb_fielddef_isseq(f)) {
+      msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
+      if (!msgval.array_val) return false;
+    } else {
+      const Descriptor *desc = Descriptor_GetFromFieldDef(f);
+      upb_fieldtype_t type = upb_fielddef_type(f);
+      if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) {
+        return false;
+      }
+    }
+
+    upb_msg_set(msg, f, msgval, arena);
+    zend_hash_move_forward_ex(table, &pos);
+    zval_dtor(&key);
+  }
+}
+
+/**
+ * Message::__construct()
+ *
+ * Constructor for Message.
+ * @param array Map of initial values ['k' = val]
+ */
+PHP_METHOD(Message, __construct) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
+  const upb_msgdef *msgdef = desc->msgdef;
+  upb_arena *arena = Arena_Get(&intern->arena);
+  zval *init_arr;
+
+  intern->desc = desc;
+  intern->msg = upb_msg_new(msgdef, arena);
+  ObjCache_Add(intern->msg, &intern->std);
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
+    return;
+  }
+
+  if (init_arr) {
+    Message_InitFromPhp(intern->msg, desc->msgdef, init_arr, arena);
+  }
+}
+
+/**
+ * Message::discardUnknownFields()
+ *
+ * Discards any unknown fields for this message or any submessages.
+ */
+PHP_METHOD(Message, discardUnknownFields) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  upb_msg_discardunknown(intern->msg, intern->desc->msgdef, 64);
+}
+
+/**
+ * Message::clear()
+ *
+ * Clears all fields of this message.
+ */
+PHP_METHOD(Message, clear) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  upb_msg_clear(intern->msg, intern->desc->msgdef);
+}
+
+/**
+ * Message::mergeFrom()
+ *
+ * Merges from the given message, which must be of the same class as us.
+ * @param object Message to merge from.
+ */
+PHP_METHOD(Message, mergeFrom) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  Message* from;
+  upb_arena *arena = Arena_Get(&intern->arena);
+  const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
+  zval* value;
+  char *pb;
+  size_t size;
+  bool ok;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &value,
+                            intern->desc->class_entry) == FAILURE) {
+    return;
+  }
+
+  from = (Message*)Z_OBJ_P(value);
+
+  // Should be guaranteed since we passed the class type to
+  // zend_parse_parameters().
+  PBPHP_ASSERT(from->desc == intern->desc);
+
+  // TODO(haberman): use a temp arena for this once we can make upb_decode()
+  // copy strings.
+  pb = upb_encode(from->msg, l, arena, &size);
+
+  if (!pb) {
+    zend_throw_exception_ex(NULL, 0, "Max nesting exceeded");
+    return;
+  }
+
+  ok = upb_decode(pb, size, intern->msg, l, arena);
+  PBPHP_ASSERT(ok);
+}
+
+/**
+ * Message::mergeFromString()
+ *
+ * Merges from the given string.
+ * @param string Binary protobuf data to merge.
+ */
+PHP_METHOD(Message, mergeFromString) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  char *data = NULL;
+  char *data_copy = NULL;
+  zend_long data_len;
+  const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
+  upb_arena *arena = Arena_Get(&intern->arena);
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) ==
+      FAILURE) {
+    return;
+  }
+
+  // TODO(haberman): avoid this copy when we can make the decoder copy.
+  data_copy = upb_arena_malloc(arena, data_len);
+  memcpy(data_copy, data, data_len);
+
+  if (!upb_decode(data_copy, data_len, intern->msg, l, arena)) {
+    zend_throw_exception_ex(NULL, 0, "Error occurred during parsing");
+    return;
+  }
+}
+
+/**
+ * Message::serializeToString()
+ *
+ * Serializes this message instance to protobuf data.
+ * @return string Serialized protobuf data.
+ */
+PHP_METHOD(Message, serializeToString) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
+  upb_arena *tmp_arena = upb_arena_new();
+  char *data;
+  size_t size;
+
+  data = upb_encode(intern->msg, l, tmp_arena, &size);
+
+  if (!data) {
+    zend_throw_exception_ex(NULL, 0, "Error occurred during serialization");
+    upb_arena_free(tmp_arena);
+    return;
+  }
+
+  RETVAL_STRINGL(data, size);
+  upb_arena_free(tmp_arena);
+}
+
+/**
+ * Message::mergeFromJsonString()
+ *
+ * Merges the JSON data parsed from the given string.
+ * @param string Serialized JSON data.
+ */
+PHP_METHOD(Message, mergeFromJsonString) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  char *data = NULL;
+  char *data_copy = NULL;
+  zend_long data_len;
+  upb_arena *arena = Arena_Get(&intern->arena);
+  upb_status status;
+  zend_bool ignore_json_unknown = false;
+  int options = 0;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len,
+                            &ignore_json_unknown) == FAILURE) {
+    return;
+  }
+
+  // TODO(haberman): avoid this copy when we can make the decoder copy.
+  data_copy = upb_arena_malloc(arena, data_len + 1);
+  memcpy(data_copy, data, data_len);
+  data_copy[data_len] = '\0';
+
+  if (ignore_json_unknown) {
+    options |= UPB_JSONDEC_IGNOREUNKNOWN;
+  }
+
+  upb_status_clear(&status);
+  if (!upb_json_decode(data_copy, data_len, intern->msg, intern->desc->msgdef,
+                       DescriptorPool_GetSymbolTable(), options, arena,
+                       &status)) {
+    zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s",
+                            upb_status_errmsg(&status));
+    return;
+  }
+}
+
+/**
+ * Message::serializeToJsonString()
+ *
+ * Serializes this object to JSON.
+ * @return string Serialized JSON data.
+ */
+PHP_METHOD(Message, serializeToJsonString) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  size_t size;
+  int options = 0;
+  char buf[1024];
+  zend_bool preserve_proto_fieldnames = false;
+  upb_status status;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b",
+                            &preserve_proto_fieldnames) == FAILURE) {
+    return;
+  }
+
+  if (preserve_proto_fieldnames) {
+    options |= UPB_JSONENC_PROTONAMES;
+  }
+
+  upb_status_clear(&status);
+  size = upb_json_encode(intern->msg, intern->desc->msgdef,
+                         DescriptorPool_GetSymbolTable(), options, buf,
+                         sizeof(buf), &status);
+
+  if (!upb_ok(&status)) {
+    zend_throw_exception_ex(NULL, 0,
+                            "Error occurred during JSON serialization: %s",
+                            upb_status_errmsg(&status));
+    return;
+  }
+
+  if (size >= sizeof(buf)) {
+    char *buf2 = malloc(size + 1);
+    upb_json_encode(intern->msg, intern->desc->msgdef,
+                    DescriptorPool_GetSymbolTable(), options, buf2, size + 1,
+                    &status);
+    RETVAL_STRINGL(buf2, size);
+    free(buf2);
+  } else {
+    RETVAL_STRINGL(buf, size);
+  }
+}
+
+/**
+ * Message::readWrapperValue()
+ *
+ * Returns an unboxed value for the given field. This is called from generated
+ * methods for wrapper fields, eg.
+ *
+ *   public function getDoubleValueUnwrapped()
+ *   {
+ *       return $this->readWrapperValue("double_value");
+ *   }
+ *
+ * @return Unwrapped field value or null.
+ */
+PHP_METHOD(Message, readWrapperValue) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  char* member;
+  const upb_fielddef *f;
+  zend_long size;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &member, &size) == FAILURE) {
+    return;
+  }
+
+  f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
+
+  if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
+    zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
+                            upb_msgdef_fullname(intern->desc->msgdef), member);
+    return;
+  }
+
+  if (upb_msg_has(intern->msg, f)) {
+    const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val;
+    const upb_msgdef *m = upb_fielddef_msgsubdef(f);
+    const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
+    const upb_fieldtype_t val_type = upb_fielddef_type(val_f);
+    upb_msgval msgval = upb_msg_get(wrapper, val_f);
+    zval ret;
+    Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena);
+    RETURN_ZVAL(&ret, 1, 0);
+  } else {
+    RETURN_NULL();
+  }
+}
+
+/**
+ * Message::writeWrapperValue()
+ *
+ * Sets the given wrapper field to the given unboxed value. This is called from
+ * generated methods for wrapper fields, eg.
+ *
+ *
+ *   public function setDoubleValueUnwrapped($var)
+ *   {
+ *       $this->writeWrapperValue("double_value", $var);
+ *       return $this;
+ *   }
+ *
+ * @param Unwrapped field value or null.
+ */
+PHP_METHOD(Message, writeWrapperValue) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  upb_arena *arena = Arena_Get(&intern->arena);
+  char* member;
+  const upb_fielddef *f;
+  upb_msgval msgval;
+  zend_long size;
+  zval* val;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &member, &size, &val) ==
+      FAILURE) {
+    return;
+  }
+
+  f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
+
+  if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
+    zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
+                            upb_msgdef_fullname(intern->desc->msgdef), member);
+    return;
+  }
+
+  if (Z_ISREF_P(val)) {
+    ZVAL_DEREF(val);
+  }
+
+  if (Z_TYPE_P(val) == IS_NULL) {
+    upb_msg_clearfield(intern->msg, f);
+  } else {
+    const upb_msgdef *m = upb_fielddef_msgsubdef(f);
+    const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
+    upb_fieldtype_t val_type = upb_fielddef_type(val_f);
+    upb_msg *wrapper;
+
+    if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) {
+      return;  // Error is already set.
+    }
+
+    wrapper = upb_msg_mutable(intern->msg, f, arena).msg;
+    upb_msg_set(wrapper, val_f, msgval, arena);
+  }
+}
+
+/**
+ * Message::whichOneof()
+ *
+ * Given a oneof name, returns the name of the field that is set for this oneof,
+ * or otherwise the empty string.
+ *
+ * @return string The field name in this oneof that is currently set.
+ */
+PHP_METHOD(Message, whichOneof) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  const upb_oneofdef* oneof;
+  const upb_fielddef* field;
+  char* name;
+  zend_long len;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &len) == FAILURE) {
+    return;
+  }
+
+  oneof = upb_msgdef_ntoo(intern->desc->msgdef, name, len);
+
+  if (!oneof) {
+    zend_throw_exception_ex(NULL, 0, "Message %s has no oneof %s",
+                            upb_msgdef_fullname(intern->desc->msgdef), name);
+    return;
+  }
+
+  field = upb_msg_whichoneof(intern->msg, oneof);
+  RETURN_STRING(field ? upb_fielddef_name(field) : "");
+}
+
+/**
+ * Message::readOneof()
+ *
+ * Returns the contents of the given oneof field, given a field number. Called
+ * from generated code methods such as:
+ *
+ *    public function getDoubleValueOneof()
+ *    {
+ *        return $this->readOneof(10);
+ *    }
+ *
+ * @return object The oneof's field value.
+ */
+PHP_METHOD(Message, readOneof) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  zend_long field_num;
+  const upb_fielddef* f;
+  zval ret;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) {
+    return;
+  }
+
+  f = upb_msgdef_itof(intern->desc->msgdef, field_num);
+
+  if (!f || !upb_fielddef_realcontainingoneof(f)) {
+    php_error_docref(NULL, E_USER_ERROR,
+                     "Internal error, no such oneof field %d\n",
+                     (int)field_num);
+  }
+
+  {
+    upb_msgval msgval = upb_msg_get(intern->msg, f);
+    const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
+    Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc,
+                     &intern->arena);
+  }
+
+  RETURN_ZVAL(&ret, 1, 0);
+}
+
+/**
+ * Message::writeOneof()
+ *
+ * Sets the contents of the given oneof field, given a field number. Called
+ * from generated code methods such as:
+ *
+ *    public function setDoubleValueOneof($var)
+ *   {
+ *       GPBUtil::checkMessage($var, \Google\Protobuf\DoubleValue::class);
+ *       $this->writeOneof(10, $var);
+ *
+ *       return $this;
+ *   }
+ *
+ * The C extension version of GPBUtil::check*() does nothing, so we perform
+ * all type checking and conversion here.
+ *
+ * @param integer The field number we are setting.
+ * @param object The field value we want to set.
+ */
+PHP_METHOD(Message, writeOneof) {
+  Message* intern = (Message*)Z_OBJ_P(getThis());
+  zend_long field_num;
+  const upb_fielddef* f;
+  upb_arena *arena = Arena_Get(&intern->arena);
+  upb_msgval msgval;
+  zval* val;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &field_num, &val) ==
+      FAILURE) {
+    return;
+  }
+
+  f = upb_msgdef_itof(intern->desc->msgdef, field_num);
+
+  if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f),
+                        Descriptor_GetFromFieldDef(f), arena)) {
+    return;
+  }
+
+  upb_msg_set(intern->msg, f, msgval, arena);
+}
+
+static zend_function_entry Message_methods[] = {
+  PHP_ME(Message, clear, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, discardUnknownFields, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, serializeToString, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, mergeFromString, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC)
+  PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED)
+  PHP_ME(Message, writeWrapperValue, NULL, ZEND_ACC_PROTECTED)
+  PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
+  PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
+  PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED)
+  PHP_ME(Message, __construct, NULL, ZEND_ACC_PROTECTED)
+  ZEND_FE_END
+};
+
+/**
+ * Message_ModuleInit()
+ *
+ * Called when the C extension is loaded to register all types.
+ */
+void Message_ModuleInit() {
+  zend_class_entry tmp_ce;
+  zend_object_handlers *h = &message_object_handlers;
+
+  INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Message",
+                   Message_methods);
+
+  message_ce = zend_register_internal_class(&tmp_ce);
+  message_ce->create_object = Message_create;
+
+  memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
+  h->dtor_obj = Message_dtor;
+  h->read_property = Message_read_property;
+  h->write_property = Message_write_property;
+  h->get_properties = Message_get_properties;
+  h->get_property_ptr_ptr = Message_get_property_ptr_ptr;
+}
diff --git a/php/ext/google/protobuf2/message.h b/php/ext/google/protobuf2/message.h
new file mode 100644
index 0000000..5083976
--- /dev/null
+++ b/php/ext/google/protobuf2/message.h
@@ -0,0 +1,59 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_MESSAGE_H_
+#define PHP_PROTOBUF_MESSAGE_H_
+
+#include <stdbool.h>
+
+#include "def.h"
+
+// Registers the PHP Message class.
+void Message_ModuleInit();
+
+// Gets a upb_msg* for the PHP object |val|, which must either be a Message
+// object or 'null'. Returns true and stores the message in |msg| if the
+// conversion succeeded (we can't return upb_msg* because null->NULL is a valid
+// conversion). Returns false and raises a PHP error if this isn't a Message
+// object or null, or if the Message object doesn't match this Descriptor.
+//
+// The given |arena| will be fused to this message's arena.
+bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena,
+                           upb_msg **msg);
+
+// Gets or creates a PHP Message object to wrap the given upb_msg* and |desc|
+// and returns it in |val|. The PHP object will keep a reference to this |arena|
+// to ensure the underlying message data stays alive.
+//
+// If |msg| is NULL, this will return a PHP null.
+void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
+                           zval *arena);
+
+#endif  // PHP_PROTOBUF_MESSAGE_H_
diff --git a/php/ext/google/protobuf2/names.c b/php/ext/google/protobuf2/names.c
new file mode 100644
index 0000000..ff04b3c
--- /dev/null
+++ b/php/ext/google/protobuf2/names.c
@@ -0,0 +1,226 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "names.h"
+
+#include <stdlib.h>
+
+#include "protobuf.h"
+
+/* stringsink *****************************************************************/
+
+typedef struct {
+  char *ptr;
+  size_t len, size;
+} stringsink;
+
+static size_t stringsink_string(stringsink *sink, const char *ptr, size_t len) {
+  size_t new_size = sink->size;
+
+  while (sink->len + len > new_size) {
+    new_size *= 2;
+  }
+
+  if (new_size != sink->size) {
+    sink->ptr = realloc(sink->ptr, new_size);
+    sink->size = new_size;
+  }
+
+  memcpy(sink->ptr + sink->len, ptr, len);
+  sink->len += len;
+
+  return len;
+}
+
+static void stringsink_init(stringsink *sink) {
+  sink->size = 32;
+  sink->ptr = malloc(sink->size);
+  PBPHP_ASSERT(sink->ptr != NULL);
+  sink->len = 0;
+}
+
+static void stringsink_uninit(stringsink *sink) { free(sink->ptr); }
+
+/* def name -> classname ******************************************************/
+
+const char *const kReservedNames[] = {
+    "abstract",   "and",        "array",        "as",           "break",
+    "callable",   "case",       "catch",        "class",        "clone",
+    "const",      "continue",   "declare",      "default",      "die",
+    "do",         "echo",       "else",         "elseif",       "empty",
+    "enddeclare", "endfor",     "endforeach",   "endif",        "endswitch",
+    "endwhile",   "eval",       "exit",         "extends",      "final",
+    "for",        "foreach",    "function",     "global",       "goto",
+    "if",         "implements", "include",      "include_once", "instanceof",
+    "insteadof",  "interface",  "isset",        "list",         "namespace",
+    "new",        "or",         "print",        "private",      "protected",
+    "public",     "require",    "require_once", "return",       "static",
+    "switch",     "throw",      "trait",        "try",          "unset",
+    "use",        "var",        "while",        "xor",          "int",
+    "float",      "bool",       "string",       "true",         "false",
+    "null",       "void",       "iterable",     NULL};
+
+bool is_reserved_name(const char* name) {
+  int i;
+  for (i = 0; kReservedNames[i]; i++) {
+    if (strcmp(kReservedNames[i], name) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static char nolocale_tolower(char ch) {
+  if (ch >= 'A' && ch <= 'Z') {
+    return ch - ('A' - 'a');
+  } else {
+    return ch;
+  }
+}
+
+static char nolocale_toupper(char ch) {
+  if (ch >= 'a' && ch <= 'z') {
+    return ch - ('a' - 'A');
+  } else {
+    return ch;
+  }
+}
+
+static bool is_reserved(const char *segment, int length) {
+  bool result;
+  char* lower = calloc(1, length + 1);
+  memcpy(lower, segment, length);
+  int i = 0;
+  while(lower[i]) {
+    lower[i] = nolocale_tolower(lower[i]);
+    i++;
+  }
+  lower[length] = 0;
+  result = is_reserved_name(lower);
+  free(lower);
+  return result;
+}
+
+static void fill_prefix(const char *segment, int length,
+                        const char *prefix_given,
+                        const char *package_name,
+                        stringsink *classname) {
+  if (prefix_given != NULL && strcmp(prefix_given, "") != 0) {
+    stringsink_string(classname, prefix_given, strlen(prefix_given));
+  } else {
+    if (is_reserved(segment, length)) {
+      if (package_name != NULL &&
+          strcmp("google.protobuf", package_name) == 0) {
+        stringsink_string(classname, "GPB", 3);
+      } else {
+        stringsink_string(classname, "PB", 2);
+      }
+    }
+  }
+}
+
+static void fill_segment(const char *segment, int length,
+                         stringsink *classname, bool use_camel) {
+  if (use_camel && (segment[0] < 'A' || segment[0] > 'Z')) {
+    char first = nolocale_toupper(segment[0]);
+    stringsink_string(classname, &first, 1);
+    stringsink_string(classname, segment + 1, length - 1);
+  } else {
+    stringsink_string(classname, segment, length);
+  }
+}
+
+static void fill_namespace(const char *package, const char *php_namespace,
+                           stringsink *classname) {
+  if (php_namespace != NULL) {
+    if (strlen(php_namespace) != 0) {
+      stringsink_string(classname, php_namespace, strlen(php_namespace));
+      stringsink_string(classname, "\\", 1);
+    }
+  } else if (package != NULL) {
+    int i = 0, j = 0;
+    size_t package_len = strlen(package);
+    while (i < package_len) {
+      j = i;
+      while (j < package_len && package[j] != '.') {
+        j++;
+      }
+      fill_prefix(package + i, j - i, "", package, classname);
+      fill_segment(package + i, j - i, classname, true);
+      stringsink_string(classname, "\\", 1);
+      i = j + 1;
+    }
+  }
+}
+
+static void fill_classname(const char *fullname,
+                           const char *package,
+                           const char *prefix,
+                           stringsink *classname) {
+  int classname_start = 0;
+  if (package != NULL) {
+    size_t package_len = strlen(package);
+    classname_start = package_len == 0 ? 0 : package_len + 1;
+  }
+  size_t fullname_len = strlen(fullname);
+
+  int i = classname_start, j;
+  while (i < fullname_len) {
+    j = i;
+    while (j < fullname_len && fullname[j] != '.') {
+      j++;
+    }
+    fill_prefix(fullname + i, j - i, prefix, package, classname);
+    fill_segment(fullname + i, j - i, classname, false);
+    if (j != fullname_len) {
+      stringsink_string(classname, "\\", 1);
+    }
+    i = j + 1;
+  }
+}
+
+char *GetPhpClassname(const upb_filedef *file, const char *fullname) {
+  // Prepend '.' to package name to make it absolute. In the 5 additional
+  // bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if
+  // given message is google.protobuf.Empty.
+  const char *package = upb_filedef_package(file);
+  const char *php_namespace = upb_filedef_phpnamespace(file);
+  const char *prefix = upb_filedef_phpprefix(file);
+  char *ret;
+  stringsink namesink;
+  stringsink_init(&namesink);
+
+  fill_namespace(package, php_namespace, &namesink);
+  fill_classname(fullname, package, prefix, &namesink);
+  stringsink_string(&namesink, "\0", 1);
+  ret = strdup(namesink.ptr);
+  stringsink_uninit(&namesink);
+  return ret;
+}
diff --git a/php/ext/google/protobuf2/names.h b/php/ext/google/protobuf2/names.h
new file mode 100644
index 0000000..75101c5
--- /dev/null
+++ b/php/ext/google/protobuf2/names.h
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_NAMES_H_
+#define PHP_PROTOBUF_NAMES_H_
+
+#include "php-upb.h"
+
+// Translates a protobuf symbol name (eg. foo.bar.Baz) into a PHP class name
+// (eg. \Foo\Bar\Baz).
+char *GetPhpClassname(const upb_filedef *file, const char *fullname);
+
+#endif  // PHP_PROTOBUF_NAMES_H_
diff --git a/php/ext/google/protobuf2/php-upb.c b/php/ext/google/protobuf2/php-upb.c
new file mode 100644
index 0000000..f5ea2ef
--- /dev/null
+++ b/php/ext/google/protobuf2/php-upb.c
@@ -0,0 +1,8077 @@
+/* Amalgamated source file */
+#include "php-upb.h"
+/*
+* This is where we define macros used across upb.
+*
+* All of these macros are undef'd in port_undef.inc to avoid leaking them to
+* users.
+*
+* The correct usage is:
+*
+*   #include "upb/foobar.h"
+*   #include "upb/baz.h"
+*
+*   // MUST be last included header.
+*   #include "upb/port_def.inc"
+*
+*   // Code for this file.
+*   // <...>
+*
+*   // Can be omitted for .c files, required for .h.
+*   #include "upb/port_undef.inc"
+*
+* This file is private and must not be included by users!
+*/
+#include <stdint.h>
+#include <stddef.h>
+
+#if UINTPTR_MAX == 0xffffffff
+#define UPB_SIZE(size32, size64) size32
+#else
+#define UPB_SIZE(size32, size64) size64
+#endif
+
+/* If we always read/write as a consistent type to each address, this shouldn't
+ * violate aliasing.
+ */
+#define UPB_PTR_AT(msg, ofs, type) ((type*)((char*)(msg) + (ofs)))
+
+#define UPB_READ_ONEOF(msg, fieldtype, offset, case_offset, case_val, default) \
+  *UPB_PTR_AT(msg, case_offset, int) == case_val                              \
+      ? *UPB_PTR_AT(msg, offset, fieldtype)                                   \
+      : default
+
+#define UPB_WRITE_ONEOF(msg, fieldtype, offset, value, case_offset, case_val) \
+  *UPB_PTR_AT(msg, case_offset, int) = case_val;                             \
+  *UPB_PTR_AT(msg, offset, fieldtype) = value;
+
+#define UPB_MAPTYPE_STRING 0
+
+/* UPB_INLINE: inline if possible, emit standalone code if required. */
+#ifdef __cplusplus
+#define UPB_INLINE inline
+#elif defined (__GNUC__) || defined(__clang__)
+#define UPB_INLINE static __inline__
+#else
+#define UPB_INLINE static
+#endif
+
+#define UPB_ALIGN_UP(size, align) (((size) + (align) - 1) / (align) * (align))
+#define UPB_ALIGN_DOWN(size, align) ((size) / (align) * (align))
+#define UPB_ALIGN_MALLOC(size) UPB_ALIGN_UP(size, 16)
+#define UPB_ALIGN_OF(type) offsetof (struct { char c; type member; }, member)
+
+/* Hints to the compiler about likely/unlikely branches. */
+#if defined (__GNUC__) || defined(__clang__)
+#define UPB_LIKELY(x) __builtin_expect((x),1)
+#define UPB_UNLIKELY(x) __builtin_expect((x),0)
+#else
+#define UPB_LIKELY(x) (x)
+#define UPB_UNLIKELY(x) (x)
+#endif
+
+/* Define UPB_BIG_ENDIAN manually if you're on big endian and your compiler
+ * doesn't provide these preprocessor symbols. */
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define UPB_BIG_ENDIAN
+#endif
+
+/* Macros for function attributes on compilers that support them. */
+#ifdef __GNUC__
+#define UPB_FORCEINLINE __inline__ __attribute__((always_inline))
+#define UPB_NOINLINE __attribute__((noinline))
+#define UPB_NORETURN __attribute__((__noreturn__))
+#else  /* !defined(__GNUC__) */
+#define UPB_FORCEINLINE
+#define UPB_NOINLINE
+#define UPB_NORETURN
+#endif
+
+#if __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L
+/* C99/C++11 versions. */
+#include <stdio.h>
+#define _upb_snprintf snprintf
+#define _upb_vsnprintf vsnprintf
+#define _upb_va_copy(a, b) va_copy(a, b)
+#elif defined(_MSC_VER)
+/* Microsoft C/C++ versions. */
+#include <stdarg.h>
+#include <stdio.h>
+#if _MSC_VER < 1900
+int msvc_snprintf(char* s, size_t n, const char* format, ...);
+int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
+#define UPB_MSVC_VSNPRINTF
+#define _upb_snprintf msvc_snprintf
+#define _upb_vsnprintf msvc_vsnprintf
+#else
+#define _upb_snprintf snprintf
+#define _upb_vsnprintf vsnprintf
+#endif
+#define _upb_va_copy(a, b) va_copy(a, b)
+#elif defined __GNUC__
+/* A few hacky workarounds for functions not in C89.
+ * For internal use only!
+ * TODO(haberman): fix these by including our own implementations, or finding
+ * another workaround.
+ */
+#define _upb_snprintf __builtin_snprintf
+#define _upb_vsnprintf __builtin_vsnprintf
+#define _upb_va_copy(a, b) __va_copy(a, b)
+#else
+#error Need implementations of [v]snprintf and va_copy
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \
+    (defined(_MSC_VER) && _MSC_VER >= 1900)
+/* C++11 is present */
+#else
+#error upb requires C++11 for C++ support
+#endif
+#endif
+
+#define UPB_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define UPB_MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#define UPB_UNUSED(var) (void)var
+
+/* UPB_ASSUME(): in release mode, we tell the compiler to assume this is true.
+ */
+#ifdef NDEBUG
+#ifdef __GNUC__
+#define UPB_ASSUME(expr) if (!(expr)) __builtin_unreachable()
+#elif defined _MSC_VER
+#define UPB_ASSUME(expr) if (!(expr)) __assume(0)
+#else
+#define UPB_ASSUME(expr) do {} if (false && (expr))
+#endif
+#else
+#define UPB_ASSUME(expr) assert(expr)
+#endif
+
+/* UPB_ASSERT(): in release mode, we use the expression without letting it be
+ * evaluated.  This prevents "unused variable" warnings. */
+#ifdef NDEBUG
+#define UPB_ASSERT(expr) do {} while (false && (expr))
+#else
+#define UPB_ASSERT(expr) assert(expr)
+#endif
+
+/* UPB_ASSERT_DEBUGVAR(): assert that uses functions or variables that only
+ * exist in debug mode.  This turns into regular assert. */
+#define UPB_ASSERT_DEBUGVAR(expr) assert(expr)
+
+#if defined(__GNUC__) || defined(__clang__)
+#define UPB_UNREACHABLE() do { assert(0); __builtin_unreachable(); } while(0)
+#else
+#define UPB_UNREACHABLE() do { assert(0); } while(0)
+#endif
+
+/* UPB_INFINITY representing floating-point positive infinity. */
+#include <math.h>
+#ifdef INFINITY
+#define UPB_INFINITY INFINITY
+#else
+#define UPB_INFINITY (1.0 / 0.0)
+#endif
+
+#include <setjmp.h>
+#include <string.h>
+
+
+
+/* Maps descriptor type -> upb field type.  */
+static const uint8_t desctype_to_fieldtype[] = {
+    -1,               /* invalid descriptor type */
+    UPB_TYPE_DOUBLE,  /* DOUBLE */
+    UPB_TYPE_FLOAT,   /* FLOAT */
+    UPB_TYPE_INT64,   /* INT64 */
+    UPB_TYPE_UINT64,  /* UINT64 */
+    UPB_TYPE_INT32,   /* INT32 */
+    UPB_TYPE_UINT64,  /* FIXED64 */
+    UPB_TYPE_UINT32,  /* FIXED32 */
+    UPB_TYPE_BOOL,    /* BOOL */
+    UPB_TYPE_STRING,  /* STRING */
+    UPB_TYPE_MESSAGE, /* GROUP */
+    UPB_TYPE_MESSAGE, /* MESSAGE */
+    UPB_TYPE_BYTES,   /* BYTES */
+    UPB_TYPE_UINT32,  /* UINT32 */
+    UPB_TYPE_ENUM,    /* ENUM */
+    UPB_TYPE_INT32,   /* SFIXED32 */
+    UPB_TYPE_INT64,   /* SFIXED64 */
+    UPB_TYPE_INT32,   /* SINT32 */
+    UPB_TYPE_INT64,   /* SINT64 */
+};
+
+/* Maps descriptor type -> upb map size.  */
+static const uint8_t desctype_to_mapsize[] = {
+    -1,                 /* invalid descriptor type */
+    8,                  /* DOUBLE */
+    4,                  /* FLOAT */
+    8,                  /* INT64 */
+    8,                  /* UINT64 */
+    4,                  /* INT32 */
+    8,                  /* FIXED64 */
+    4,                  /* FIXED32 */
+    1,                  /* BOOL */
+    UPB_MAPTYPE_STRING, /* STRING */
+    sizeof(void *),     /* GROUP */
+    sizeof(void *),     /* MESSAGE */
+    UPB_MAPTYPE_STRING, /* BYTES */
+    4,                  /* UINT32 */
+    4,                  /* ENUM */
+    4,                  /* SFIXED32 */
+    8,                  /* SFIXED64 */
+    4,                  /* SINT32 */
+    8,                  /* SINT64 */
+};
+
+static const unsigned fixed32_ok = (1 << UPB_DTYPE_FLOAT) |
+                                   (1 << UPB_DTYPE_FIXED32) |
+                                   (1 << UPB_DTYPE_SFIXED32);
+
+static const unsigned fixed64_ok = (1 << UPB_DTYPE_DOUBLE) |
+                                   (1 << UPB_DTYPE_FIXED64) |
+                                   (1 << UPB_DTYPE_SFIXED64);
+
+/* Op: an action to be performed for a wire-type/field-type combination. */
+#define OP_SCALAR_LG2(n) (n)
+#define OP_FIXPCK_LG2(n) (n + 4)
+#define OP_VARPCK_LG2(n) (n + 8)
+#define OP_STRING 4
+#define OP_SUBMSG 5
+
+static const int8_t varint_ops[19] = {
+    -1,               /* field not found */
+    -1,               /* DOUBLE */
+    -1,               /* FLOAT */
+    OP_SCALAR_LG2(3), /* INT64 */
+    OP_SCALAR_LG2(3), /* UINT64 */
+    OP_SCALAR_LG2(2), /* INT32 */
+    -1,               /* FIXED64 */
+    -1,               /* FIXED32 */
+    OP_SCALAR_LG2(0), /* BOOL */
+    -1,               /* STRING */
+    -1,               /* GROUP */
+    -1,               /* MESSAGE */
+    -1,               /* BYTES */
+    OP_SCALAR_LG2(2), /* UINT32 */
+    OP_SCALAR_LG2(2), /* ENUM */
+    -1,               /* SFIXED32 */
+    -1,               /* SFIXED64 */
+    OP_SCALAR_LG2(2), /* SINT32 */
+    OP_SCALAR_LG2(3), /* SINT64 */
+};
+
+static const int8_t delim_ops[37] = {
+    /* For non-repeated field type. */
+    -1,        /* field not found */
+    -1,        /* DOUBLE */
+    -1,        /* FLOAT */
+    -1,        /* INT64 */
+    -1,        /* UINT64 */
+    -1,        /* INT32 */
+    -1,        /* FIXED64 */
+    -1,        /* FIXED32 */
+    -1,        /* BOOL */
+    OP_STRING, /* STRING */
+    -1,        /* GROUP */
+    OP_SUBMSG, /* MESSAGE */
+    OP_STRING, /* BYTES */
+    -1,        /* UINT32 */
+    -1,        /* ENUM */
+    -1,        /* SFIXED32 */
+    -1,        /* SFIXED64 */
+    -1,        /* SINT32 */
+    -1,        /* SINT64 */
+    /* For repeated field type. */
+    OP_FIXPCK_LG2(3), /* REPEATED DOUBLE */
+    OP_FIXPCK_LG2(2), /* REPEATED FLOAT */
+    OP_VARPCK_LG2(3), /* REPEATED INT64 */
+    OP_VARPCK_LG2(3), /* REPEATED UINT64 */
+    OP_VARPCK_LG2(2), /* REPEATED INT32 */
+    OP_FIXPCK_LG2(3), /* REPEATED FIXED64 */
+    OP_FIXPCK_LG2(2), /* REPEATED FIXED32 */
+    OP_VARPCK_LG2(0), /* REPEATED BOOL */
+    OP_STRING,        /* REPEATED STRING */
+    OP_SUBMSG,        /* REPEATED GROUP */
+    OP_SUBMSG,        /* REPEATED MESSAGE */
+    OP_STRING,        /* REPEATED BYTES */
+    OP_VARPCK_LG2(2), /* REPEATED UINT32 */
+    OP_VARPCK_LG2(2), /* REPEATED ENUM */
+    OP_FIXPCK_LG2(2), /* REPEATED SFIXED32 */
+    OP_FIXPCK_LG2(3), /* REPEATED SFIXED64 */
+    OP_VARPCK_LG2(2), /* REPEATED SINT32 */
+    OP_VARPCK_LG2(3), /* REPEATED SINT64 */
+};
+
+/* Data pertaining to the parse. */
+typedef struct {
+  const char *limit;       /* End of delimited region or end of buffer. */
+  upb_arena *arena;
+  int depth;
+  uint32_t end_group; /* Set to field number of END_GROUP tag, if any. */
+  jmp_buf err;
+} upb_decstate;
+
+typedef union {
+  bool bool_val;
+  int32_t int32_val;
+  int64_t int64_val;
+  uint32_t uint32_val;
+  uint64_t uint64_val;
+  upb_strview str_val;
+} wireval;
+
+static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
+                              const upb_msglayout *layout);
+
+UPB_NORETURN static void decode_err(upb_decstate *d) { longjmp(d->err, 1); }
+
+static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) {
+  bool need_realloc = arr->size - arr->len < elem;
+  if (need_realloc && !_upb_array_realloc(arr, arr->len + elem, d->arena)) {
+    decode_err(d);
+  }
+  return need_realloc;
+}
+
+UPB_NOINLINE
+static const char *decode_longvarint64(upb_decstate *d, const char *ptr,
+                                       const char *limit, uint64_t *val) {
+  uint8_t byte;
+  int bitpos = 0;
+  uint64_t out = 0;
+
+  do {
+    if (bitpos >= 70 || ptr == limit) decode_err(d);
+    byte = *ptr;
+    out |= (uint64_t)(byte & 0x7F) << bitpos;
+    ptr++;
+    bitpos += 7;
+  } while (byte & 0x80);
+
+  *val = out;
+  return ptr;
+}
+
+UPB_FORCEINLINE
+static const char *decode_varint64(upb_decstate *d, const char *ptr,
+                                   const char *limit, uint64_t *val) {
+  if (UPB_LIKELY(ptr < limit && (*ptr & 0x80) == 0)) {
+    *val = (uint8_t)*ptr;
+    return ptr + 1;
+  } else {
+    return decode_longvarint64(d, ptr, limit, val);
+  }
+}
+
+static const char *decode_varint32(upb_decstate *d, const char *ptr,
+                                   const char *limit, uint32_t *val) {
+  uint64_t u64;
+  ptr = decode_varint64(d, ptr, limit, &u64);
+  if (u64 > UINT32_MAX) decode_err(d);
+  *val = (uint32_t)u64;
+  return ptr;
+}
+
+static void decode_munge(int type, wireval *val) {
+  switch (type) {
+    case UPB_DESCRIPTOR_TYPE_BOOL:
+      val->bool_val = val->uint64_val != 0;
+      break;
+    case UPB_DESCRIPTOR_TYPE_SINT32: {
+      uint32_t n = val->uint32_val;
+      val->int32_val = (n >> 1) ^ -(int32_t)(n & 1);
+      break;
+    }
+    case UPB_DESCRIPTOR_TYPE_SINT64: {
+      uint64_t n = val->uint64_val;
+      val->int64_val = (n >> 1) ^ -(int64_t)(n & 1);
+      break;
+    }
+  }
+}
+
+static const upb_msglayout_field *upb_find_field(const upb_msglayout *l,
+                                                 uint32_t field_number) {
+  static upb_msglayout_field none = {0};
+
+  /* Lots of optimization opportunities here. */
+  int i;
+  if (l == NULL) return &none;
+  for (i = 0; i < l->field_count; i++) {
+    if (l->fields[i].number == field_number) {
+      return &l->fields[i];
+    }
+  }
+
+  return &none; /* Unknown field. */
+}
+
+static upb_msg *decode_newsubmsg(upb_decstate *d, const upb_msglayout *layout,
+                                 const upb_msglayout_field *field) {
+  const upb_msglayout *subl = layout->submsgs[field->submsg_index];
+  return _upb_msg_new(subl, d->arena);
+}
+
+static void decode_tosubmsg(upb_decstate *d, upb_msg *submsg,
+                            const upb_msglayout *layout,
+                            const upb_msglayout_field *field, upb_strview val) {
+  const upb_msglayout *subl = layout->submsgs[field->submsg_index];
+  const char *saved_limit = d->limit;
+  if (--d->depth < 0) decode_err(d);
+  d->limit = val.data + val.size;
+  decode_msg(d, val.data, submsg, subl);
+  d->limit = saved_limit;
+  if (d->end_group != 0) decode_err(d);
+  d->depth++;
+}
+
+static const char *decode_group(upb_decstate *d, const char *ptr,
+                                upb_msg *submsg, const upb_msglayout *subl,
+                                uint32_t number) {
+  if (--d->depth < 0) decode_err(d);
+  ptr = decode_msg(d, ptr, submsg, subl);
+  if (d->end_group != number) decode_err(d);
+  d->end_group = 0;
+  d->depth++;
+  return ptr;
+}
+
+static const char *decode_togroup(upb_decstate *d, const char *ptr,
+                                  upb_msg *submsg, const upb_msglayout *layout,
+                                  const upb_msglayout_field *field) {
+  const upb_msglayout *subl = layout->submsgs[field->submsg_index];
+  return decode_group(d, ptr, submsg, subl, field->number);
+}
+
+static const char *decode_toarray(upb_decstate *d, const char *ptr,
+                                  upb_msg *msg, const upb_msglayout *layout,
+                                  const upb_msglayout_field *field, wireval val,
+                                  int op) {
+  upb_array **arrp = UPB_PTR_AT(msg, field->offset, void);
+  upb_array *arr = *arrp;
+  void *mem;
+
+  if (!arr) {
+    upb_fieldtype_t type = desctype_to_fieldtype[field->descriptortype];
+    arr = _upb_array_new(d->arena, type);
+    if (!arr) decode_err(d);
+    *arrp = arr;
+  }
+
+  decode_reserve(d, arr, 1);
+
+  switch (op) {
+    case OP_SCALAR_LG2(0):
+    case OP_SCALAR_LG2(2):
+    case OP_SCALAR_LG2(3):
+      /* Append scalar value. */
+      mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << op, void);
+      arr->len++;
+      memcpy(mem, &val, 1 << op);
+      return ptr;
+    case OP_STRING:
+      /* Append string. */
+      mem =
+          UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(upb_strview), void);
+      arr->len++;
+      memcpy(mem, &val, sizeof(upb_strview));
+      return ptr;
+    case OP_SUBMSG: {
+      /* Append submessage / group. */
+      upb_msg *submsg = decode_newsubmsg(d, layout, field);
+      *UPB_PTR_AT(_upb_array_ptr(arr), arr->len * sizeof(void *), upb_msg *) =
+          submsg;
+      arr->len++;
+      if (UPB_UNLIKELY(field->descriptortype == UPB_DTYPE_GROUP)) {
+        ptr = decode_togroup(d, ptr, submsg, layout, field);
+      } else {
+        decode_tosubmsg(d, submsg, layout, field, val.str_val);
+      }
+      return ptr;
+    }
+    case OP_FIXPCK_LG2(2):
+    case OP_FIXPCK_LG2(3): {
+      /* Fixed packed. */
+      int lg2 = op - OP_FIXPCK_LG2(0);
+      int mask = (1 << lg2) - 1;
+      size_t count = val.str_val.size >> lg2;
+      if ((val.str_val.size & mask) != 0) {
+        decode_err(d); /* Length isn't a round multiple of elem size. */
+      }
+      decode_reserve(d, arr, count);
+      mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
+      arr->len += count;
+      memcpy(mem, val.str_val.data, val.str_val.size);
+      return ptr;
+    }
+    case OP_VARPCK_LG2(0):
+    case OP_VARPCK_LG2(2):
+    case OP_VARPCK_LG2(3): {
+      /* Varint packed. */
+      int lg2 = op - OP_VARPCK_LG2(0);
+      int scale = 1 << lg2;
+      const char *ptr = val.str_val.data;
+      const char *end = ptr + val.str_val.size;
+      char *out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
+      while (ptr < end) {
+        wireval elem;
+        ptr = decode_varint64(d, ptr, end, &elem.uint64_val);
+        decode_munge(field->descriptortype, &elem);
+        if (decode_reserve(d, arr, 1)) {
+          out = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
+        }
+        arr->len++;
+        memcpy(out, &elem, scale);
+        out += scale;
+      }
+      if (ptr != end) decode_err(d);
+      return ptr;
+    }
+    default:
+      UPB_UNREACHABLE();
+  }
+}
+
+static void decode_tomap(upb_decstate *d, upb_msg *msg,
+                         const upb_msglayout *layout,
+                         const upb_msglayout_field *field, wireval val) {
+  upb_map **map_p = UPB_PTR_AT(msg, field->offset, upb_map *);
+  upb_map *map = *map_p;
+  upb_map_entry ent;
+  const upb_msglayout *entry = layout->submsgs[field->submsg_index];
+
+  if (!map) {
+    /* Lazily create map. */
+    const upb_msglayout *entry = layout->submsgs[field->submsg_index];
+    const upb_msglayout_field *key_field = &entry->fields[0];
+    const upb_msglayout_field *val_field = &entry->fields[1];
+    char key_size = desctype_to_mapsize[key_field->descriptortype];
+    char val_size = desctype_to_mapsize[val_field->descriptortype];
+    UPB_ASSERT(key_field->offset == 0);
+    UPB_ASSERT(val_field->offset == sizeof(upb_strview));
+    map = _upb_map_new(d->arena, key_size, val_size);
+    *map_p = map;
+  }
+
+  /* Parse map entry. */
+  memset(&ent, 0, sizeof(ent));
+
+  if (entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
+      entry->fields[1].descriptortype == UPB_DESCRIPTOR_TYPE_GROUP) {
+    /* Create proactively to handle the case where it doesn't appear. */
+    ent.v.val.val = (uint64_t)_upb_msg_new(entry->submsgs[0], d->arena);
+  }
+
+  decode_tosubmsg(d, &ent.k, layout, field, val.str_val);
+
+  /* Insert into map. */
+  _upb_map_set(map, &ent.k, map->key_size, &ent.v, map->val_size, d->arena);
+}
+
+static const char *decode_tomsg(upb_decstate *d, const char *ptr, upb_msg *msg,
+                                const upb_msglayout *layout,
+                                const upb_msglayout_field *field, wireval val,
+                                int op) {
+  void *mem = UPB_PTR_AT(msg, field->offset, void);
+  int type = field->descriptortype;
+
+  /* Set presence if necessary. */
+  if (field->presence < 0) {
+    /* Oneof case */
+    uint32_t *oneof_case = _upb_oneofcase_field(msg, field);
+    if (op == OP_SUBMSG && *oneof_case != field->number) {
+      memset(mem, 0, sizeof(void*));
+    }
+    *oneof_case = field->number;
+  } else if (field->presence > 0) {
+    _upb_sethas_field(msg, field);
+  }
+
+  /* Store into message. */
+  switch (op) {
+    case OP_SUBMSG: {
+      upb_msg **submsgp = mem;
+      upb_msg *submsg = *submsgp;
+      if (!submsg) {
+        submsg = decode_newsubmsg(d, layout, field);
+        *submsgp = submsg;
+      }
+      if (UPB_UNLIKELY(type == UPB_DTYPE_GROUP)) {
+        ptr = decode_togroup(d, ptr, submsg, layout, field);
+      } else {
+        decode_tosubmsg(d, submsg, layout, field, val.str_val);
+      }
+      break;
+    }
+    case OP_STRING:
+      memcpy(mem, &val, sizeof(upb_strview));
+      break;
+    case OP_SCALAR_LG2(3):
+      memcpy(mem, &val, 8);
+      break;
+    case OP_SCALAR_LG2(2):
+      memcpy(mem, &val, 4);
+      break;
+    case OP_SCALAR_LG2(0):
+      memcpy(mem, &val, 1);
+      break;
+    default:
+      UPB_UNREACHABLE();
+  }
+
+  return ptr;
+}
+
+static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
+                              const upb_msglayout *layout) {
+  while (ptr < d->limit) {
+    uint32_t tag;
+    const upb_msglayout_field *field;
+    int field_number;
+    int wire_type;
+    const char *field_start = ptr;
+    wireval val;
+    int op;
+
+    ptr = decode_varint32(d, ptr, d->limit, &tag);
+    field_number = tag >> 3;
+    wire_type = tag & 7;
+
+    field = upb_find_field(layout, field_number);
+
+    switch (wire_type) {
+      case UPB_WIRE_TYPE_VARINT:
+        ptr = decode_varint64(d, ptr, d->limit, &val.uint64_val);
+        op = varint_ops[field->descriptortype];
+        decode_munge(field->descriptortype, &val);
+        break;
+      case UPB_WIRE_TYPE_32BIT:
+        if (d->limit - ptr < 4) decode_err(d);
+        memcpy(&val, ptr, 4);
+        ptr += 4;
+        op = OP_SCALAR_LG2(2);
+        if (((1 << field->descriptortype) & fixed32_ok) == 0) goto unknown;
+        break;
+      case UPB_WIRE_TYPE_64BIT:
+        if (d->limit - ptr < 8) decode_err(d);
+        memcpy(&val, ptr, 8);
+        ptr += 8;
+        op = OP_SCALAR_LG2(3);
+        if (((1 << field->descriptortype) & fixed64_ok) == 0) goto unknown;
+        break;
+      case UPB_WIRE_TYPE_DELIMITED: {
+        uint32_t size;
+        int ndx = field->descriptortype;
+        if (_upb_isrepeated(field)) ndx += 18;
+        ptr = decode_varint32(d, ptr, d->limit, &size);
+        if (size >= INT32_MAX || (size_t)(d->limit - ptr) < size) {
+          decode_err(d); /* Length overflow. */
+        }
+        val.str_val.data = ptr;
+        val.str_val.size = size;
+        ptr += size;
+        op = delim_ops[ndx];
+        break;
+      }
+      case UPB_WIRE_TYPE_START_GROUP:
+        val.int32_val = field_number;
+        op = OP_SUBMSG;
+        if (field->descriptortype != UPB_DTYPE_GROUP) goto unknown;
+        break;
+      case UPB_WIRE_TYPE_END_GROUP:
+        d->end_group = field_number;
+        return ptr;
+      default:
+        decode_err(d);
+    }
+
+    if (op >= 0) {
+      /* Parse, using op for dispatch. */
+      switch (field->label) {
+        case UPB_LABEL_REPEATED:
+        case _UPB_LABEL_PACKED:
+          ptr = decode_toarray(d, ptr, msg, layout, field, val, op);
+          break;
+        case _UPB_LABEL_MAP:
+          decode_tomap(d, msg, layout, field, val);
+          break;
+        default:
+          ptr = decode_tomsg(d, ptr, msg, layout, field, val, op);
+          break;
+      }
+    } else {
+    unknown:
+      /* Skip unknown field. */
+      if (field_number == 0) decode_err(d);
+      if (wire_type == UPB_WIRE_TYPE_START_GROUP) {
+        ptr = decode_group(d, ptr, NULL, NULL, field_number);
+      }
+      if (msg) {
+        if (!_upb_msg_addunknown(msg, field_start, ptr - field_start,
+                                 d->arena)) {
+          decode_err(d);
+        }
+      }
+    }
+  }
+
+  if (ptr != d->limit) decode_err(d);
+  return ptr;
+}
+
+bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l,
+                upb_arena *arena) {
+  upb_decstate state;
+  state.limit = buf + size;
+  state.arena = arena;
+  state.depth = 64;
+  state.end_group = 0;
+
+  if (setjmp(state.err)) return false;
+
+  if (size == 0) return true;
+  decode_msg(&state, buf, msg, l);
+
+  return state.end_group == 0;
+}
+
+#undef OP_SCALAR_LG2
+#undef OP_FIXPCK_LG2
+#undef OP_VARPCK_LG2
+#undef OP_STRING
+#undef OP_SUBMSG
+/* We encode backwards, to avoid pre-computing lengths (one-pass encode). */
+
+
+#include <string.h>
+
+
+
+#define UPB_PB_VARINT_MAX_LEN 10
+#define CHK(x) do { if (!(x)) { return false; } } while(0)
+
+static size_t upb_encode_varint(uint64_t val, char *buf) {
+  size_t i;
+  if (val < 128) { buf[0] = val; return 1; }
+  i = 0;
+  while (val) {
+    uint8_t byte = val & 0x7fU;
+    val >>= 7;
+    if (val) byte |= 0x80U;
+    buf[i++] = byte;
+  }
+  return i;
+}
+
+static uint32_t upb_zzencode_32(int32_t n) { return ((uint32_t)n << 1) ^ (n >> 31); }
+static uint64_t upb_zzencode_64(int64_t n) { return ((uint64_t)n << 1) ^ (n >> 63); }
+
+typedef struct {
+  upb_alloc *alloc;
+  char *buf, *ptr, *limit;
+} upb_encstate;
+
+static size_t upb_roundup_pow2(size_t bytes) {
+  size_t ret = 128;
+  while (ret < bytes) {
+    ret *= 2;
+  }
+  return ret;
+}
+
+static bool upb_encode_growbuffer(upb_encstate *e, size_t bytes) {
+  size_t old_size = e->limit - e->buf;
+  size_t new_size = upb_roundup_pow2(bytes + (e->limit - e->ptr));
+  char *new_buf = upb_realloc(e->alloc, e->buf, old_size, new_size);
+  CHK(new_buf);
+
+  /* We want previous data at the end, realloc() put it at the beginning. */
+  if (old_size > 0) {
+    memmove(new_buf + new_size - old_size, e->buf, old_size);
+  }
+
+  e->ptr = new_buf + new_size - (e->limit - e->ptr);
+  e->limit = new_buf + new_size;
+  e->buf = new_buf;
+  return true;
+}
+
+/* Call to ensure that at least "bytes" bytes are available for writing at
+ * e->ptr.  Returns false if the bytes could not be allocated. */
+static bool upb_encode_reserve(upb_encstate *e, size_t bytes) {
+  CHK(UPB_LIKELY((size_t)(e->ptr - e->buf) >= bytes) ||
+      upb_encode_growbuffer(e, bytes));
+
+  e->ptr -= bytes;
+  return true;
+}
+
+/* Writes the given bytes to the buffer, handling reserve/advance. */
+static bool upb_put_bytes(upb_encstate *e, const void *data, size_t len) {
+  if (len == 0) return true;
+  CHK(upb_encode_reserve(e, len));
+  memcpy(e->ptr, data, len);
+  return true;
+}
+
+static bool upb_put_fixed64(upb_encstate *e, uint64_t val) {
+  /* TODO(haberman): byte-swap for big endian. */
+  return upb_put_bytes(e, &val, sizeof(uint64_t));
+}
+
+static bool upb_put_fixed32(upb_encstate *e, uint32_t val) {
+  /* TODO(haberman): byte-swap for big endian. */
+  return upb_put_bytes(e, &val, sizeof(uint32_t));
+}
+
+static bool upb_put_varint(upb_encstate *e, uint64_t val) {
+  size_t len;
+  char *start;
+  CHK(upb_encode_reserve(e, UPB_PB_VARINT_MAX_LEN));
+  len = upb_encode_varint(val, e->ptr);
+  start = e->ptr + UPB_PB_VARINT_MAX_LEN - len;
+  memmove(start, e->ptr, len);
+  e->ptr = start;
+  return true;
+}
+
+static bool upb_put_double(upb_encstate *e, double d) {
+  uint64_t u64;
+  UPB_ASSERT(sizeof(double) == sizeof(uint64_t));
+  memcpy(&u64, &d, sizeof(uint64_t));
+  return upb_put_fixed64(e, u64);
+}
+
+static bool upb_put_float(upb_encstate *e, float d) {
+  uint32_t u32;
+  UPB_ASSERT(sizeof(float) == sizeof(uint32_t));
+  memcpy(&u32, &d, sizeof(uint32_t));
+  return upb_put_fixed32(e, u32);
+}
+
+static bool upb_put_tag(upb_encstate *e, int field_number, int wire_type) {
+  return upb_put_varint(e, (field_number << 3) | wire_type);
+}
+
+static bool upb_put_fixedarray(upb_encstate *e, const upb_array *arr,
+                               size_t elem_size, uint32_t tag) {
+  size_t bytes = arr->len * elem_size;
+  const char* data = _upb_array_constptr(arr);
+  const char* ptr = data + bytes - elem_size;
+  if (tag) {
+    while (true) {
+      CHK(upb_put_bytes(e, ptr, elem_size) && upb_put_varint(e, tag));
+      if (ptr == data) break;
+      ptr -= elem_size;
+    }
+    return true;
+  } else {
+    return upb_put_bytes(e, data, bytes) && upb_put_varint(e, bytes);
+  }
+}
+
+bool upb_encode_message(upb_encstate *e, const char *msg,
+                        const upb_msglayout *m, size_t *size);
+
+static bool upb_encode_scalarfield(upb_encstate *e, const void *_field_mem,
+                                   const upb_msglayout *m,
+                                   const upb_msglayout_field *f,
+                                   bool skip_zero_value) {
+  const char *field_mem = _field_mem;
+#define CASE(ctype, type, wire_type, encodeval) do { \
+  ctype val = *(ctype*)field_mem; \
+  if (skip_zero_value && val == 0) { \
+    return true; \
+  } \
+  return upb_put_ ## type(e, encodeval) && \
+      upb_put_tag(e, f->number, wire_type); \
+} while(0)
+
+  switch (f->descriptortype) {
+    case UPB_DESCRIPTOR_TYPE_DOUBLE:
+      CASE(double, double, UPB_WIRE_TYPE_64BIT, val);
+    case UPB_DESCRIPTOR_TYPE_FLOAT:
+      CASE(float, float, UPB_WIRE_TYPE_32BIT, val);
+    case UPB_DESCRIPTOR_TYPE_INT64:
+    case UPB_DESCRIPTOR_TYPE_UINT64:
+      CASE(uint64_t, varint, UPB_WIRE_TYPE_VARINT, val);
+    case UPB_DESCRIPTOR_TYPE_UINT32:
+      CASE(uint32_t, varint, UPB_WIRE_TYPE_VARINT, val);
+    case UPB_DESCRIPTOR_TYPE_INT32:
+    case UPB_DESCRIPTOR_TYPE_ENUM:
+      CASE(int32_t, varint, UPB_WIRE_TYPE_VARINT, (int64_t)val);
+    case UPB_DESCRIPTOR_TYPE_SFIXED64:
+    case UPB_DESCRIPTOR_TYPE_FIXED64:
+      CASE(uint64_t, fixed64, UPB_WIRE_TYPE_64BIT, val);
+    case UPB_DESCRIPTOR_TYPE_FIXED32:
+    case UPB_DESCRIPTOR_TYPE_SFIXED32:
+      CASE(uint32_t, fixed32, UPB_WIRE_TYPE_32BIT, val);
+    case UPB_DESCRIPTOR_TYPE_BOOL:
+      CASE(bool, varint, UPB_WIRE_TYPE_VARINT, val);
+    case UPB_DESCRIPTOR_TYPE_SINT32:
+      CASE(int32_t, varint, UPB_WIRE_TYPE_VARINT, upb_zzencode_32(val));
+    case UPB_DESCRIPTOR_TYPE_SINT64:
+      CASE(int64_t, varint, UPB_WIRE_TYPE_VARINT, upb_zzencode_64(val));
+    case UPB_DESCRIPTOR_TYPE_STRING:
+    case UPB_DESCRIPTOR_TYPE_BYTES: {
+      upb_strview view = *(upb_strview*)field_mem;
+      if (skip_zero_value && view.size == 0) {
+        return true;
+      }
+      return upb_put_bytes(e, view.data, view.size) &&
+          upb_put_varint(e, view.size) &&
+          upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED);
+    }
+    case UPB_DESCRIPTOR_TYPE_GROUP: {
+      size_t size;
+      void *submsg = *(void **)field_mem;
+      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      if (submsg == NULL) {
+        return true;
+      }
+      return upb_put_tag(e, f->number, UPB_WIRE_TYPE_END_GROUP) &&
+          upb_encode_message(e, submsg, subm, &size) &&
+          upb_put_tag(e, f->number, UPB_WIRE_TYPE_START_GROUP);
+    }
+    case UPB_DESCRIPTOR_TYPE_MESSAGE: {
+      size_t size;
+      void *submsg = *(void **)field_mem;
+      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      if (submsg == NULL) {
+        return true;
+      }
+      return upb_encode_message(e, submsg, subm, &size) &&
+          upb_put_varint(e, size) &&
+          upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED);
+    }
+  }
+#undef CASE
+  UPB_UNREACHABLE();
+}
+
+static bool upb_encode_array(upb_encstate *e, const char *field_mem,
+                             const upb_msglayout *m,
+                             const upb_msglayout_field *f) {
+  const upb_array *arr = *(const upb_array**)field_mem;
+  bool packed = f->label == _UPB_LABEL_PACKED;
+
+  if (arr == NULL || arr->len == 0) {
+    return true;
+  }
+
+#define VARINT_CASE(ctype, encode)                                       \
+  {                                                                      \
+    const ctype *start = _upb_array_constptr(arr);                       \
+    const ctype *ptr = start + arr->len;                                 \
+    size_t pre_len = e->limit - e->ptr;                                  \
+    uint32_t tag = packed ? 0 : (f->number << 3) | UPB_WIRE_TYPE_VARINT; \
+    do {                                                                 \
+      ptr--;                                                             \
+      CHK(upb_put_varint(e, encode));                                    \
+      if (tag) CHK(upb_put_varint(e, tag));                              \
+    } while (ptr != start);                                              \
+    if (!tag) CHK(upb_put_varint(e, e->limit - e->ptr - pre_len));       \
+  }                                                                      \
+  break;                                                                 \
+  do {                                                                   \
+    ;                                                                    \
+  } while (0)
+
+#define TAG(wire_type) (packed ? 0 : (f->number << 3 | wire_type))
+
+  switch (f->descriptortype) {
+    case UPB_DESCRIPTOR_TYPE_DOUBLE:
+      CHK(upb_put_fixedarray(e, arr, sizeof(double), TAG(UPB_WIRE_TYPE_64BIT)));
+      break;
+    case UPB_DESCRIPTOR_TYPE_FLOAT:
+      CHK(upb_put_fixedarray(e, arr, sizeof(float), TAG(UPB_WIRE_TYPE_32BIT)));
+      break;
+    case UPB_DESCRIPTOR_TYPE_SFIXED64:
+    case UPB_DESCRIPTOR_TYPE_FIXED64:
+      CHK(upb_put_fixedarray(e, arr, sizeof(uint64_t), TAG(UPB_WIRE_TYPE_64BIT)));
+      break;
+    case UPB_DESCRIPTOR_TYPE_FIXED32:
+    case UPB_DESCRIPTOR_TYPE_SFIXED32:
+      CHK(upb_put_fixedarray(e, arr, sizeof(uint32_t), TAG(UPB_WIRE_TYPE_32BIT)));
+      break;
+    case UPB_DESCRIPTOR_TYPE_INT64:
+    case UPB_DESCRIPTOR_TYPE_UINT64:
+      VARINT_CASE(uint64_t, *ptr);
+    case UPB_DESCRIPTOR_TYPE_UINT32:
+      VARINT_CASE(uint32_t, *ptr);
+    case UPB_DESCRIPTOR_TYPE_INT32:
+    case UPB_DESCRIPTOR_TYPE_ENUM:
+      VARINT_CASE(int32_t, (int64_t)*ptr);
+    case UPB_DESCRIPTOR_TYPE_BOOL:
+      VARINT_CASE(bool, *ptr);
+    case UPB_DESCRIPTOR_TYPE_SINT32:
+      VARINT_CASE(int32_t, upb_zzencode_32(*ptr));
+    case UPB_DESCRIPTOR_TYPE_SINT64:
+      VARINT_CASE(int64_t, upb_zzencode_64(*ptr));
+    case UPB_DESCRIPTOR_TYPE_STRING:
+    case UPB_DESCRIPTOR_TYPE_BYTES: {
+      const upb_strview *start = _upb_array_constptr(arr);
+      const upb_strview *ptr = start + arr->len;
+      do {
+        ptr--;
+        CHK(upb_put_bytes(e, ptr->data, ptr->size) &&
+            upb_put_varint(e, ptr->size) &&
+            upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
+      } while (ptr != start);
+      return true;
+    }
+    case UPB_DESCRIPTOR_TYPE_GROUP: {
+      const void *const*start = _upb_array_constptr(arr);
+      const void *const*ptr = start + arr->len;
+      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      do {
+        size_t size;
+        ptr--;
+        CHK(upb_put_tag(e, f->number, UPB_WIRE_TYPE_END_GROUP) &&
+            upb_encode_message(e, *ptr, subm, &size) &&
+            upb_put_tag(e, f->number, UPB_WIRE_TYPE_START_GROUP));
+      } while (ptr != start);
+      return true;
+    }
+    case UPB_DESCRIPTOR_TYPE_MESSAGE: {
+      const void *const*start = _upb_array_constptr(arr);
+      const void *const*ptr = start + arr->len;
+      const upb_msglayout *subm = m->submsgs[f->submsg_index];
+      do {
+        size_t size;
+        ptr--;
+        CHK(upb_encode_message(e, *ptr, subm, &size) &&
+            upb_put_varint(e, size) &&
+            upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
+      } while (ptr != start);
+      return true;
+    }
+  }
+#undef VARINT_CASE
+
+  if (packed) {
+    CHK(upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
+  }
+  return true;
+}
+
+static bool upb_encode_map(upb_encstate *e, const char *field_mem,
+                           const upb_msglayout *m,
+                           const upb_msglayout_field *f) {
+  const upb_map *map = *(const upb_map**)field_mem;
+  const upb_msglayout *entry = m->submsgs[f->submsg_index];
+  const upb_msglayout_field *key_field = &entry->fields[0];
+  const upb_msglayout_field *val_field = &entry->fields[1];
+  upb_strtable_iter i;
+  if (map == NULL) {
+    return true;
+  }
+
+  upb_strtable_begin(&i, &map->table);
+  for(; !upb_strtable_done(&i); upb_strtable_next(&i)) {
+    size_t pre_len = e->limit - e->ptr;
+    size_t size;
+    upb_strview key = upb_strtable_iter_key(&i);
+    const upb_value val = upb_strtable_iter_value(&i);
+    upb_map_entry ent;
+    _upb_map_fromkey(key, &ent.k, map->key_size);
+    _upb_map_fromvalue(val, &ent.v, map->val_size);
+    CHK(upb_encode_scalarfield(e, &ent.v, entry, val_field, false));
+    CHK(upb_encode_scalarfield(e, &ent.k, entry, key_field, false));
+    size = (e->limit - e->ptr) - pre_len;
+    CHK(upb_put_varint(e, size));
+    CHK(upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
+  }
+
+  return true;
+}
+
+
+bool upb_encode_message(upb_encstate *e, const char *msg,
+                        const upb_msglayout *m, size_t *size) {
+  int i;
+  size_t pre_len = e->limit - e->ptr;
+  const char *unknown;
+  size_t unknown_size;
+
+  unknown = upb_msg_getunknown(msg, &unknown_size);
+
+  if (unknown) {
+    upb_put_bytes(e, unknown, unknown_size);
+  }
+
+  for (i = m->field_count - 1; i >= 0; i--) {
+    const upb_msglayout_field *f = &m->fields[i];
+
+    if (_upb_isrepeated(f)) {
+      CHK(upb_encode_array(e, msg + f->offset, m, f));
+    } else if (f->label == _UPB_LABEL_MAP) {
+      CHK(upb_encode_map(e, msg + f->offset, m, f));
+    } else {
+      bool skip_empty = false;
+      if (f->presence == 0) {
+        /* Proto3 presence. */
+        skip_empty = true;
+      } else if (f->presence > 0) {
+        /* Proto2 presence: hasbit. */
+        if (!_upb_hasbit_field(msg, f)) {
+          continue;
+        }
+      } else {
+        /* Field is in a oneof. */
+        if (_upb_getoneofcase_field(msg, f) != f->number) {
+          continue;
+        }
+      }
+      CHK(upb_encode_scalarfield(e, msg + f->offset, m, f, skip_empty));
+    }
+  }
+
+  *size = (e->limit - e->ptr) - pre_len;
+  return true;
+}
+
+char *upb_encode(const void *msg, const upb_msglayout *m, upb_arena *arena,
+                 size_t *size) {
+  upb_encstate e;
+  e.alloc = upb_arena_alloc(arena);
+  e.buf = NULL;
+  e.limit = NULL;
+  e.ptr = NULL;
+
+  if (!upb_encode_message(&e, msg, m, size)) {
+    *size = 0;
+    return NULL;
+  }
+
+  *size = e.limit - e.ptr;
+
+  if (*size == 0) {
+    static char ch;
+    return &ch;
+  } else {
+    UPB_ASSERT(e.ptr);
+    return e.ptr;
+  }
+}
+
+#undef CHK
+
+
+
+
+/** upb_msg *******************************************************************/
+
+static const char _upb_fieldtype_to_sizelg2[12] = {
+  0,
+  0,  /* UPB_TYPE_BOOL */
+  2,  /* UPB_TYPE_FLOAT */
+  2,  /* UPB_TYPE_INT32 */
+  2,  /* UPB_TYPE_UINT32 */
+  2,  /* UPB_TYPE_ENUM */
+  UPB_SIZE(2, 3),  /* UPB_TYPE_MESSAGE */
+  3,  /* UPB_TYPE_DOUBLE */
+  3,  /* UPB_TYPE_INT64 */
+  3,  /* UPB_TYPE_UINT64 */
+  UPB_SIZE(3, 4),  /* UPB_TYPE_STRING */
+  UPB_SIZE(3, 4),  /* UPB_TYPE_BYTES */
+};
+
+static uintptr_t tag_arrptr(void* ptr, int elem_size_lg2) {
+  UPB_ASSERT(elem_size_lg2 <= 4);
+  return (uintptr_t)ptr | elem_size_lg2;
+}
+
+static int upb_msg_internalsize(const upb_msglayout *l) {
+  return sizeof(upb_msg_internal) - l->extendable * sizeof(void *);
+}
+
+static size_t upb_msg_sizeof(const upb_msglayout *l) {
+  return l->size + upb_msg_internalsize(l);
+}
+
+static const upb_msg_internal *upb_msg_getinternal_const(const upb_msg *msg) {
+  ptrdiff_t size = sizeof(upb_msg_internal);
+  return UPB_PTR_AT(msg, -size, upb_msg_internal);
+}
+
+static upb_msg_internal *upb_msg_getinternal(upb_msg *msg) {
+  return (upb_msg_internal*)upb_msg_getinternal_const(msg);
+}
+
+void _upb_msg_clear(upb_msg *msg, const upb_msglayout *l) {
+  ptrdiff_t internal = upb_msg_internalsize(l);
+  void *mem = UPB_PTR_AT(msg, -internal, char);
+  memset(mem, 0, l->size + internal);
+}
+
+upb_msg *_upb_msg_new(const upb_msglayout *l, upb_arena *a) {
+  void *mem = upb_arena_malloc(a, upb_msg_sizeof(l));
+  upb_msg *msg;
+
+  if (!mem) {
+    return NULL;
+  }
+
+  msg = UPB_PTR_AT(mem, upb_msg_internalsize(l), upb_msg);
+  _upb_msg_clear(msg, l);
+  return msg;
+}
+
+bool _upb_msg_addunknown(upb_msg *msg, const char *data, size_t len,
+                         upb_arena *arena) {
+  upb_msg_internal *in = upb_msg_getinternal(msg);
+  if (len > in->unknown_size - in->unknown_len) {
+    upb_alloc *alloc = upb_arena_alloc(arena);
+    size_t need = in->unknown_size + len;
+    size_t newsize = UPB_MAX(in->unknown_size * 2, need);
+    void *mem = upb_realloc(alloc, in->unknown, in->unknown_size, newsize);
+    if (!mem) return false;
+    in->unknown = mem;
+    in->unknown_size = newsize;
+  }
+  memcpy(in->unknown + in->unknown_len, data, len);
+  in->unknown_len += len;
+  return true;
+}
+
+void _upb_msg_discardunknown_shallow(upb_msg *msg) {
+  upb_msg_internal *in = upb_msg_getinternal(msg);
+  in->unknown_len = 0;
+}
+
+const char *upb_msg_getunknown(const upb_msg *msg, size_t *len) {
+  const upb_msg_internal *in = upb_msg_getinternal_const(msg);
+  *len = in->unknown_len;
+  return in->unknown;
+}
+
+/** upb_array *****************************************************************/
+
+upb_array *_upb_array_new(upb_arena *a, upb_fieldtype_t type) {
+  upb_array *arr = upb_arena_malloc(a, sizeof(upb_array));
+
+  if (!arr) {
+    return NULL;
+  }
+
+  arr->data = tag_arrptr(NULL, _upb_fieldtype_to_sizelg2[type]);
+  arr->len = 0;
+  arr->size = 0;
+
+  return arr;
+}
+
+bool _upb_array_realloc(upb_array *arr, size_t min_size, upb_arena *arena) {
+  size_t new_size = UPB_MAX(arr->size, 4);
+  int elem_size_lg2 = arr->data & 7;
+  size_t old_bytes = arr->size << elem_size_lg2;
+  size_t new_bytes;
+  void* ptr = _upb_array_ptr(arr);
+
+  /* Log2 ceiling of size. */
+  while (new_size < min_size) new_size *= 2;
+
+  new_bytes = new_size << elem_size_lg2;
+  ptr = upb_arena_realloc(arena, ptr, old_bytes, new_bytes);
+
+  if (!ptr) {
+    return false;
+  }
+
+  arr->data = tag_arrptr(ptr, elem_size_lg2);
+  arr->size = new_size;
+  return true;
+}
+
+static upb_array *getorcreate_array(upb_array **arr_ptr, upb_fieldtype_t type,
+                                    upb_arena *arena) {
+  upb_array *arr = *arr_ptr;
+  if (!arr) {
+    arr = _upb_array_new(arena, type);
+    if (!arr) return NULL;
+    *arr_ptr = arr;
+  }
+  return arr;
+}
+
+void *_upb_array_resize_fallback(upb_array **arr_ptr, size_t size,
+                                 upb_fieldtype_t type, upb_arena *arena) {
+  upb_array *arr = getorcreate_array(arr_ptr, type, arena);
+  return arr && _upb_array_resize(arr, size, arena) ? _upb_array_ptr(arr) : NULL;
+}
+
+bool _upb_array_append_fallback(upb_array **arr_ptr, const void *value,
+                                upb_fieldtype_t type, upb_arena *arena) {
+  upb_array *arr = getorcreate_array(arr_ptr, type, arena);
+  size_t elem = arr->len;
+  int lg2 = _upb_fieldtype_to_sizelg2[type];
+  char *data;
+
+  if (!arr || !_upb_array_resize(arr, elem + 1, arena)) return false;
+
+  data = _upb_array_ptr(arr);
+  memcpy(data + (elem << lg2), value, 1 << lg2);
+  return true;
+}
+
+/** upb_map *******************************************************************/
+
+upb_map *_upb_map_new(upb_arena *a, size_t key_size, size_t value_size) {
+  upb_map *map = upb_arena_malloc(a, sizeof(upb_map));
+
+  if (!map) {
+    return NULL;
+  }
+
+  upb_strtable_init2(&map->table, UPB_CTYPE_INT32, upb_arena_alloc(a));
+  map->key_size = key_size;
+  map->val_size = value_size;
+
+  return map;
+}
+/*
+** upb_table Implementation
+**
+** Implementation is heavily inspired by Lua's ltable.c.
+*/
+
+
+#include <string.h>
+
+
+#define UPB_MAXARRSIZE 16  /* 64k. */
+
+/* From Chromium. */
+#define ARRAY_SIZE(x) \
+    ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+
+static const double MAX_LOAD = 0.85;
+
+/* The minimum utilization of the array part of a mixed hash/array table.  This
+ * is a speed/memory-usage tradeoff (though it's not straightforward because of
+ * cache effects).  The lower this is, the more memory we'll use. */
+static const double MIN_DENSITY = 0.1;
+
+bool is_pow2(uint64_t v) { return v == 0 || (v & (v - 1)) == 0; }
+
+int log2ceil(uint64_t v) {
+  int ret = 0;
+  bool pow2 = is_pow2(v);
+  while (v >>= 1) ret++;
+  ret = pow2 ? ret : ret + 1;  /* Ceiling. */
+  return UPB_MIN(UPB_MAXARRSIZE, ret);
+}
+
+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, upb_alloc *a) {
+  size_t n;
+  char *p;
+
+  /* Prevent overflow errors. */
+  if (len == SIZE_MAX) return NULL;
+  /* 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 = upb_malloc(a, n);
+  if (p) {
+    memcpy(p, s, len);
+    p[len] = 0;
+  }
+  return p;
+}
+
+/* A type to represent the lookup key of either a strtable or an inttable. */
+typedef union {
+  uintptr_t num;
+  struct {
+    const char *str;
+    size_t len;
+  } str;
+} lookupkey_t;
+
+static lookupkey_t strkey2(const char *str, size_t len) {
+  lookupkey_t k;
+  k.str.str = str;
+  k.str.len = len;
+  return k;
+}
+
+static lookupkey_t intkey(uintptr_t key) {
+  lookupkey_t k;
+  k.num = key;
+  return k;
+}
+
+typedef uint32_t hashfunc_t(upb_tabkey key);
+typedef bool eqlfunc_t(upb_tabkey k1, lookupkey_t k2);
+
+/* Base table (shared code) ***************************************************/
+
+/* For when we need to cast away const. */
+static upb_tabent *mutable_entries(upb_table *t) {
+  return (upb_tabent*)t->entries;
+}
+
+static bool isfull(upb_table *t) {
+  if (upb_table_size(t) == 0) {
+    return true;
+  } else {
+    return ((double)(t->count + 1) / upb_table_size(t)) > MAX_LOAD;
+  }
+}
+
+static bool init(upb_table *t, uint8_t size_lg2, upb_alloc *a) {
+  size_t bytes;
+
+  t->count = 0;
+  t->size_lg2 = size_lg2;
+  t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0;
+  bytes = upb_table_size(t) * sizeof(upb_tabent);
+  if (bytes > 0) {
+    t->entries = upb_malloc(a, bytes);
+    if (!t->entries) return false;
+    memset(mutable_entries(t), 0, bytes);
+  } else {
+    t->entries = NULL;
+  }
+  return true;
+}
+
+static void uninit(upb_table *t, upb_alloc *a) {
+  upb_free(a, mutable_entries(t));
+}
+
+static upb_tabent *emptyent(upb_table *t) {
+  upb_tabent *e = mutable_entries(t) + upb_table_size(t);
+  while (1) { if (upb_tabent_isempty(--e)) return e; UPB_ASSERT(e > t->entries); }
+}
+
+static upb_tabent *getentry_mutable(upb_table *t, uint32_t hash) {
+  return (upb_tabent*)upb_getentry(t, hash);
+}
+
+static const upb_tabent *findentry(const upb_table *t, lookupkey_t key,
+                                   uint32_t hash, eqlfunc_t *eql) {
+  const upb_tabent *e;
+
+  if (t->size_lg2 == 0) return NULL;
+  e = upb_getentry(t, hash);
+  if (upb_tabent_isempty(e)) return NULL;
+  while (1) {
+    if (eql(e->key, key)) return e;
+    if ((e = e->next) == NULL) return NULL;
+  }
+}
+
+static upb_tabent *findentry_mutable(upb_table *t, lookupkey_t key,
+                                     uint32_t hash, eqlfunc_t *eql) {
+  return (upb_tabent*)findentry(t, key, hash, eql);
+}
+
+static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v,
+                   uint32_t hash, eqlfunc_t *eql) {
+  const upb_tabent *e = findentry(t, key, hash, eql);
+  if (e) {
+    if (v) {
+      _upb_value_setval(v, e->val.val);
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+/* The given key must not already exist in the table. */
+static void insert(upb_table *t, lookupkey_t key, upb_tabkey tabkey,
+                   upb_value val, uint32_t hash,
+                   hashfunc_t *hashfunc, eqlfunc_t *eql) {
+  upb_tabent *mainpos_e;
+  upb_tabent *our_e;
+
+  UPB_ASSERT(findentry(t, key, hash, eql) == NULL);
+
+  t->count++;
+  mainpos_e = getentry_mutable(t, hash);
+  our_e = mainpos_e;
+
+  if (upb_tabent_isempty(mainpos_e)) {
+    /* Our main position is empty; use it. */
+    our_e->next = NULL;
+  } else {
+    /* Collision. */
+    upb_tabent *new_e = emptyent(t);
+    /* Head of collider's chain. */
+    upb_tabent *chain = getentry_mutable(t, hashfunc(mainpos_e->key));
+    if (chain == mainpos_e) {
+      /* Existing ent is in its main posisiton (it has the same hash as us, and
+       * is the head of our chain).  Insert to new ent and append to this chain. */
+      new_e->next = mainpos_e->next;
+      mainpos_e->next = new_e;
+      our_e = new_e;
+    } else {
+      /* Existing ent is not in its main position (it is a node in some other
+       * chain).  This implies that no existing ent in the table has our hash.
+       * Evict it (updating its chain) and use its ent for head of our chain. */
+      *new_e = *mainpos_e;  /* copies next. */
+      while (chain->next != mainpos_e) {
+        chain = (upb_tabent*)chain->next;
+        UPB_ASSERT(chain);
+      }
+      chain->next = new_e;
+      our_e = mainpos_e;
+      our_e->next = NULL;
+    }
+  }
+  our_e->key = tabkey;
+  our_e->val.val = val.val;
+  UPB_ASSERT(findentry(t, key, hash, eql) == our_e);
+}
+
+static bool rm(upb_table *t, lookupkey_t key, upb_value *val,
+               upb_tabkey *removed, uint32_t hash, eqlfunc_t *eql) {
+  upb_tabent *chain = getentry_mutable(t, hash);
+  if (upb_tabent_isempty(chain)) return false;
+  if (eql(chain->key, key)) {
+    /* Element to remove is at the head of its chain. */
+    t->count--;
+    if (val) _upb_value_setval(val, chain->val.val);
+    if (removed) *removed = chain->key;
+    if (chain->next) {
+      upb_tabent *move = (upb_tabent*)chain->next;
+      *chain = *move;
+      move->key = 0;  /* Make the slot empty. */
+    } else {
+      chain->key = 0;  /* Make the slot empty. */
+    }
+    return true;
+  } else {
+    /* Element to remove is either in a non-head position or not in the
+     * table. */
+    while (chain->next && !eql(chain->next->key, key)) {
+      chain = (upb_tabent*)chain->next;
+    }
+    if (chain->next) {
+      /* Found element to remove. */
+      upb_tabent *rm = (upb_tabent*)chain->next;
+      t->count--;
+      if (val) _upb_value_setval(val, chain->next->val.val);
+      if (removed) *removed = rm->key;
+      rm->key = 0;  /* Make the slot empty. */
+      chain->next = rm->next;
+      return true;
+    } else {
+      /* Element to remove is not in the table. */
+      return false;
+    }
+  }
+}
+
+static size_t next(const upb_table *t, size_t i) {
+  do {
+    if (++i >= upb_table_size(t))
+      return SIZE_MAX - 1;  /* Distinct from -1. */
+  } while(upb_tabent_isempty(&t->entries[i]));
+
+  return i;
+}
+
+static size_t begin(const upb_table *t) {
+  return next(t, -1);
+}
+
+
+/* upb_strtable ***************************************************************/
+
+/* A simple "subclass" of upb_table that only adds a hash function for strings. */
+
+static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) {
+  uint32_t len = (uint32_t) k2.str.len;
+  char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1);
+  if (str == NULL) return 0;
+  memcpy(str, &len, sizeof(uint32_t));
+  if (k2.str.len) memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len);
+  str[sizeof(uint32_t) + k2.str.len] = '\0';
+  return (uintptr_t)str;
+}
+
+static uint32_t strhash(upb_tabkey key) {
+  uint32_t len;
+  char *str = upb_tabstr(key, &len);
+  return upb_murmur_hash2(str, len, 0);
+}
+
+static bool streql(upb_tabkey k1, lookupkey_t k2) {
+  uint32_t len;
+  char *str = upb_tabstr(k1, &len);
+  return len == k2.str.len && (len == 0 || memcmp(str, k2.str.str, len) == 0);
+}
+
+bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) {
+  UPB_UNUSED(ctype);  /* TODO(haberman): rm */
+  return init(&t->t, 2, a);
+}
+
+void upb_strtable_clear(upb_strtable *t) {
+  size_t bytes = upb_table_size(&t->t) * sizeof(upb_tabent);
+  t->t.count = 0;
+  memset((char*)t->t.entries, 0, bytes);
+}
+
+void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) {
+  size_t i;
+  for (i = 0; i < upb_table_size(&t->t); i++)
+    upb_free(a, (void*)t->t.entries[i].key);
+  uninit(&t->t, a);
+}
+
+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, size_lg2, a))
+    return false;
+  upb_strtable_begin(&i, t);
+  for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) {
+    upb_strview key = upb_strtable_iter_key(&i);
+    upb_strtable_insert3(
+        &new_table, key.data, key.size,
+        upb_strtable_iter_value(&i), a);
+  }
+  upb_strtable_uninit2(t, a);
+  *t = new_table;
+  return true;
+}
+
+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;
+
+  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, a)) {
+      return false;
+    }
+  }
+
+  key = strkey2(k, len);
+  tabkey = strcopy(key, a);
+  if (tabkey == 0) return false;
+
+  hash = upb_murmur_hash2(key.str.str, key.str.len, 0);
+  insert(&t->t, key, tabkey, v, hash, &strhash, &streql);
+  return true;
+}
+
+bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len,
+                          upb_value *v) {
+  uint32_t hash = upb_murmur_hash2(key, len, 0);
+  return lookup(&t->t, strkey2(key, len), v, hash, &streql);
+}
+
+bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
+                         upb_value *val, upb_alloc *alloc) {
+  uint32_t hash = upb_murmur_hash2(key, len, 0);
+  upb_tabkey tabkey;
+  if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) {
+    if (alloc) {
+      /* Arena-based allocs don't need to free and won't pass this. */
+      upb_free(alloc, (void*)tabkey);
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+/* Iteration */
+
+void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t) {
+  i->t = t;
+  i->index = begin(&t->t);
+}
+
+void upb_strtable_next(upb_strtable_iter *i) {
+  i->index = next(&i->t->t, i->index);
+}
+
+bool upb_strtable_done(const upb_strtable_iter *i) {
+  if (!i->t) return true;
+  return i->index >= upb_table_size(&i->t->t) ||
+         upb_tabent_isempty(str_tabent(i));
+}
+
+upb_strview upb_strtable_iter_key(const upb_strtable_iter *i) {
+  upb_strview key;
+  uint32_t len;
+  UPB_ASSERT(!upb_strtable_done(i));
+  key.data = upb_tabstr(str_tabent(i)->key, &len);
+  key.size = len;
+  return key;
+}
+
+upb_value upb_strtable_iter_value(const upb_strtable_iter *i) {
+  UPB_ASSERT(!upb_strtable_done(i));
+  return _upb_value_val(str_tabent(i)->val.val);
+}
+
+void upb_strtable_iter_setdone(upb_strtable_iter *i) {
+  i->t = NULL;
+  i->index = SIZE_MAX;
+}
+
+bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,
+                               const upb_strtable_iter *i2) {
+  if (upb_strtable_done(i1) && upb_strtable_done(i2))
+    return true;
+  return i1->t == i2->t && i1->index == i2->index;
+}
+
+
+/* upb_inttable ***************************************************************/
+
+/* For inttables we use a hybrid structure where small keys are kept in an
+ * array and large keys are put in the hash table. */
+
+static uint32_t inthash(upb_tabkey key) { return upb_inthash(key); }
+
+static bool inteql(upb_tabkey k1, lookupkey_t k2) {
+  return k1 == k2.num;
+}
+
+static upb_tabval *mutable_array(upb_inttable *t) {
+  return (upb_tabval*)t->array;
+}
+
+static upb_tabval *inttable_val(upb_inttable *t, uintptr_t key) {
+  if (key < t->array_size) {
+    return upb_arrhas(t->array[key]) ? &(mutable_array(t)[key]) : NULL;
+  } else {
+    upb_tabent *e =
+        findentry_mutable(&t->t, intkey(key), upb_inthash(key), &inteql);
+    return e ? &e->val : NULL;
+  }
+}
+
+static const upb_tabval *inttable_val_const(const upb_inttable *t,
+                                            uintptr_t key) {
+  return inttable_val((upb_inttable*)t, key);
+}
+
+size_t upb_inttable_count(const upb_inttable *t) {
+  return t->t.count + t->array_count;
+}
+
+static void check(upb_inttable *t) {
+  UPB_UNUSED(t);
+#if defined(UPB_DEBUG_TABLE) && !defined(NDEBUG)
+  {
+    /* This check is very expensive (makes inserts/deletes O(N)). */
+    size_t count = 0;
+    upb_inttable_iter i;
+    upb_inttable_begin(&i, t);
+    for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) {
+      UPB_ASSERT(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL));
+    }
+    UPB_ASSERT(count == upb_inttable_count(t));
+  }
+#endif
+}
+
+bool upb_inttable_sizedinit(upb_inttable *t, size_t asize, int hsize_lg2,
+                            upb_alloc *a) {
+  size_t array_bytes;
+
+  if (!init(&t->t, 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 = upb_malloc(a, array_bytes);
+  if (!t->array) {
+    uninit(&t->t, a);
+    return false;
+  }
+  memset(mutable_array(t), 0xff, array_bytes);
+  check(t);
+  return true;
+}
+
+bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) {
+  UPB_UNUSED(ctype);  /* TODO(haberman): rm */
+  return upb_inttable_sizedinit(t, 0, 4, a);
+}
+
+void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) {
+  uninit(&t->t, a);
+  upb_free(a, mutable_array(t));
+}
+
+bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
+                          upb_alloc *a) {
+  upb_tabval tabval;
+  tabval.val = val.val;
+  UPB_ASSERT(upb_arrhas(tabval));  /* This will reject (uint64_t)-1.  Fix this. */
+
+  if (key < t->array_size) {
+    UPB_ASSERT(!upb_arrhas(t->array[key]));
+    t->array_count++;
+    mutable_array(t)[key].val = val.val;
+  } else {
+    if (isfull(&t->t)) {
+      /* 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.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;
+        upb_value v;
+
+        _upb_value_setval(&v, e->val.val);
+        hash = upb_inthash(e->key);
+        insert(&new_table, intkey(e->key), e->key, v, hash, &inthash, &inteql);
+      }
+
+      UPB_ASSERT(t->t.count == new_table.count);
+
+      uninit(&t->t, a);
+      t->t = new_table;
+    }
+    insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql);
+  }
+  check(t);
+  return true;
+}
+
+bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v) {
+  const upb_tabval *table_v = inttable_val_const(t, key);
+  if (!table_v) return false;
+  if (v) _upb_value_setval(v, table_v->val);
+  return true;
+}
+
+bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val) {
+  upb_tabval *table_v = inttable_val(t, key);
+  if (!table_v) return false;
+  table_v->val = val.val;
+  return true;
+}
+
+bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) {
+  bool success;
+  if (key < t->array_size) {
+    if (upb_arrhas(t->array[key])) {
+      upb_tabval empty = UPB_TABVALUE_EMPTY_INIT;
+      t->array_count--;
+      if (val) {
+        _upb_value_setval(val, t->array[key].val);
+      }
+      mutable_array(t)[key] = empty;
+      success = true;
+    } else {
+      success = false;
+    }
+  } else {
+    success = rm(&t->t, intkey(key), val, NULL, upb_inthash(key), &inteql);
+  }
+  check(t);
+  return success;
+}
+
+bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {
+  return upb_inttable_insert2(t, upb_inttable_count(t), val, a);
+}
+
+upb_value upb_inttable_pop(upb_inttable *t) {
+  upb_value val;
+  bool ok = upb_inttable_remove(t, upb_inttable_count(t) - 1, &val);
+  UPB_ASSERT(ok);
+  return val;
+}
+
+bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
+                             upb_alloc *a) {
+  return upb_inttable_insert2(t, (uintptr_t)key, val, a);
+}
+
+bool upb_inttable_lookupptr(const upb_inttable *t, const void *key,
+                            upb_value *v) {
+  return upb_inttable_lookup(t, (uintptr_t)key, v);
+}
+
+bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) {
+  return upb_inttable_remove(t, (uintptr_t)key, val);
+}
+
+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};
+
+  /* The max key in each bucket. */
+  uintptr_t max[UPB_MAXARRSIZE + 1] = {0};
+
+  upb_inttable_iter i;
+  size_t arr_count;
+  int size_lg2;
+  upb_inttable new_t;
+
+  upb_inttable_begin(&i, t);
+  for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+    uintptr_t key = upb_inttable_iter_key(&i);
+    int bucket = log2ceil(key);
+    max[bucket] = UPB_MAX(max[bucket], key);
+    counts[bucket]++;
+  }
+
+  /* Find the largest power of two that satisfies the MIN_DENSITY
+   * definition (while actually having some keys). */
+  arr_count = upb_inttable_count(t);
+
+  for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 0; size_lg2--) {
+    if (counts[size_lg2] == 0) {
+      /* We can halve again without losing any entries. */
+      continue;
+    } else if (arr_count >= (1 << size_lg2) * MIN_DENSITY) {
+      break;
+    }
+
+    arr_count -= counts[size_lg2];
+  }
+
+  UPB_ASSERT(arr_count <= upb_inttable_count(t));
+
+  {
+    /* Insert all elements into new, perfectly-sized table. */
+    size_t arr_size = max[size_lg2] + 1;  /* +1 so arr[max] will fit. */
+    size_t hash_count = upb_inttable_count(t) - arr_count;
+    size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
+    int hashsize_lg2 = log2ceil(hash_size);
+
+    upb_inttable_sizedinit(&new_t, 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_insert2(&new_t, k, upb_inttable_iter_value(&i), a);
+    }
+    UPB_ASSERT(new_t.array_size == arr_size);
+    UPB_ASSERT(new_t.t.size_lg2 == hashsize_lg2);
+  }
+  upb_inttable_uninit2(t, a);
+  *t = new_t;
+}
+
+/* Iteration. */
+
+static const upb_tabent *int_tabent(const upb_inttable_iter *i) {
+  UPB_ASSERT(!i->array_part);
+  return &i->t->t.entries[i->index];
+}
+
+static upb_tabval int_arrent(const upb_inttable_iter *i) {
+  UPB_ASSERT(i->array_part);
+  return i->t->array[i->index];
+}
+
+void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t) {
+  i->t = t;
+  i->index = -1;
+  i->array_part = true;
+  upb_inttable_next(i);
+}
+
+void upb_inttable_next(upb_inttable_iter *iter) {
+  const upb_inttable *t = iter->t;
+  if (iter->array_part) {
+    while (++iter->index < t->array_size) {
+      if (upb_arrhas(int_arrent(iter))) {
+        return;
+      }
+    }
+    iter->array_part = false;
+    iter->index = begin(&t->t);
+  } else {
+    iter->index = next(&t->t, iter->index);
+  }
+}
+
+bool upb_inttable_done(const upb_inttable_iter *i) {
+  if (!i->t) return true;
+  if (i->array_part) {
+    return i->index >= i->t->array_size ||
+           !upb_arrhas(int_arrent(i));
+  } else {
+    return i->index >= upb_table_size(&i->t->t) ||
+           upb_tabent_isempty(int_tabent(i));
+  }
+}
+
+uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i) {
+  UPB_ASSERT(!upb_inttable_done(i));
+  return i->array_part ? i->index : int_tabent(i)->key;
+}
+
+upb_value upb_inttable_iter_value(const upb_inttable_iter *i) {
+  UPB_ASSERT(!upb_inttable_done(i));
+  return _upb_value_val(
+      i->array_part ? i->t->array[i->index].val : int_tabent(i)->val.val);
+}
+
+void upb_inttable_iter_setdone(upb_inttable_iter *i) {
+  i->t = NULL;
+  i->index = SIZE_MAX;
+  i->array_part = false;
+}
+
+bool upb_inttable_iter_isequal(const upb_inttable_iter *i1,
+                                          const upb_inttable_iter *i2) {
+  if (upb_inttable_done(i1) && upb_inttable_done(i2))
+    return true;
+  return i1->t == i2->t && i1->index == i2->index &&
+         i1->array_part == i2->array_part;
+}
+
+#if defined(UPB_UNALIGNED_READS_OK) || defined(__s390x__)
+/* -----------------------------------------------------------------------------
+ * MurmurHash2, by Austin Appleby (released as public domain).
+ * Reformatted and C99-ified by Joshua Haberman.
+ * Note - This code makes a few assumptions about how your machine behaves -
+ *   1. We can read a 4-byte value from any address without crashing
+ *   2. sizeof(int) == 4 (in upb this limitation is removed by using uint32_t
+ * And it has a few limitations -
+ *   1. It will not work incrementally.
+ *   2. It will not produce the same results on little-endian and big-endian
+ *      machines. */
+uint32_t upb_murmur_hash2(const void *key, size_t len, uint32_t seed) {
+  /* 'm' and 'r' are mixing constants generated offline.
+   * They're not really 'magic', they just happen to work well. */
+  const uint32_t m = 0x5bd1e995;
+  const int32_t r = 24;
+
+  /* Initialize the hash to a 'random' value */
+  uint32_t h = seed ^ len;
+
+  /* Mix 4 bytes at a time into the hash */
+  const uint8_t * data = (const uint8_t *)key;
+  while(len >= 4) {
+    uint32_t k;
+    memcpy(&k, data, sizeof(k));
+
+    k *= m;
+    k ^= k >> r;
+    k *= m;
+
+    h *= m;
+    h ^= k;
+
+    data += 4;
+    len -= 4;
+  }
+
+  /* Handle the last few bytes of the input array */
+  switch(len) {
+    case 3: h ^= data[2] << 16;
+    case 2: h ^= data[1] << 8;
+    case 1: h ^= data[0]; h *= m;
+  };
+
+  /* Do a few final mixes of the hash to ensure the last few
+   * bytes are well-incorporated. */
+  h ^= h >> 13;
+  h *= m;
+  h ^= h >> 15;
+
+  return h;
+}
+
+#else /* !UPB_UNALIGNED_READS_OK */
+
+/* -----------------------------------------------------------------------------
+ * MurmurHashAligned2, by Austin Appleby
+ * Same algorithm as MurmurHash2, but only does aligned reads - should be safer
+ * on certain platforms.
+ * Performance will be lower than MurmurHash2 */
+
+#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
+
+uint32_t upb_murmur_hash2(const void * key, size_t len, uint32_t seed) {
+  const uint32_t m = 0x5bd1e995;
+  const int32_t r = 24;
+  const uint8_t * data = (const uint8_t *)key;
+  uint32_t h = (uint32_t)(seed ^ len);
+  uint8_t align = (uintptr_t)data & 3;
+
+  if(align && (len >= 4)) {
+    /* Pre-load the temp registers */
+    uint32_t t = 0, d = 0;
+    int32_t sl;
+    int32_t sr;
+
+    switch(align) {
+      case 1: t |= data[2] << 16;  /* fallthrough */
+      case 2: t |= data[1] << 8;   /* fallthrough */
+      case 3: t |= data[0];
+    }
+
+    t <<= (8 * align);
+
+    data += 4-align;
+    len -= 4-align;
+
+    sl = 8 * (4-align);
+    sr = 8 * align;
+
+    /* Mix */
+
+    while(len >= 4) {
+      uint32_t k;
+
+      d = *(uint32_t *)data;
+      t = (t >> sr) | (d << sl);
+
+      k = t;
+
+      MIX(h,k,m);
+
+      t = d;
+
+      data += 4;
+      len -= 4;
+    }
+
+    /* Handle leftover data in temp registers */
+
+    d = 0;
+
+    if(len >= align) {
+      uint32_t k;
+
+      switch(align) {
+        case 3: d |= data[2] << 16;  /* fallthrough */
+        case 2: d |= data[1] << 8;   /* fallthrough */
+        case 1: d |= data[0];        /* fallthrough */
+      }
+
+      k = (t >> sr) | (d << sl);
+      MIX(h,k,m);
+
+      data += align;
+      len -= align;
+
+      /* ----------
+       * Handle tail bytes */
+
+      switch(len) {
+        case 3: h ^= data[2] << 16;    /* fallthrough */
+        case 2: h ^= data[1] << 8;     /* fallthrough */
+        case 1: h ^= data[0]; h *= m;  /* fallthrough */
+      };
+    } else {
+      switch(len) {
+        case 3: d |= data[2] << 16;  /* fallthrough */
+        case 2: d |= data[1] << 8;   /* fallthrough */
+        case 1: d |= data[0];        /* fallthrough */
+        case 0: h ^= (t >> sr) | (d << sl); h *= m;
+      }
+    }
+
+    h ^= h >> 13;
+    h *= m;
+    h ^= h >> 15;
+
+    return h;
+  } else {
+    while(len >= 4) {
+      uint32_t k = *(uint32_t *)data;
+
+      MIX(h,k,m);
+
+      data += 4;
+      len -= 4;
+    }
+
+    /* ----------
+     * Handle tail bytes */
+
+    switch(len) {
+      case 3: h ^= data[2] << 16; /* fallthrough */
+      case 2: h ^= data[1] << 8;  /* fallthrough */
+      case 1: h ^= data[0]; h *= m;
+    };
+
+    h ^= h >> 13;
+    h *= m;
+    h ^= h >> 15;
+
+    return h;
+  }
+}
+#undef MIX
+
+#endif /* UPB_UNALIGNED_READS_OK */
+
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* upb_status *****************************************************************/
+
+void upb_status_clear(upb_status *status) {
+  if (!status) return;
+  status->ok = true;
+  status->msg[0] = '\0';
+}
+
+bool upb_ok(const upb_status *status) { return status->ok; }
+
+const char *upb_status_errmsg(const upb_status *status) { return status->msg; }
+
+void upb_status_seterrmsg(upb_status *status, const char *msg) {
+  if (!status) return;
+  status->ok = false;
+  strncpy(status->msg, msg, UPB_STATUS_MAX_MESSAGE - 1);
+  status->msg[UPB_STATUS_MAX_MESSAGE - 1] = '\0';
+}
+
+void upb_status_seterrf(upb_status *status, const char *fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  upb_status_vseterrf(status, fmt, args);
+  va_end(args);
+}
+
+void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) {
+  if (!status) return;
+  status->ok = false;
+  _upb_vsnprintf(status->msg, sizeof(status->msg), fmt, args);
+  status->msg[UPB_STATUS_MAX_MESSAGE - 1] = '\0';
+}
+
+void upb_status_vappenderrf(upb_status *status, const char *fmt, va_list args) {
+  size_t len;
+  if (!status) return;
+  status->ok = false;
+  len = strlen(status->msg);
+  _upb_vsnprintf(status->msg + len, sizeof(status->msg) - len, fmt, args);
+  status->msg[UPB_STATUS_MAX_MESSAGE - 1] = '\0';
+}
+
+/* upb_alloc ******************************************************************/
+
+static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                                  size_t size) {
+  UPB_UNUSED(alloc);
+  UPB_UNUSED(oldsize);
+  if (size == 0) {
+    free(ptr);
+    return NULL;
+  } else {
+    return realloc(ptr, size);
+  }
+}
+
+upb_alloc upb_alloc_global = {&upb_global_allocfunc};
+
+/* upb_arena ******************************************************************/
+
+/* Be conservative and choose 16 in case anyone is using SSE. */
+
+typedef struct mem_block {
+  struct mem_block *next;
+  uint32_t size;
+  uint32_t cleanups;
+  /* Data follows. */
+} mem_block;
+
+typedef struct cleanup_ent {
+  upb_cleanup_func *cleanup;
+  void *ud;
+} cleanup_ent;
+
+struct upb_arena {
+  _upb_arena_head head;
+  uint32_t *cleanups;
+
+  /* Allocator to allocate arena blocks.  We are responsible for freeing these
+   * when we are destroyed. */
+  upb_alloc *block_alloc;
+  uint32_t last_size;
+
+  /* When multiple arenas are fused together, each arena points to a parent
+   * arena (root points to itself). The root tracks how many live arenas
+   * reference it. */
+  uint32_t refcount;  /* Only used when a->parent == a */
+  struct upb_arena *parent;
+
+  /* Linked list of blocks to free/cleanup. */
+  mem_block *freelist, *freelist_tail;
+};
+
+static const size_t memblock_reserve = UPB_ALIGN_UP(sizeof(mem_block), 16);
+
+static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size) {
+  mem_block *block = ptr;
+
+  block->next = a->freelist;
+  block->size = (uint32_t)size;
+  block->cleanups = 0;
+  a->freelist = block;
+  a->last_size = block->size;
+  if (!a->freelist_tail) a->freelist_tail = block;
+
+  a->head.ptr = UPB_PTR_AT(block, memblock_reserve, char);
+  a->head.end = UPB_PTR_AT(block, size, char);
+  a->cleanups = &block->cleanups;
+
+  /* TODO(haberman): ASAN poison. */
+}
+
+static bool upb_arena_allocblock(upb_arena *a, size_t size) {
+  size_t block_size = UPB_MAX(size, a->last_size * 2) + memblock_reserve;
+  mem_block *block = upb_malloc(a->block_alloc, block_size);
+
+  if (!block) return false;
+  upb_arena_addblock(a, block, block_size);
+  return true;
+}
+
+static bool arena_has(upb_arena *a, size_t size) {
+  _upb_arena_head *h = (_upb_arena_head*)a;
+  return (size_t)(h->end - h->ptr) >= size;
+}
+
+void *_upb_arena_slowmalloc(upb_arena *a, size_t size) {
+  if (!upb_arena_allocblock(a, size)) return NULL;  /* Out of memory. */
+  UPB_ASSERT(arena_has(a, size));
+  return upb_arena_malloc(a, size);
+}
+
+static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                               size_t size) {
+  upb_arena *a = (upb_arena*)alloc;  /* upb_alloc is initial member. */
+  return upb_arena_realloc(a, ptr, oldsize, size);
+}
+
+static upb_arena *arena_findroot(upb_arena *a) {
+  /* Path splitting keeps time complexity down, see:
+   *   https://en.wikipedia.org/wiki/Disjoint-set_data_structure */
+  while (a->parent != a) {
+    upb_arena *next = a->parent;
+    a->parent = next->parent;
+    a = next;
+  }
+  return a;
+}
+
+/* Public Arena API ***********************************************************/
+
+upb_arena *arena_initslow(void *mem, size_t n, upb_alloc *alloc) {
+  const size_t first_block_overhead = sizeof(upb_arena) + memblock_reserve;
+  upb_arena *a;
+
+  /* We need to malloc the initial block. */
+  n = first_block_overhead + 256;
+  if (!alloc || !(mem = upb_malloc(alloc, n))) {
+    return NULL;
+  }
+
+  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_arena);
+  n -= sizeof(*a);
+
+  a->head.alloc.func = &upb_arena_doalloc;
+  a->block_alloc = alloc;
+  a->parent = a;
+  a->refcount = 1;
+  a->freelist = NULL;
+  a->freelist_tail = NULL;
+
+  upb_arena_addblock(a, mem, n);
+
+  return a;
+}
+
+upb_arena *upb_arena_init(void *mem, size_t n, upb_alloc *alloc) {
+  upb_arena *a;
+
+  /* Round block size down to alignof(*a) since we will allocate the arena
+   * itself at the end. */
+  n = UPB_ALIGN_DOWN(n, UPB_ALIGN_OF(upb_arena));
+
+  if (UPB_UNLIKELY(n < sizeof(upb_arena))) {
+    return arena_initslow(mem, n, alloc);
+  }
+
+  a = UPB_PTR_AT(mem, n - sizeof(*a), upb_arena);
+  n -= sizeof(*a);
+
+  a->head.alloc.func = &upb_arena_doalloc;
+  a->block_alloc = alloc;
+  a->parent = a;
+  a->refcount = 1;
+  a->last_size = 128;
+  a->head.ptr = mem;
+  a->head.end = UPB_PTR_AT(mem, n, char);
+  a->freelist = NULL;
+  a->cleanups = NULL;
+
+  return a;
+}
+
+static void arena_dofree(upb_arena *a) {
+  mem_block *block = a->freelist;
+  UPB_ASSERT(a->parent == a);
+  UPB_ASSERT(a->refcount == 0);
+
+  while (block) {
+    /* Load first since we are deleting block. */
+    mem_block *next = block->next;
+
+    if (block->cleanups > 0) {
+      cleanup_ent *end = UPB_PTR_AT(block, block->size, void);
+      cleanup_ent *ptr = end - block->cleanups;
+
+      for (; ptr < end; ptr++) {
+        ptr->cleanup(ptr->ud);
+      }
+    }
+
+    upb_free(a->block_alloc, block);
+    block = next;
+  }
+}
+
+void upb_arena_free(upb_arena *a) {
+  a = arena_findroot(a);
+  if (--a->refcount == 0) arena_dofree(a);
+}
+
+bool upb_arena_addcleanup(upb_arena *a, void *ud, upb_cleanup_func *func) {
+  cleanup_ent *ent;
+
+  if (!a->cleanups || !arena_has(a, sizeof(cleanup_ent))) {
+    if (!upb_arena_allocblock(a, 128)) return false;  /* Out of memory. */
+    UPB_ASSERT(arena_has(a, sizeof(cleanup_ent)));
+  }
+
+  a->head.end -= sizeof(cleanup_ent);
+  ent = (cleanup_ent*)a->head.end;
+  (*a->cleanups)++;
+
+  ent->cleanup = func;
+  ent->ud = ud;
+
+  return true;
+}
+
+void upb_arena_fuse(upb_arena *a1, upb_arena *a2) {
+  upb_arena *r1 = arena_findroot(a1);
+  upb_arena *r2 = arena_findroot(a2);
+
+  if (r1 == r2) return;  /* Already fused. */
+
+  /* We want to join the smaller tree to the larger tree.
+   * So swap first if they are backwards. */
+  if (r1->refcount < r2->refcount) {
+    upb_arena *tmp = r1;
+    r1 = r2;
+    r2 = tmp;
+  }
+
+  /* r1 takes over r2's freelist and refcount. */
+  r1->refcount += r2->refcount;
+  if (r2->freelist_tail) {
+    UPB_ASSERT(r2->freelist_tail->next == NULL);
+    r2->freelist_tail->next = r1->freelist;
+    r1->freelist = r2->freelist;
+  }
+  r2->parent = r1;
+}
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     google/protobuf/descriptor.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#include <stddef.h>
+
+
+static const upb_msglayout *const google_protobuf_FileDescriptorSet_submsgs[1] = {
+  &google_protobuf_FileDescriptorProto_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_FileDescriptorSet__fields[1] = {
+  {1, UPB_SIZE(0, 0), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_FileDescriptorSet_msginit = {
+  &google_protobuf_FileDescriptorSet_submsgs[0],
+  &google_protobuf_FileDescriptorSet__fields[0],
+  UPB_SIZE(4, 8), 1, false,
+};
+
+static const upb_msglayout *const google_protobuf_FileDescriptorProto_submsgs[6] = {
+  &google_protobuf_DescriptorProto_msginit,
+  &google_protobuf_EnumDescriptorProto_msginit,
+  &google_protobuf_FieldDescriptorProto_msginit,
+  &google_protobuf_FileOptions_msginit,
+  &google_protobuf_ServiceDescriptorProto_msginit,
+  &google_protobuf_SourceCodeInfo_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_FileDescriptorProto__fields[12] = {
+  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {2, UPB_SIZE(12, 24), 2, 0, 9, 1},
+  {3, UPB_SIZE(36, 72), 0, 0, 9, 3},
+  {4, UPB_SIZE(40, 80), 0, 0, 11, 3},
+  {5, UPB_SIZE(44, 88), 0, 1, 11, 3},
+  {6, UPB_SIZE(48, 96), 0, 4, 11, 3},
+  {7, UPB_SIZE(52, 104), 0, 2, 11, 3},
+  {8, UPB_SIZE(28, 56), 4, 3, 11, 1},
+  {9, UPB_SIZE(32, 64), 5, 5, 11, 1},
+  {10, UPB_SIZE(56, 112), 0, 0, 5, 3},
+  {11, UPB_SIZE(60, 120), 0, 0, 5, 3},
+  {12, UPB_SIZE(20, 40), 3, 0, 9, 1},
+};
+
+const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
+  &google_protobuf_FileDescriptorProto_submsgs[0],
+  &google_protobuf_FileDescriptorProto__fields[0],
+  UPB_SIZE(64, 128), 12, false,
+};
+
+static const upb_msglayout *const google_protobuf_DescriptorProto_submsgs[8] = {
+  &google_protobuf_DescriptorProto_msginit,
+  &google_protobuf_DescriptorProto_ExtensionRange_msginit,
+  &google_protobuf_DescriptorProto_ReservedRange_msginit,
+  &google_protobuf_EnumDescriptorProto_msginit,
+  &google_protobuf_FieldDescriptorProto_msginit,
+  &google_protobuf_MessageOptions_msginit,
+  &google_protobuf_OneofDescriptorProto_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_DescriptorProto__fields[10] = {
+  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {2, UPB_SIZE(16, 32), 0, 4, 11, 3},
+  {3, UPB_SIZE(20, 40), 0, 0, 11, 3},
+  {4, UPB_SIZE(24, 48), 0, 3, 11, 3},
+  {5, UPB_SIZE(28, 56), 0, 1, 11, 3},
+  {6, UPB_SIZE(32, 64), 0, 4, 11, 3},
+  {7, UPB_SIZE(12, 24), 2, 5, 11, 1},
+  {8, UPB_SIZE(36, 72), 0, 6, 11, 3},
+  {9, UPB_SIZE(40, 80), 0, 2, 11, 3},
+  {10, UPB_SIZE(44, 88), 0, 0, 9, 3},
+};
+
+const upb_msglayout google_protobuf_DescriptorProto_msginit = {
+  &google_protobuf_DescriptorProto_submsgs[0],
+  &google_protobuf_DescriptorProto__fields[0],
+  UPB_SIZE(48, 96), 10, false,
+};
+
+static const upb_msglayout *const google_protobuf_DescriptorProto_ExtensionRange_submsgs[1] = {
+  &google_protobuf_ExtensionRangeOptions_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_DescriptorProto_ExtensionRange__fields[3] = {
+  {1, UPB_SIZE(4, 4), 1, 0, 5, 1},
+  {2, UPB_SIZE(8, 8), 2, 0, 5, 1},
+  {3, UPB_SIZE(12, 16), 3, 0, 11, 1},
+};
+
+const upb_msglayout google_protobuf_DescriptorProto_ExtensionRange_msginit = {
+  &google_protobuf_DescriptorProto_ExtensionRange_submsgs[0],
+  &google_protobuf_DescriptorProto_ExtensionRange__fields[0],
+  UPB_SIZE(16, 24), 3, false,
+};
+
+static const upb_msglayout_field google_protobuf_DescriptorProto_ReservedRange__fields[2] = {
+  {1, UPB_SIZE(4, 4), 1, 0, 5, 1},
+  {2, UPB_SIZE(8, 8), 2, 0, 5, 1},
+};
+
+const upb_msglayout google_protobuf_DescriptorProto_ReservedRange_msginit = {
+  NULL,
+  &google_protobuf_DescriptorProto_ReservedRange__fields[0],
+  UPB_SIZE(12, 12), 2, false,
+};
+
+static const upb_msglayout *const google_protobuf_ExtensionRangeOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_ExtensionRangeOptions__fields[1] = {
+  {999, UPB_SIZE(0, 0), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_ExtensionRangeOptions_msginit = {
+  &google_protobuf_ExtensionRangeOptions_submsgs[0],
+  &google_protobuf_ExtensionRangeOptions__fields[0],
+  UPB_SIZE(4, 8), 1, false,
+};
+
+static const upb_msglayout *const google_protobuf_FieldDescriptorProto_submsgs[1] = {
+  &google_protobuf_FieldOptions_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_FieldDescriptorProto__fields[11] = {
+  {1, UPB_SIZE(36, 40), 6, 0, 9, 1},
+  {2, UPB_SIZE(44, 56), 7, 0, 9, 1},
+  {3, UPB_SIZE(24, 24), 3, 0, 5, 1},
+  {4, UPB_SIZE(8, 8), 1, 0, 14, 1},
+  {5, UPB_SIZE(16, 16), 2, 0, 14, 1},
+  {6, UPB_SIZE(52, 72), 8, 0, 9, 1},
+  {7, UPB_SIZE(60, 88), 9, 0, 9, 1},
+  {8, UPB_SIZE(76, 120), 11, 0, 11, 1},
+  {9, UPB_SIZE(28, 28), 4, 0, 5, 1},
+  {10, UPB_SIZE(68, 104), 10, 0, 9, 1},
+  {17, UPB_SIZE(32, 32), 5, 0, 8, 1},
+};
+
+const upb_msglayout google_protobuf_FieldDescriptorProto_msginit = {
+  &google_protobuf_FieldDescriptorProto_submsgs[0],
+  &google_protobuf_FieldDescriptorProto__fields[0],
+  UPB_SIZE(80, 128), 11, false,
+};
+
+static const upb_msglayout *const google_protobuf_OneofDescriptorProto_submsgs[1] = {
+  &google_protobuf_OneofOptions_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_OneofDescriptorProto__fields[2] = {
+  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {2, UPB_SIZE(12, 24), 2, 0, 11, 1},
+};
+
+const upb_msglayout google_protobuf_OneofDescriptorProto_msginit = {
+  &google_protobuf_OneofDescriptorProto_submsgs[0],
+  &google_protobuf_OneofDescriptorProto__fields[0],
+  UPB_SIZE(16, 32), 2, false,
+};
+
+static const upb_msglayout *const google_protobuf_EnumDescriptorProto_submsgs[3] = {
+  &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit,
+  &google_protobuf_EnumOptions_msginit,
+  &google_protobuf_EnumValueDescriptorProto_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_EnumDescriptorProto__fields[5] = {
+  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {2, UPB_SIZE(16, 32), 0, 2, 11, 3},
+  {3, UPB_SIZE(12, 24), 2, 1, 11, 1},
+  {4, UPB_SIZE(20, 40), 0, 0, 11, 3},
+  {5, UPB_SIZE(24, 48), 0, 0, 9, 3},
+};
+
+const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
+  &google_protobuf_EnumDescriptorProto_submsgs[0],
+  &google_protobuf_EnumDescriptorProto__fields[0],
+  UPB_SIZE(32, 64), 5, false,
+};
+
+static const upb_msglayout_field google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[2] = {
+  {1, UPB_SIZE(4, 4), 1, 0, 5, 1},
+  {2, UPB_SIZE(8, 8), 2, 0, 5, 1},
+};
+
+const upb_msglayout google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit = {
+  NULL,
+  &google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[0],
+  UPB_SIZE(12, 12), 2, false,
+};
+
+static const upb_msglayout *const google_protobuf_EnumValueDescriptorProto_submsgs[1] = {
+  &google_protobuf_EnumValueOptions_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_EnumValueDescriptorProto__fields[3] = {
+  {1, UPB_SIZE(8, 8), 2, 0, 9, 1},
+  {2, UPB_SIZE(4, 4), 1, 0, 5, 1},
+  {3, UPB_SIZE(16, 24), 3, 0, 11, 1},
+};
+
+const upb_msglayout google_protobuf_EnumValueDescriptorProto_msginit = {
+  &google_protobuf_EnumValueDescriptorProto_submsgs[0],
+  &google_protobuf_EnumValueDescriptorProto__fields[0],
+  UPB_SIZE(24, 32), 3, false,
+};
+
+static const upb_msglayout *const google_protobuf_ServiceDescriptorProto_submsgs[2] = {
+  &google_protobuf_MethodDescriptorProto_msginit,
+  &google_protobuf_ServiceOptions_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_ServiceDescriptorProto__fields[3] = {
+  {1, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {2, UPB_SIZE(16, 32), 0, 0, 11, 3},
+  {3, UPB_SIZE(12, 24), 2, 1, 11, 1},
+};
+
+const upb_msglayout google_protobuf_ServiceDescriptorProto_msginit = {
+  &google_protobuf_ServiceDescriptorProto_submsgs[0],
+  &google_protobuf_ServiceDescriptorProto__fields[0],
+  UPB_SIZE(24, 48), 3, false,
+};
+
+static const upb_msglayout *const google_protobuf_MethodDescriptorProto_submsgs[1] = {
+  &google_protobuf_MethodOptions_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_MethodDescriptorProto__fields[6] = {
+  {1, UPB_SIZE(4, 8), 3, 0, 9, 1},
+  {2, UPB_SIZE(12, 24), 4, 0, 9, 1},
+  {3, UPB_SIZE(20, 40), 5, 0, 9, 1},
+  {4, UPB_SIZE(28, 56), 6, 0, 11, 1},
+  {5, UPB_SIZE(1, 1), 1, 0, 8, 1},
+  {6, UPB_SIZE(2, 2), 2, 0, 8, 1},
+};
+
+const upb_msglayout google_protobuf_MethodDescriptorProto_msginit = {
+  &google_protobuf_MethodDescriptorProto_submsgs[0],
+  &google_protobuf_MethodDescriptorProto__fields[0],
+  UPB_SIZE(32, 64), 6, false,
+};
+
+static const upb_msglayout *const google_protobuf_FileOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_FileOptions__fields[21] = {
+  {1, UPB_SIZE(28, 32), 11, 0, 9, 1},
+  {8, UPB_SIZE(36, 48), 12, 0, 9, 1},
+  {9, UPB_SIZE(8, 8), 1, 0, 14, 1},
+  {10, UPB_SIZE(16, 16), 2, 0, 8, 1},
+  {11, UPB_SIZE(44, 64), 13, 0, 9, 1},
+  {16, UPB_SIZE(17, 17), 3, 0, 8, 1},
+  {17, UPB_SIZE(18, 18), 4, 0, 8, 1},
+  {18, UPB_SIZE(19, 19), 5, 0, 8, 1},
+  {20, UPB_SIZE(20, 20), 6, 0, 8, 1},
+  {23, UPB_SIZE(21, 21), 7, 0, 8, 1},
+  {27, UPB_SIZE(22, 22), 8, 0, 8, 1},
+  {31, UPB_SIZE(23, 23), 9, 0, 8, 1},
+  {36, UPB_SIZE(52, 80), 14, 0, 9, 1},
+  {37, UPB_SIZE(60, 96), 15, 0, 9, 1},
+  {39, UPB_SIZE(68, 112), 16, 0, 9, 1},
+  {40, UPB_SIZE(76, 128), 17, 0, 9, 1},
+  {41, UPB_SIZE(84, 144), 18, 0, 9, 1},
+  {42, UPB_SIZE(24, 24), 10, 0, 8, 1},
+  {44, UPB_SIZE(92, 160), 19, 0, 9, 1},
+  {45, UPB_SIZE(100, 176), 20, 0, 9, 1},
+  {999, UPB_SIZE(108, 192), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_FileOptions_msginit = {
+  &google_protobuf_FileOptions_submsgs[0],
+  &google_protobuf_FileOptions__fields[0],
+  UPB_SIZE(112, 208), 21, false,
+};
+
+static const upb_msglayout *const google_protobuf_MessageOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_MessageOptions__fields[5] = {
+  {1, UPB_SIZE(1, 1), 1, 0, 8, 1},
+  {2, UPB_SIZE(2, 2), 2, 0, 8, 1},
+  {3, UPB_SIZE(3, 3), 3, 0, 8, 1},
+  {7, UPB_SIZE(4, 4), 4, 0, 8, 1},
+  {999, UPB_SIZE(8, 8), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_MessageOptions_msginit = {
+  &google_protobuf_MessageOptions_submsgs[0],
+  &google_protobuf_MessageOptions__fields[0],
+  UPB_SIZE(12, 16), 5, false,
+};
+
+static const upb_msglayout *const google_protobuf_FieldOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_FieldOptions__fields[7] = {
+  {1, UPB_SIZE(8, 8), 1, 0, 14, 1},
+  {2, UPB_SIZE(24, 24), 3, 0, 8, 1},
+  {3, UPB_SIZE(25, 25), 4, 0, 8, 1},
+  {5, UPB_SIZE(26, 26), 5, 0, 8, 1},
+  {6, UPB_SIZE(16, 16), 2, 0, 14, 1},
+  {10, UPB_SIZE(27, 27), 6, 0, 8, 1},
+  {999, UPB_SIZE(28, 32), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_FieldOptions_msginit = {
+  &google_protobuf_FieldOptions_submsgs[0],
+  &google_protobuf_FieldOptions__fields[0],
+  UPB_SIZE(32, 40), 7, false,
+};
+
+static const upb_msglayout *const google_protobuf_OneofOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_OneofOptions__fields[1] = {
+  {999, UPB_SIZE(0, 0), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_OneofOptions_msginit = {
+  &google_protobuf_OneofOptions_submsgs[0],
+  &google_protobuf_OneofOptions__fields[0],
+  UPB_SIZE(4, 8), 1, false,
+};
+
+static const upb_msglayout *const google_protobuf_EnumOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_EnumOptions__fields[3] = {
+  {2, UPB_SIZE(1, 1), 1, 0, 8, 1},
+  {3, UPB_SIZE(2, 2), 2, 0, 8, 1},
+  {999, UPB_SIZE(4, 8), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_EnumOptions_msginit = {
+  &google_protobuf_EnumOptions_submsgs[0],
+  &google_protobuf_EnumOptions__fields[0],
+  UPB_SIZE(8, 16), 3, false,
+};
+
+static const upb_msglayout *const google_protobuf_EnumValueOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_EnumValueOptions__fields[2] = {
+  {1, UPB_SIZE(1, 1), 1, 0, 8, 1},
+  {999, UPB_SIZE(4, 8), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_EnumValueOptions_msginit = {
+  &google_protobuf_EnumValueOptions_submsgs[0],
+  &google_protobuf_EnumValueOptions__fields[0],
+  UPB_SIZE(8, 16), 2, false,
+};
+
+static const upb_msglayout *const google_protobuf_ServiceOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_ServiceOptions__fields[2] = {
+  {33, UPB_SIZE(1, 1), 1, 0, 8, 1},
+  {999, UPB_SIZE(4, 8), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_ServiceOptions_msginit = {
+  &google_protobuf_ServiceOptions_submsgs[0],
+  &google_protobuf_ServiceOptions__fields[0],
+  UPB_SIZE(8, 16), 2, false,
+};
+
+static const upb_msglayout *const google_protobuf_MethodOptions_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_MethodOptions__fields[3] = {
+  {33, UPB_SIZE(16, 16), 2, 0, 8, 1},
+  {34, UPB_SIZE(8, 8), 1, 0, 14, 1},
+  {999, UPB_SIZE(20, 24), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_MethodOptions_msginit = {
+  &google_protobuf_MethodOptions_submsgs[0],
+  &google_protobuf_MethodOptions__fields[0],
+  UPB_SIZE(24, 32), 3, false,
+};
+
+static const upb_msglayout *const google_protobuf_UninterpretedOption_submsgs[1] = {
+  &google_protobuf_UninterpretedOption_NamePart_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_UninterpretedOption__fields[7] = {
+  {2, UPB_SIZE(56, 80), 0, 0, 11, 3},
+  {3, UPB_SIZE(32, 32), 4, 0, 9, 1},
+  {4, UPB_SIZE(8, 8), 1, 0, 4, 1},
+  {5, UPB_SIZE(16, 16), 2, 0, 3, 1},
+  {6, UPB_SIZE(24, 24), 3, 0, 1, 1},
+  {7, UPB_SIZE(40, 48), 5, 0, 12, 1},
+  {8, UPB_SIZE(48, 64), 6, 0, 9, 1},
+};
+
+const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
+  &google_protobuf_UninterpretedOption_submsgs[0],
+  &google_protobuf_UninterpretedOption__fields[0],
+  UPB_SIZE(64, 96), 7, false,
+};
+
+static const upb_msglayout_field google_protobuf_UninterpretedOption_NamePart__fields[2] = {
+  {1, UPB_SIZE(4, 8), 2, 0, 9, 2},
+  {2, UPB_SIZE(1, 1), 1, 0, 8, 2},
+};
+
+const upb_msglayout google_protobuf_UninterpretedOption_NamePart_msginit = {
+  NULL,
+  &google_protobuf_UninterpretedOption_NamePart__fields[0],
+  UPB_SIZE(16, 32), 2, false,
+};
+
+static const upb_msglayout *const google_protobuf_SourceCodeInfo_submsgs[1] = {
+  &google_protobuf_SourceCodeInfo_Location_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_SourceCodeInfo__fields[1] = {
+  {1, UPB_SIZE(0, 0), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_SourceCodeInfo_msginit = {
+  &google_protobuf_SourceCodeInfo_submsgs[0],
+  &google_protobuf_SourceCodeInfo__fields[0],
+  UPB_SIZE(4, 8), 1, false,
+};
+
+static const upb_msglayout_field google_protobuf_SourceCodeInfo_Location__fields[5] = {
+  {1, UPB_SIZE(20, 40), 0, 0, 5, _UPB_LABEL_PACKED},
+  {2, UPB_SIZE(24, 48), 0, 0, 5, _UPB_LABEL_PACKED},
+  {3, UPB_SIZE(4, 8), 1, 0, 9, 1},
+  {4, UPB_SIZE(12, 24), 2, 0, 9, 1},
+  {6, UPB_SIZE(28, 56), 0, 0, 9, 3},
+};
+
+const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
+  NULL,
+  &google_protobuf_SourceCodeInfo_Location__fields[0],
+  UPB_SIZE(32, 64), 5, false,
+};
+
+static const upb_msglayout *const google_protobuf_GeneratedCodeInfo_submsgs[1] = {
+  &google_protobuf_GeneratedCodeInfo_Annotation_msginit,
+};
+
+static const upb_msglayout_field google_protobuf_GeneratedCodeInfo__fields[1] = {
+  {1, UPB_SIZE(0, 0), 0, 0, 11, 3},
+};
+
+const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit = {
+  &google_protobuf_GeneratedCodeInfo_submsgs[0],
+  &google_protobuf_GeneratedCodeInfo__fields[0],
+  UPB_SIZE(4, 8), 1, false,
+};
+
+static const upb_msglayout_field google_protobuf_GeneratedCodeInfo_Annotation__fields[4] = {
+  {1, UPB_SIZE(20, 32), 0, 0, 5, _UPB_LABEL_PACKED},
+  {2, UPB_SIZE(12, 16), 3, 0, 9, 1},
+  {3, UPB_SIZE(4, 4), 1, 0, 5, 1},
+  {4, UPB_SIZE(8, 8), 2, 0, 5, 1},
+};
+
+const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit = {
+  NULL,
+  &google_protobuf_GeneratedCodeInfo_Annotation__fields[0],
+  UPB_SIZE(24, 48), 4, false,
+};
+
+
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     google/protobuf/descriptor.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+
+extern const upb_msglayout google_protobuf_FileDescriptorSet_msginit;
+extern const upb_msglayout google_protobuf_FileDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_DescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_DescriptorProto_ExtensionRange_msginit;
+extern const upb_msglayout google_protobuf_DescriptorProto_ReservedRange_msginit;
+extern const upb_msglayout google_protobuf_ExtensionRangeOptions_msginit;
+extern const upb_msglayout google_protobuf_FieldDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_OneofDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_EnumDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit;
+extern const upb_msglayout google_protobuf_EnumValueDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_ServiceDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_MethodDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_FileOptions_msginit;
+extern const upb_msglayout google_protobuf_MessageOptions_msginit;
+extern const upb_msglayout google_protobuf_FieldOptions_msginit;
+extern const upb_msglayout google_protobuf_OneofOptions_msginit;
+extern const upb_msglayout google_protobuf_EnumOptions_msginit;
+extern const upb_msglayout google_protobuf_EnumValueOptions_msginit;
+extern const upb_msglayout google_protobuf_ServiceOptions_msginit;
+extern const upb_msglayout google_protobuf_MethodOptions_msginit;
+extern const upb_msglayout google_protobuf_UninterpretedOption_msginit;
+extern const upb_msglayout google_protobuf_UninterpretedOption_NamePart_msginit;
+extern const upb_msglayout google_protobuf_SourceCodeInfo_msginit;
+extern const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit;
+extern const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit;
+extern const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit;
+
+static const upb_msglayout *layouts[27] = {
+  &google_protobuf_FileDescriptorSet_msginit,
+  &google_protobuf_FileDescriptorProto_msginit,
+  &google_protobuf_DescriptorProto_msginit,
+  &google_protobuf_DescriptorProto_ExtensionRange_msginit,
+  &google_protobuf_DescriptorProto_ReservedRange_msginit,
+  &google_protobuf_ExtensionRangeOptions_msginit,
+  &google_protobuf_FieldDescriptorProto_msginit,
+  &google_protobuf_OneofDescriptorProto_msginit,
+  &google_protobuf_EnumDescriptorProto_msginit,
+  &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit,
+  &google_protobuf_EnumValueDescriptorProto_msginit,
+  &google_protobuf_ServiceDescriptorProto_msginit,
+  &google_protobuf_MethodDescriptorProto_msginit,
+  &google_protobuf_FileOptions_msginit,
+  &google_protobuf_MessageOptions_msginit,
+  &google_protobuf_FieldOptions_msginit,
+  &google_protobuf_OneofOptions_msginit,
+  &google_protobuf_EnumOptions_msginit,
+  &google_protobuf_EnumValueOptions_msginit,
+  &google_protobuf_ServiceOptions_msginit,
+  &google_protobuf_MethodOptions_msginit,
+  &google_protobuf_UninterpretedOption_msginit,
+  &google_protobuf_UninterpretedOption_NamePart_msginit,
+  &google_protobuf_SourceCodeInfo_msginit,
+  &google_protobuf_SourceCodeInfo_Location_msginit,
+  &google_protobuf_GeneratedCodeInfo_msginit,
+  &google_protobuf_GeneratedCodeInfo_Annotation_msginit,
+};
+
+static const char descriptor[7619] = {'\n', ' ', 'g', 'o', 'o', 'g', 'l', 'e', '/', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '/', 'd', 'e', 's', 'c', 'r', 'i', 'p', 
+'t', 'o', 'r', '.', 'p', 'r', 'o', 't', 'o', '\022', '\017', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 
+'f', '\"', 'M', '\n', '\021', 'F', 'i', 'l', 'e', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'S', 'e', 't', '\022', '8', '\n', 
+'\004', 'f', 'i', 'l', 'e', '\030', '\001', ' ', '\003', '(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 
+'o', 'b', 'u', 'f', '.', 'F', 'i', 'l', 'e', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', 
+'\004', 'f', 'i', 'l', 'e', '\"', '\344', '\004', '\n', '\023', 'F', 'i', 'l', 'e', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 
+'r', 'o', 't', 'o', '\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', 
+'\030', '\n', '\007', 'p', 'a', 'c', 'k', 'a', 'g', 'e', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\007', 'p', 'a', 'c', 'k', 'a', 'g', 'e', 
+'\022', '\036', '\n', '\n', 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'y', '\030', '\003', ' ', '\003', '(', '\t', 'R', '\n', 'd', 'e', 'p', 
+'e', 'n', 'd', 'e', 'n', 'c', 'y', '\022', '+', '\n', '\021', 'p', 'u', 'b', 'l', 'i', 'c', '_', 'd', 'e', 'p', 'e', 'n', 'd', 'e', 
+'n', 'c', 'y', '\030', '\n', ' ', '\003', '(', '\005', 'R', '\020', 'p', 'u', 'b', 'l', 'i', 'c', 'D', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 
+'c', 'y', '\022', '\'', '\n', '\017', 'w', 'e', 'a', 'k', '_', 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'y', '\030', '\013', ' ', '\003', 
+'(', '\005', 'R', '\016', 'w', 'e', 'a', 'k', 'D', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'y', '\022', 'C', '\n', '\014', 'm', 'e', 's', 
+'s', 'a', 'g', 'e', '_', 't', 'y', 'p', 'e', '\030', '\004', ' ', '\003', '(', '\013', '2', ' ', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 
+'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', 
+'\013', 'm', 'e', 's', 's', 'a', 'g', 'e', 'T', 'y', 'p', 'e', '\022', 'A', '\n', '\t', 'e', 'n', 'u', 'm', '_', 't', 'y', 'p', 'e', 
+'\030', '\005', ' ', '\003', '(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 
+'E', 'n', 'u', 'm', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\010', 'e', 'n', 'u', 'm', 
+'T', 'y', 'p', 'e', '\022', 'A', '\n', '\007', 's', 'e', 'r', 'v', 'i', 'c', 'e', '\030', '\006', ' ', '\003', '(', '\013', '2', '\'', '.', 'g', 
+'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 'D', 'e', 's', 
+'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\007', 's', 'e', 'r', 'v', 'i', 'c', 'e', '\022', 'C', '\n', '\t', 
+'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\030', '\007', ' ', '\003', '(', '\013', '2', '%', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 
+'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 
+'r', 'o', 't', 'o', 'R', '\t', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\022', '6', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 
+'s', '\030', '\010', ' ', '\001', '(', '\013', '2', '\034', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', 
+'.', 'F', 'i', 'l', 'e', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\022', 'I', '\n', '\020', 
+'s', 'o', 'u', 'r', 'c', 'e', '_', 'c', 'o', 'd', 'e', '_', 'i', 'n', 'f', 'o', '\030', '\t', ' ', '\001', '(', '\013', '2', '\037', '.', 
+'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'S', 'o', 'u', 'r', 'c', 'e', 'C', 'o', 'd', 
+'e', 'I', 'n', 'f', 'o', 'R', '\016', 's', 'o', 'u', 'r', 'c', 'e', 'C', 'o', 'd', 'e', 'I', 'n', 'f', 'o', '\022', '\026', '\n', '\006', 
+'s', 'y', 'n', 't', 'a', 'x', '\030', '\014', ' ', '\001', '(', '\t', 'R', '\006', 's', 'y', 'n', 't', 'a', 'x', '\"', '\271', '\006', '\n', '\017', 
+'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', 
+' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', ';', '\n', '\005', 'f', 'i', 'e', 'l', 'd', '\030', '\002', ' ', '\003', '(', '\013', 
+'2', '%', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 'd', 'D', 
+'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\005', 'f', 'i', 'e', 'l', 'd', '\022', 'C', '\n', '\t', 
+'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\030', '\006', ' ', '\003', '(', '\013', '2', '%', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 
+'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 
+'r', 'o', 't', 'o', 'R', '\t', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\022', 'A', '\n', '\013', 'n', 'e', 's', 't', 'e', 'd', 
+'_', 't', 'y', 'p', 'e', '\030', '\003', ' ', '\003', '(', '\013', '2', ' ', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 
+'o', 'b', 'u', 'f', '.', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\n', 'n', 'e', 's', 
+'t', 'e', 'd', 'T', 'y', 'p', 'e', '\022', 'A', '\n', '\t', 'e', 'n', 'u', 'm', '_', 't', 'y', 'p', 'e', '\030', '\004', ' ', '\003', '(', 
+'\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'E', 'n', 'u', 'm', 'D', 
+'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\010', 'e', 'n', 'u', 'm', 'T', 'y', 'p', 'e', '\022', 
+'X', '\n', '\017', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '_', 'r', 'a', 'n', 'g', 'e', '\030', '\005', ' ', '\003', '(', '\013', '2', 
+'/', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'D', 'e', 's', 'c', 'r', 'i', 'p', 
+'t', 'o', 'r', 'P', 'r', 'o', 't', 'o', '.', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'R', 'a', 'n', 'g', 'e', 'R', '\016', 
+'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'R', 'a', 'n', 'g', 'e', '\022', 'D', '\n', '\n', 'o', 'n', 'e', 'o', 'f', '_', 'd', 
+'e', 'c', 'l', '\030', '\010', ' ', '\003', '(', '\013', '2', '%', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 
+'u', 'f', '.', 'O', 'n', 'e', 'o', 'f', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\t', 
+'o', 'n', 'e', 'o', 'f', 'D', 'e', 'c', 'l', '\022', '9', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '\007', ' ', '\001', '(', 
+'\013', '2', '\037', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'M', 'e', 's', 's', 'a', 
+'g', 'e', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\022', 'U', '\n', '\016', 'r', 'e', 's', 
+'e', 'r', 'v', 'e', 'd', '_', 'r', 'a', 'n', 'g', 'e', '\030', '\t', ' ', '\003', '(', '\013', '2', '.', '.', 'g', 'o', 'o', 'g', 'l', 
+'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 
+'o', '.', 'R', 'e', 's', 'e', 'r', 'v', 'e', 'd', 'R', 'a', 'n', 'g', 'e', 'R', '\r', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd', 
+'R', 'a', 'n', 'g', 'e', '\022', '#', '\n', '\r', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd', '_', 'n', 'a', 'm', 'e', '\030', '\n', ' ', 
+'\003', '(', '\t', 'R', '\014', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd', 'N', 'a', 'm', 'e', '\032', 'z', '\n', '\016', 'E', 'x', 't', 'e', 
+'n', 's', 'i', 'o', 'n', 'R', 'a', 'n', 'g', 'e', '\022', '\024', '\n', '\005', 's', 't', 'a', 'r', 't', '\030', '\001', ' ', '\001', '(', '\005', 
+'R', '\005', 's', 't', 'a', 'r', 't', '\022', '\020', '\n', '\003', 'e', 'n', 'd', '\030', '\002', ' ', '\001', '(', '\005', 'R', '\003', 'e', 'n', 'd', 
+'\022', '@', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '\003', ' ', '\001', '(', '\013', '2', '&', '.', 'g', 'o', 'o', 'g', 'l', 
+'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'R', 'a', 'n', 'g', 'e', 
+'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\032', '7', '\n', '\r', 'R', 'e', 's', 'e', 'r', 
+'v', 'e', 'd', 'R', 'a', 'n', 'g', 'e', '\022', '\024', '\n', '\005', 's', 't', 'a', 'r', 't', '\030', '\001', ' ', '\001', '(', '\005', 'R', '\005', 
+'s', 't', 'a', 'r', 't', '\022', '\020', '\n', '\003', 'e', 'n', 'd', '\030', '\002', ' ', '\001', '(', '\005', 'R', '\003', 'e', 'n', 'd', '\"', '|', 
+'\n', '\025', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', 'R', 'a', 'n', 'g', 'e', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', 'X', 
+'\n', '\024', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', 
+' ', '\003', '(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 
+'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 
+'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '*', '\t', '\010', '\350', '\007', '\020', '\200', '\200', '\200', '\200', '\002', '\"', 
+'\301', '\006', '\n', '\024', 'F', 'i', 'e', 'l', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '\022', 
+'\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '\026', '\n', '\006', 'n', 'u', 
+'m', 'b', 'e', 'r', '\030', '\003', ' ', '\001', '(', '\005', 'R', '\006', 'n', 'u', 'm', 'b', 'e', 'r', '\022', 'A', '\n', '\005', 'l', 'a', 'b', 
+'e', 'l', '\030', '\004', ' ', '\001', '(', '\016', '2', '+', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 
+'f', '.', 'F', 'i', 'e', 'l', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '.', 'L', 'a', 
+'b', 'e', 'l', 'R', '\005', 'l', 'a', 'b', 'e', 'l', '\022', '>', '\n', '\004', 't', 'y', 'p', 'e', '\030', '\005', ' ', '\001', '(', '\016', '2', 
+'*', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 'd', 'D', 'e', 
+'s', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '.', 'T', 'y', 'p', 'e', 'R', '\004', 't', 'y', 'p', 'e', '\022', 
+'\033', '\n', '\t', 't', 'y', 'p', 'e', '_', 'n', 'a', 'm', 'e', '\030', '\006', ' ', '\001', '(', '\t', 'R', '\010', 't', 'y', 'p', 'e', 'N', 
+'a', 'm', 'e', '\022', '\032', '\n', '\010', 'e', 'x', 't', 'e', 'n', 'd', 'e', 'e', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\010', 'e', 'x', 
+'t', 'e', 'n', 'd', 'e', 'e', '\022', '#', '\n', '\r', 'd', 'e', 'f', 'a', 'u', 'l', 't', '_', 'v', 'a', 'l', 'u', 'e', '\030', '\007', 
+' ', '\001', '(', '\t', 'R', '\014', 'd', 'e', 'f', 'a', 'u', 'l', 't', 'V', 'a', 'l', 'u', 'e', '\022', '\037', '\n', '\013', 'o', 'n', 'e', 
+'o', 'f', '_', 'i', 'n', 'd', 'e', 'x', '\030', '\t', ' ', '\001', '(', '\005', 'R', '\n', 'o', 'n', 'e', 'o', 'f', 'I', 'n', 'd', 'e', 
+'x', '\022', '\033', '\n', '\t', 'j', 's', 'o', 'n', '_', 'n', 'a', 'm', 'e', '\030', '\n', ' ', '\001', '(', '\t', 'R', '\010', 'j', 's', 'o', 
+'n', 'N', 'a', 'm', 'e', '\022', '7', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '\010', ' ', '\001', '(', '\013', '2', '\035', '.', 
+'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 'd', 'O', 'p', 't', 'i', 
+'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\022', '\'', '\n', '\017', 'p', 'r', 'o', 't', 'o', '3', '_', 'o', 'p', 
+'t', 'i', 'o', 'n', 'a', 'l', '\030', '\021', ' ', '\001', '(', '\010', 'R', '\016', 'p', 'r', 'o', 't', 'o', '3', 'O', 'p', 't', 'i', 'o', 
+'n', 'a', 'l', '\"', '\266', '\002', '\n', '\004', 'T', 'y', 'p', 'e', '\022', '\017', '\n', '\013', 'T', 'Y', 'P', 'E', '_', 'D', 'O', 'U', 'B', 
+'L', 'E', '\020', '\001', '\022', '\016', '\n', '\n', 'T', 'Y', 'P', 'E', '_', 'F', 'L', 'O', 'A', 'T', '\020', '\002', '\022', '\016', '\n', '\n', 'T', 
+'Y', 'P', 'E', '_', 'I', 'N', 'T', '6', '4', '\020', '\003', '\022', '\017', '\n', '\013', 'T', 'Y', 'P', 'E', '_', 'U', 'I', 'N', 'T', '6', 
+'4', '\020', '\004', '\022', '\016', '\n', '\n', 'T', 'Y', 'P', 'E', '_', 'I', 'N', 'T', '3', '2', '\020', '\005', '\022', '\020', '\n', '\014', 'T', 'Y', 
+'P', 'E', '_', 'F', 'I', 'X', 'E', 'D', '6', '4', '\020', '\006', '\022', '\020', '\n', '\014', 'T', 'Y', 'P', 'E', '_', 'F', 'I', 'X', 'E', 
+'D', '3', '2', '\020', '\007', '\022', '\r', '\n', '\t', 'T', 'Y', 'P', 'E', '_', 'B', 'O', 'O', 'L', '\020', '\010', '\022', '\017', '\n', '\013', 'T', 
+'Y', 'P', 'E', '_', 'S', 'T', 'R', 'I', 'N', 'G', '\020', '\t', '\022', '\016', '\n', '\n', 'T', 'Y', 'P', 'E', '_', 'G', 'R', 'O', 'U', 
+'P', '\020', '\n', '\022', '\020', '\n', '\014', 'T', 'Y', 'P', 'E', '_', 'M', 'E', 'S', 'S', 'A', 'G', 'E', '\020', '\013', '\022', '\016', '\n', '\n', 
+'T', 'Y', 'P', 'E', '_', 'B', 'Y', 'T', 'E', 'S', '\020', '\014', '\022', '\017', '\n', '\013', 'T', 'Y', 'P', 'E', '_', 'U', 'I', 'N', 'T', 
+'3', '2', '\020', '\r', '\022', '\r', '\n', '\t', 'T', 'Y', 'P', 'E', '_', 'E', 'N', 'U', 'M', '\020', '\016', '\022', '\021', '\n', '\r', 'T', 'Y', 
+'P', 'E', '_', 'S', 'F', 'I', 'X', 'E', 'D', '3', '2', '\020', '\017', '\022', '\021', '\n', '\r', 'T', 'Y', 'P', 'E', '_', 'S', 'F', 'I', 
+'X', 'E', 'D', '6', '4', '\020', '\020', '\022', '\017', '\n', '\013', 'T', 'Y', 'P', 'E', '_', 'S', 'I', 'N', 'T', '3', '2', '\020', '\021', '\022', 
+'\017', '\n', '\013', 'T', 'Y', 'P', 'E', '_', 'S', 'I', 'N', 'T', '6', '4', '\020', '\022', '\"', 'C', '\n', '\005', 'L', 'a', 'b', 'e', 'l', 
+'\022', '\022', '\n', '\016', 'L', 'A', 'B', 'E', 'L', '_', 'O', 'P', 'T', 'I', 'O', 'N', 'A', 'L', '\020', '\001', '\022', '\022', '\n', '\016', 'L', 
+'A', 'B', 'E', 'L', '_', 'R', 'E', 'Q', 'U', 'I', 'R', 'E', 'D', '\020', '\002', '\022', '\022', '\n', '\016', 'L', 'A', 'B', 'E', 'L', '_', 
+'R', 'E', 'P', 'E', 'A', 'T', 'E', 'D', '\020', '\003', '\"', 'c', '\n', '\024', 'O', 'n', 'e', 'o', 'f', 'D', 'e', 's', 'c', 'r', 'i', 
+'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 
+'n', 'a', 'm', 'e', '\022', '7', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '\002', ' ', '\001', '(', '\013', '2', '\035', '.', 'g', 
+'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'O', 'n', 'e', 'o', 'f', 'O', 'p', 't', 'i', 'o', 
+'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\"', '\343', '\002', '\n', '\023', 'E', 'n', 'u', 'm', 'D', 'e', 's', 'c', 'r', 
+'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', 
+'\004', 'n', 'a', 'm', 'e', '\022', '?', '\n', '\005', 'v', 'a', 'l', 'u', 'e', '\030', '\002', ' ', '\003', '(', '\013', '2', ')', '.', 'g', 'o', 
+'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'E', 'n', 'u', 'm', 'V', 'a', 'l', 'u', 'e', 'D', 'e', 
+'s', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 'R', '\005', 'v', 'a', 'l', 'u', 'e', '\022', '6', '\n', '\007', 'o', 
+'p', 't', 'i', 'o', 'n', 's', '\030', '\003', ' ', '\001', '(', '\013', '2', '\034', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 
+'t', 'o', 'b', 'u', 'f', '.', 'E', 'n', 'u', 'm', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 
+'s', '\022', ']', '\n', '\016', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd', '_', 'r', 'a', 'n', 'g', 'e', '\030', '\004', ' ', '\003', '(', '\013', 
+'2', '6', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'E', 'n', 'u', 'm', 'D', 'e', 
+'s', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '.', 'E', 'n', 'u', 'm', 'R', 'e', 's', 'e', 'r', 'v', 'e', 
+'d', 'R', 'a', 'n', 'g', 'e', 'R', '\r', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd', 'R', 'a', 'n', 'g', 'e', '\022', '#', '\n', '\r', 
+'r', 'e', 's', 'e', 'r', 'v', 'e', 'd', '_', 'n', 'a', 'm', 'e', '\030', '\005', ' ', '\003', '(', '\t', 'R', '\014', 'r', 'e', 's', 'e', 
+'r', 'v', 'e', 'd', 'N', 'a', 'm', 'e', '\032', ';', '\n', '\021', 'E', 'n', 'u', 'm', 'R', 'e', 's', 'e', 'r', 'v', 'e', 'd', 'R', 
+'a', 'n', 'g', 'e', '\022', '\024', '\n', '\005', 's', 't', 'a', 'r', 't', '\030', '\001', ' ', '\001', '(', '\005', 'R', '\005', 's', 't', 'a', 'r', 
+'t', '\022', '\020', '\n', '\003', 'e', 'n', 'd', '\030', '\002', ' ', '\001', '(', '\005', 'R', '\003', 'e', 'n', 'd', '\"', '\203', '\001', '\n', '\030', 'E', 
+'n', 'u', 'm', 'V', 'a', 'l', 'u', 'e', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '\022', '\022', 
+'\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '\026', '\n', '\006', 'n', 'u', 'm', 
+'b', 'e', 'r', '\030', '\002', ' ', '\001', '(', '\005', 'R', '\006', 'n', 'u', 'm', 'b', 'e', 'r', '\022', ';', '\n', '\007', 'o', 'p', 't', 'i', 
+'o', 'n', 's', '\030', '\003', ' ', '\001', '(', '\013', '2', '!', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 
+'u', 'f', '.', 'E', 'n', 'u', 'm', 'V', 'a', 'l', 'u', 'e', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 
+'o', 'n', 's', '\"', '\247', '\001', '\n', '\026', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 
+'P', 'r', 'o', 't', 'o', '\022', '\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', 
+'\022', '>', '\n', '\006', 'm', 'e', 't', 'h', 'o', 'd', '\030', '\002', ' ', '\003', '(', '\013', '2', '&', '.', 'g', 'o', 'o', 'g', 'l', 'e', 
+'.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'M', 'e', 't', 'h', 'o', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 
+'r', 'P', 'r', 'o', 't', 'o', 'R', '\006', 'm', 'e', 't', 'h', 'o', 'd', '\022', '9', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', 
+'\030', '\003', ' ', '\001', '(', '\013', '2', '\037', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 
+'S', 'e', 'r', 'v', 'i', 'c', 'e', 'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\"', '\211', 
+'\002', '\n', '\025', 'M', 'e', 't', 'h', 'o', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', '\022', 
+'\022', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\001', ' ', '\001', '(', '\t', 'R', '\004', 'n', 'a', 'm', 'e', '\022', '\035', '\n', '\n', 'i', 'n', 
+'p', 'u', 't', '_', 't', 'y', 'p', 'e', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\t', 'i', 'n', 'p', 'u', 't', 'T', 'y', 'p', 'e', 
+'\022', '\037', '\n', '\013', 'o', 'u', 't', 'p', 'u', 't', '_', 't', 'y', 'p', 'e', '\030', '\003', ' ', '\001', '(', '\t', 'R', '\n', 'o', 'u', 
+'t', 'p', 'u', 't', 'T', 'y', 'p', 'e', '\022', '8', '\n', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\030', '\004', ' ', '\001', '(', '\013', 
+'2', '\036', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'M', 'e', 't', 'h', 'o', 'd', 
+'O', 'p', 't', 'i', 'o', 'n', 's', 'R', '\007', 'o', 'p', 't', 'i', 'o', 'n', 's', '\022', '0', '\n', '\020', 'c', 'l', 'i', 'e', 'n', 
+'t', '_', 's', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g', '\030', '\005', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', 
+'\017', 'c', 'l', 'i', 'e', 'n', 't', 'S', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g', '\022', '0', '\n', '\020', 's', 'e', 'r', 'v', 'e', 
+'r', '_', 's', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g', '\030', '\006', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', 
+'\017', 's', 'e', 'r', 'v', 'e', 'r', 'S', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g', '\"', '\221', '\t', '\n', '\013', 'F', 'i', 'l', 'e', 
+'O', 'p', 't', 'i', 'o', 'n', 's', '\022', '!', '\n', '\014', 'j', 'a', 'v', 'a', '_', 'p', 'a', 'c', 'k', 'a', 'g', 'e', '\030', '\001', 
+' ', '\001', '(', '\t', 'R', '\013', 'j', 'a', 'v', 'a', 'P', 'a', 'c', 'k', 'a', 'g', 'e', '\022', '0', '\n', '\024', 'j', 'a', 'v', 'a', 
+'_', 'o', 'u', 't', 'e', 'r', '_', 'c', 'l', 'a', 's', 's', 'n', 'a', 'm', 'e', '\030', '\010', ' ', '\001', '(', '\t', 'R', '\022', 'j', 
+'a', 'v', 'a', 'O', 'u', 't', 'e', 'r', 'C', 'l', 'a', 's', 's', 'n', 'a', 'm', 'e', '\022', '5', '\n', '\023', 'j', 'a', 'v', 'a', 
+'_', 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', '_', 'f', 'i', 'l', 'e', 's', '\030', '\n', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 
+'l', 's', 'e', 'R', '\021', 'j', 'a', 'v', 'a', 'M', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'F', 'i', 'l', 'e', 's', '\022', 'D', '\n', 
+'\035', 'j', 'a', 'v', 'a', '_', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', '_', 'e', 'q', 'u', 'a', 'l', 's', '_', 'a', 'n', 'd', 
+'_', 'h', 'a', 's', 'h', '\030', '\024', ' ', '\001', '(', '\010', 'B', '\002', '\030', '\001', 'R', '\031', 'j', 'a', 'v', 'a', 'G', 'e', 'n', 'e', 
+'r', 'a', 't', 'e', 'E', 'q', 'u', 'a', 'l', 's', 'A', 'n', 'd', 'H', 'a', 's', 'h', '\022', ':', '\n', '\026', 'j', 'a', 'v', 'a', 
+'_', 's', 't', 'r', 'i', 'n', 'g', '_', 'c', 'h', 'e', 'c', 'k', '_', 'u', 't', 'f', '8', '\030', '\033', ' ', '\001', '(', '\010', ':', 
+'\005', 'f', 'a', 'l', 's', 'e', 'R', '\023', 'j', 'a', 'v', 'a', 'S', 't', 'r', 'i', 'n', 'g', 'C', 'h', 'e', 'c', 'k', 'U', 't', 
+'f', '8', '\022', 'S', '\n', '\014', 'o', 'p', 't', 'i', 'm', 'i', 'z', 'e', '_', 'f', 'o', 'r', '\030', '\t', ' ', '\001', '(', '\016', '2', 
+')', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'l', 'e', 'O', 'p', 't', 
+'i', 'o', 'n', 's', '.', 'O', 'p', 't', 'i', 'm', 'i', 'z', 'e', 'M', 'o', 'd', 'e', ':', '\005', 'S', 'P', 'E', 'E', 'D', 'R', 
+'\013', 'o', 'p', 't', 'i', 'm', 'i', 'z', 'e', 'F', 'o', 'r', '\022', '\035', '\n', '\n', 'g', 'o', '_', 'p', 'a', 'c', 'k', 'a', 'g', 
+'e', '\030', '\013', ' ', '\001', '(', '\t', 'R', '\t', 'g', 'o', 'P', 'a', 'c', 'k', 'a', 'g', 'e', '\022', '5', '\n', '\023', 'c', 'c', '_', 
+'g', 'e', 'n', 'e', 'r', 'i', 'c', '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '\030', '\020', ' ', '\001', '(', '\010', ':', '\005', 'f', 
+'a', 'l', 's', 'e', 'R', '\021', 'c', 'c', 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 's', '\022', '9', 
+'\n', '\025', 'j', 'a', 'v', 'a', '_', 'g', 'e', 'n', 'e', 'r', 'i', 'c', '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '\030', '\021', 
+' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\023', 'j', 'a', 'v', 'a', 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 
+'e', 'r', 'v', 'i', 'c', 'e', 's', '\022', '5', '\n', '\023', 'p', 'y', '_', 'g', 'e', 'n', 'e', 'r', 'i', 'c', '_', 's', 'e', 'r', 
+'v', 'i', 'c', 'e', 's', '\030', '\022', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\021', 'p', 'y', 'G', 'e', 'n', 
+'e', 'r', 'i', 'c', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 's', '\022', '7', '\n', '\024', 'p', 'h', 'p', '_', 'g', 'e', 'n', 'e', 'r', 
+'i', 'c', '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '\030', '*', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', 
+'\022', 'p', 'h', 'p', 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 's', '\022', '%', '\n', '\n', 'd', 'e', 
+'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\030', '\027', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\n', 'd', 'e', 
+'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\022', '.', '\n', '\020', 'c', 'c', '_', 'e', 'n', 'a', 'b', 'l', 'e', '_', 'a', 'r', 'e', 
+'n', 'a', 's', '\030', '\037', ' ', '\001', '(', '\010', ':', '\004', 't', 'r', 'u', 'e', 'R', '\016', 'c', 'c', 'E', 'n', 'a', 'b', 'l', 'e', 
+'A', 'r', 'e', 'n', 'a', 's', '\022', '*', '\n', '\021', 'o', 'b', 'j', 'c', '_', 'c', 'l', 'a', 's', 's', '_', 'p', 'r', 'e', 'f', 
+'i', 'x', '\030', '$', ' ', '\001', '(', '\t', 'R', '\017', 'o', 'b', 'j', 'c', 'C', 'l', 'a', 's', 's', 'P', 'r', 'e', 'f', 'i', 'x', 
+'\022', ')', '\n', '\020', 'c', 's', 'h', 'a', 'r', 'p', '_', 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\030', '%', ' ', '\001', '(', 
+'\t', 'R', '\017', 'c', 's', 'h', 'a', 'r', 'p', 'N', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\022', '!', '\n', '\014', 's', 'w', 'i', 
+'f', 't', '_', 'p', 'r', 'e', 'f', 'i', 'x', '\030', '\'', ' ', '\001', '(', '\t', 'R', '\013', 's', 'w', 'i', 'f', 't', 'P', 'r', 'e', 
+'f', 'i', 'x', '\022', '(', '\n', '\020', 'p', 'h', 'p', '_', 'c', 'l', 'a', 's', 's', '_', 'p', 'r', 'e', 'f', 'i', 'x', '\030', '(', 
+' ', '\001', '(', '\t', 'R', '\016', 'p', 'h', 'p', 'C', 'l', 'a', 's', 's', 'P', 'r', 'e', 'f', 'i', 'x', '\022', '#', '\n', '\r', 'p', 
+'h', 'p', '_', 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\030', ')', ' ', '\001', '(', '\t', 'R', '\014', 'p', 'h', 'p', 'N', 'a', 
+'m', 'e', 's', 'p', 'a', 'c', 'e', '\022', '4', '\n', '\026', 'p', 'h', 'p', '_', 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '_', 'n', 
+'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\030', ',', ' ', '\001', '(', '\t', 'R', '\024', 'p', 'h', 'p', 'M', 'e', 't', 'a', 'd', 'a', 
+'t', 'a', 'N', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\022', '!', '\n', '\014', 'r', 'u', 'b', 'y', '_', 'p', 'a', 'c', 'k', 'a', 
+'g', 'e', '\030', '-', ' ', '\001', '(', '\t', 'R', '\013', 'r', 'u', 'b', 'y', 'P', 'a', 'c', 'k', 'a', 'g', 'e', '\022', 'X', '\n', '\024', 
+'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', 
+'(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 
+'t', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 
+'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '\"', ':', '\n', '\014', 'O', 'p', 't', 'i', 'm', 'i', 'z', 'e', 'M', 'o', 
+'d', 'e', '\022', '\t', '\n', '\005', 'S', 'P', 'E', 'E', 'D', '\020', '\001', '\022', '\r', '\n', '\t', 'C', 'O', 'D', 'E', '_', 'S', 'I', 'Z', 
+'E', '\020', '\002', '\022', '\020', '\n', '\014', 'L', 'I', 'T', 'E', '_', 'R', 'U', 'N', 'T', 'I', 'M', 'E', '\020', '\003', '*', '\t', '\010', '\350', 
+'\007', '\020', '\200', '\200', '\200', '\200', '\002', 'J', '\004', '\010', '&', '\020', '\'', '\"', '\321', '\002', '\n', '\016', 'M', 'e', 's', 's', 'a', 'g', 'e', 
+'O', 'p', 't', 'i', 'o', 'n', 's', '\022', '<', '\n', '\027', 'm', 'e', 's', 's', 'a', 'g', 'e', '_', 's', 'e', 't', '_', 'w', 'i', 
+'r', 'e', '_', 'f', 'o', 'r', 'm', 'a', 't', '\030', '\001', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\024', 'm', 
+'e', 's', 's', 'a', 'g', 'e', 'S', 'e', 't', 'W', 'i', 'r', 'e', 'F', 'o', 'r', 'm', 'a', 't', '\022', 'L', '\n', '\037', 'n', 'o', 
+'_', 's', 't', 'a', 'n', 'd', 'a', 'r', 'd', '_', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', '_', 'a', 'c', 'c', 'e', 
+'s', 's', 'o', 'r', '\030', '\002', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\034', 'n', 'o', 'S', 't', 'a', 'n', 
+'d', 'a', 'r', 'd', 'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'A', 'c', 'c', 'e', 's', 's', 'o', 'r', '\022', '%', '\n', 
+'\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\030', '\003', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', 
+'\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\022', '\033', '\n', '\t', 'm', 'a', 'p', '_', 'e', 'n', 't', 'r', 'y', '\030', 
+'\007', ' ', '\001', '(', '\010', 'R', '\010', 'm', 'a', 'p', 'E', 'n', 't', 'r', 'y', '\022', 'X', '\n', '\024', 'u', 'n', 'i', 'n', 't', 'e', 
+'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', '2', '$', '.', 'g', 
+'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 
+'t', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 
+'p', 't', 'i', 'o', 'n', '*', '\t', '\010', '\350', '\007', '\020', '\200', '\200', '\200', '\200', '\002', 'J', '\004', '\010', '\010', '\020', '\t', 'J', '\004', '\010', 
+'\t', '\020', '\n', '\"', '\342', '\003', '\n', '\014', 'F', 'i', 'e', 'l', 'd', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', 'A', '\n', '\005', 'c', 
+'t', 'y', 'p', 'e', '\030', '\001', ' ', '\001', '(', '\016', '2', '#', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 
+'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 'd', 'O', 'p', 't', 'i', 'o', 'n', 's', '.', 'C', 'T', 'y', 'p', 'e', ':', '\006', 'S', 
+'T', 'R', 'I', 'N', 'G', 'R', '\005', 'c', 't', 'y', 'p', 'e', '\022', '\026', '\n', '\006', 'p', 'a', 'c', 'k', 'e', 'd', '\030', '\002', ' ', 
+'\001', '(', '\010', 'R', '\006', 'p', 'a', 'c', 'k', 'e', 'd', '\022', 'G', '\n', '\006', 'j', 's', 't', 'y', 'p', 'e', '\030', '\006', ' ', '\001', 
+'(', '\016', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'F', 'i', 'e', 'l', 
+'d', 'O', 'p', 't', 'i', 'o', 'n', 's', '.', 'J', 'S', 'T', 'y', 'p', 'e', ':', '\t', 'J', 'S', '_', 'N', 'O', 'R', 'M', 'A', 
+'L', 'R', '\006', 'j', 's', 't', 'y', 'p', 'e', '\022', '\031', '\n', '\004', 'l', 'a', 'z', 'y', '\030', '\005', ' ', '\001', '(', '\010', ':', '\005', 
+'f', 'a', 'l', 's', 'e', 'R', '\004', 'l', 'a', 'z', 'y', '\022', '%', '\n', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', 
+'\030', '\003', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', 
+'\022', '\031', '\n', '\004', 'w', 'e', 'a', 'k', '\030', '\n', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\004', 'w', 'e', 
+'a', 'k', '\022', 'X', '\n', '\024', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 
+'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 
+'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 
+'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '\"', '/', '\n', '\005', 'C', 'T', 'y', 'p', 
+'e', '\022', '\n', '\n', '\006', 'S', 'T', 'R', 'I', 'N', 'G', '\020', '\000', '\022', '\010', '\n', '\004', 'C', 'O', 'R', 'D', '\020', '\001', '\022', '\020', 
+'\n', '\014', 'S', 'T', 'R', 'I', 'N', 'G', '_', 'P', 'I', 'E', 'C', 'E', '\020', '\002', '\"', '5', '\n', '\006', 'J', 'S', 'T', 'y', 'p', 
+'e', '\022', '\r', '\n', '\t', 'J', 'S', '_', 'N', 'O', 'R', 'M', 'A', 'L', '\020', '\000', '\022', '\r', '\n', '\t', 'J', 'S', '_', 'S', 'T', 
+'R', 'I', 'N', 'G', '\020', '\001', '\022', '\r', '\n', '\t', 'J', 'S', '_', 'N', 'U', 'M', 'B', 'E', 'R', '\020', '\002', '*', '\t', '\010', '\350', 
+'\007', '\020', '\200', '\200', '\200', '\200', '\002', 'J', '\004', '\010', '\004', '\020', '\005', '\"', 's', '\n', '\014', 'O', 'n', 'e', 'o', 'f', 'O', 'p', 't', 
+'i', 'o', 'n', 's', '\022', 'X', '\n', '\024', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 
+'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 
+'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 
+'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '*', '\t', '\010', '\350', '\007', '\020', 
+'\200', '\200', '\200', '\200', '\002', '\"', '\300', '\001', '\n', '\013', 'E', 'n', 'u', 'm', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', '\037', '\n', '\013', 
+'a', 'l', 'l', 'o', 'w', '_', 'a', 'l', 'i', 'a', 's', '\030', '\002', ' ', '\001', '(', '\010', 'R', '\n', 'a', 'l', 'l', 'o', 'w', 'A', 
+'l', 'i', 'a', 's', '\022', '%', '\n', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\030', '\003', ' ', '\001', '(', '\010', ':', 
+'\005', 'f', 'a', 'l', 's', 'e', 'R', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\022', 'X', '\n', '\024', 'u', 'n', 'i', 
+'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', '2', 
+'$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 
+'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 
+'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '*', '\t', '\010', '\350', '\007', '\020', '\200', '\200', '\200', '\200', '\002', 'J', '\004', '\010', '\005', '\020', '\006', 
+'\"', '\236', '\001', '\n', '\020', 'E', 'n', 'u', 'm', 'V', 'a', 'l', 'u', 'e', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', '%', '\n', '\n', 
+'d', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\030', '\001', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\n', 
+'d', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\022', 'X', '\n', '\024', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 
+'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', '2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', 
+'.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 
+'t', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 
+'*', '\t', '\010', '\350', '\007', '\020', '\200', '\200', '\200', '\200', '\002', '\"', '\234', '\001', '\n', '\016', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 'O', 'p', 
+'t', 'i', 'o', 'n', 's', '\022', '%', '\n', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\030', '!', ' ', '\001', '(', '\010', 
+':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 'e', 'd', '\022', 'X', '\n', '\024', 'u', 'n', 
+'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', 
+'2', '$', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 
+'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 
+'t', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '*', '\t', '\010', '\350', '\007', '\020', '\200', '\200', '\200', '\200', '\002', '\"', '\340', '\002', '\n', '\r', 
+'M', 'e', 't', 'h', 'o', 'd', 'O', 'p', 't', 'i', 'o', 'n', 's', '\022', '%', '\n', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 
+'e', 'd', '\030', '!', ' ', '\001', '(', '\010', ':', '\005', 'f', 'a', 'l', 's', 'e', 'R', '\n', 'd', 'e', 'p', 'r', 'e', 'c', 'a', 't', 
+'e', 'd', '\022', 'q', '\n', '\021', 'i', 'd', 'e', 'm', 'p', 'o', 't', 'e', 'n', 'c', 'y', '_', 'l', 'e', 'v', 'e', 'l', '\030', '\"', 
+' ', '\001', '(', '\016', '2', '/', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'M', 'e', 
+'t', 'h', 'o', 'd', 'O', 'p', 't', 'i', 'o', 'n', 's', '.', 'I', 'd', 'e', 'm', 'p', 'o', 't', 'e', 'n', 'c', 'y', 'L', 'e', 
+'v', 'e', 'l', ':', '\023', 'I', 'D', 'E', 'M', 'P', 'O', 'T', 'E', 'N', 'C', 'Y', '_', 'U', 'N', 'K', 'N', 'O', 'W', 'N', 'R', 
+'\020', 'i', 'd', 'e', 'm', 'p', 'o', 't', 'e', 'n', 'c', 'y', 'L', 'e', 'v', 'e', 'l', '\022', 'X', '\n', '\024', 'u', 'n', 'i', 'n', 
+'t', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', '_', 'o', 'p', 't', 'i', 'o', 'n', '\030', '\347', '\007', ' ', '\003', '(', '\013', '2', '$', 
+'.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 
+'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', 'R', '\023', 'u', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 
+'d', 'O', 'p', 't', 'i', 'o', 'n', '\"', 'P', '\n', '\020', 'I', 'd', 'e', 'm', 'p', 'o', 't', 'e', 'n', 'c', 'y', 'L', 'e', 'v', 
+'e', 'l', '\022', '\027', '\n', '\023', 'I', 'D', 'E', 'M', 'P', 'O', 'T', 'E', 'N', 'C', 'Y', '_', 'U', 'N', 'K', 'N', 'O', 'W', 'N', 
+'\020', '\000', '\022', '\023', '\n', '\017', 'N', 'O', '_', 'S', 'I', 'D', 'E', '_', 'E', 'F', 'F', 'E', 'C', 'T', 'S', '\020', '\001', '\022', '\016', 
+'\n', '\n', 'I', 'D', 'E', 'M', 'P', 'O', 'T', 'E', 'N', 'T', '\020', '\002', '*', '\t', '\010', '\350', '\007', '\020', '\200', '\200', '\200', '\200', '\002', 
+'\"', '\232', '\003', '\n', '\023', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 'n', '\022', 
+'A', '\n', '\004', 'n', 'a', 'm', 'e', '\030', '\002', ' ', '\003', '(', '\013', '2', '-', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 
+'o', 't', 'o', 'b', 'u', 'f', '.', 'U', 'n', 'i', 'n', 't', 'e', 'r', 'p', 'r', 'e', 't', 'e', 'd', 'O', 'p', 't', 'i', 'o', 
+'n', '.', 'N', 'a', 'm', 'e', 'P', 'a', 'r', 't', 'R', '\004', 'n', 'a', 'm', 'e', '\022', ')', '\n', '\020', 'i', 'd', 'e', 'n', 't', 
+'i', 'f', 'i', 'e', 'r', '_', 'v', 'a', 'l', 'u', 'e', '\030', '\003', ' ', '\001', '(', '\t', 'R', '\017', 'i', 'd', 'e', 'n', 't', 'i', 
+'f', 'i', 'e', 'r', 'V', 'a', 'l', 'u', 'e', '\022', ',', '\n', '\022', 'p', 'o', 's', 'i', 't', 'i', 'v', 'e', '_', 'i', 'n', 't', 
+'_', 'v', 'a', 'l', 'u', 'e', '\030', '\004', ' ', '\001', '(', '\004', 'R', '\020', 'p', 'o', 's', 'i', 't', 'i', 'v', 'e', 'I', 'n', 't', 
+'V', 'a', 'l', 'u', 'e', '\022', ',', '\n', '\022', 'n', 'e', 'g', 'a', 't', 'i', 'v', 'e', '_', 'i', 'n', 't', '_', 'v', 'a', 'l', 
+'u', 'e', '\030', '\005', ' ', '\001', '(', '\003', 'R', '\020', 'n', 'e', 'g', 'a', 't', 'i', 'v', 'e', 'I', 'n', 't', 'V', 'a', 'l', 'u', 
+'e', '\022', '!', '\n', '\014', 'd', 'o', 'u', 'b', 'l', 'e', '_', 'v', 'a', 'l', 'u', 'e', '\030', '\006', ' ', '\001', '(', '\001', 'R', '\013', 
+'d', 'o', 'u', 'b', 'l', 'e', 'V', 'a', 'l', 'u', 'e', '\022', '!', '\n', '\014', 's', 't', 'r', 'i', 'n', 'g', '_', 'v', 'a', 'l', 
+'u', 'e', '\030', '\007', ' ', '\001', '(', '\014', 'R', '\013', 's', 't', 'r', 'i', 'n', 'g', 'V', 'a', 'l', 'u', 'e', '\022', '\'', '\n', '\017', 
+'a', 'g', 'g', 'r', 'e', 'g', 'a', 't', 'e', '_', 'v', 'a', 'l', 'u', 'e', '\030', '\010', ' ', '\001', '(', '\t', 'R', '\016', 'a', 'g', 
+'g', 'r', 'e', 'g', 'a', 't', 'e', 'V', 'a', 'l', 'u', 'e', '\032', 'J', '\n', '\010', 'N', 'a', 'm', 'e', 'P', 'a', 'r', 't', '\022', 
+'\033', '\n', '\t', 'n', 'a', 'm', 'e', '_', 'p', 'a', 'r', 't', '\030', '\001', ' ', '\002', '(', '\t', 'R', '\010', 'n', 'a', 'm', 'e', 'P', 
+'a', 'r', 't', '\022', '!', '\n', '\014', 'i', 's', '_', 'e', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\030', '\002', ' ', '\002', '(', '\010', 
+'R', '\013', 'i', 's', 'E', 'x', 't', 'e', 'n', 's', 'i', 'o', 'n', '\"', '\247', '\002', '\n', '\016', 'S', 'o', 'u', 'r', 'c', 'e', 'C', 
+'o', 'd', 'e', 'I', 'n', 'f', 'o', '\022', 'D', '\n', '\010', 'l', 'o', 'c', 'a', 't', 'i', 'o', 'n', '\030', '\001', ' ', '\003', '(', '\013', 
+'2', '(', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'S', 'o', 'u', 'r', 'c', 'e', 
+'C', 'o', 'd', 'e', 'I', 'n', 'f', 'o', '.', 'L', 'o', 'c', 'a', 't', 'i', 'o', 'n', 'R', '\010', 'l', 'o', 'c', 'a', 't', 'i', 
+'o', 'n', '\032', '\316', '\001', '\n', '\010', 'L', 'o', 'c', 'a', 't', 'i', 'o', 'n', '\022', '\026', '\n', '\004', 'p', 'a', 't', 'h', '\030', '\001', 
+' ', '\003', '(', '\005', 'B', '\002', '\020', '\001', 'R', '\004', 'p', 'a', 't', 'h', '\022', '\026', '\n', '\004', 's', 'p', 'a', 'n', '\030', '\002', ' ', 
+'\003', '(', '\005', 'B', '\002', '\020', '\001', 'R', '\004', 's', 'p', 'a', 'n', '\022', ')', '\n', '\020', 'l', 'e', 'a', 'd', 'i', 'n', 'g', '_', 
+'c', 'o', 'm', 'm', 'e', 'n', 't', 's', '\030', '\003', ' ', '\001', '(', '\t', 'R', '\017', 'l', 'e', 'a', 'd', 'i', 'n', 'g', 'C', 'o', 
+'m', 'm', 'e', 'n', 't', 's', '\022', '+', '\n', '\021', 't', 'r', 'a', 'i', 'l', 'i', 'n', 'g', '_', 'c', 'o', 'm', 'm', 'e', 'n', 
+'t', 's', '\030', '\004', ' ', '\001', '(', '\t', 'R', '\020', 't', 'r', 'a', 'i', 'l', 'i', 'n', 'g', 'C', 'o', 'm', 'm', 'e', 'n', 't', 
+'s', '\022', ':', '\n', '\031', 'l', 'e', 'a', 'd', 'i', 'n', 'g', '_', 'd', 'e', 't', 'a', 'c', 'h', 'e', 'd', '_', 'c', 'o', 'm', 
+'m', 'e', 'n', 't', 's', '\030', '\006', ' ', '\003', '(', '\t', 'R', '\027', 'l', 'e', 'a', 'd', 'i', 'n', 'g', 'D', 'e', 't', 'a', 'c', 
+'h', 'e', 'd', 'C', 'o', 'm', 'm', 'e', 'n', 't', 's', '\"', '\321', '\001', '\n', '\021', 'G', 'e', 'n', 'e', 'r', 'a', 't', 'e', 'd', 
+'C', 'o', 'd', 'e', 'I', 'n', 'f', 'o', '\022', 'M', '\n', '\n', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', '\030', '\001', ' ', 
+'\003', '(', '\013', '2', '-', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'G', 'e', 'n', 
+'e', 'r', 'a', 't', 'e', 'd', 'C', 'o', 'd', 'e', 'I', 'n', 'f', 'o', '.', 'A', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', 
+'R', '\n', 'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n', '\032', 'm', '\n', '\n', 'A', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 
+'n', '\022', '\026', '\n', '\004', 'p', 'a', 't', 'h', '\030', '\001', ' ', '\003', '(', '\005', 'B', '\002', '\020', '\001', 'R', '\004', 'p', 'a', 't', 'h', 
+'\022', '\037', '\n', '\013', 's', 'o', 'u', 'r', 'c', 'e', '_', 'f', 'i', 'l', 'e', '\030', '\002', ' ', '\001', '(', '\t', 'R', '\n', 's', 'o', 
+'u', 'r', 'c', 'e', 'F', 'i', 'l', 'e', '\022', '\024', '\n', '\005', 'b', 'e', 'g', 'i', 'n', '\030', '\003', ' ', '\001', '(', '\005', 'R', '\005', 
+'b', 'e', 'g', 'i', 'n', '\022', '\020', '\n', '\003', 'e', 'n', 'd', '\030', '\004', ' ', '\001', '(', '\005', 'R', '\003', 'e', 'n', 'd', 'B', '\217', 
+'\001', '\n', '\023', 'c', 'o', 'm', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', 'B', '\020', 'D', 
+'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 'P', 'r', 'o', 't', 'o', 's', 'H', '\001', 'Z', '>', 'g', 'i', 't', 'h', 'u', 'b', 
+'.', 'c', 'o', 'm', '/', 'g', 'o', 'l', 'a', 'n', 'g', '/', 'p', 'r', 'o', 't', 'o', 'b', 'u', 'f', '/', 'p', 'r', 'o', 't', 
+'o', 'c', '-', 'g', 'e', 'n', '-', 'g', 'o', '/', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', ';', 'd', 'e', 's', 'c', 
+'r', 'i', 'p', 't', 'o', 'r', '\370', '\001', '\001', '\242', '\002', '\003', 'G', 'P', 'B', '\252', '\002', '\032', 'G', 'o', 'o', 'g', 'l', 'e', '.', 
+'P', 'r', 'o', 't', 'o', 'b', 'u', 'f', '.', 'R', 'e', 'f', 'l', 'e', 'c', 't', 'i', 'o', 'n', 
+};
+
+static upb_def_init *deps[1] = {
+  NULL
+};
+
+upb_def_init google_protobuf_descriptor_proto_upbdefinit = {
+  deps,
+  layouts,
+  "google/protobuf/descriptor.proto",
+  UPB_STRVIEW_INIT(descriptor, 7619)
+};
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+typedef struct {
+  size_t len;
+  char str[1];  /* Null-terminated string data follows. */
+} str_t;
+
+static str_t *newstr(upb_alloc *alloc, const char *data, size_t len) {
+  str_t *ret = upb_malloc(alloc, sizeof(*ret) + len);
+  if (!ret) return NULL;
+  ret->len = len;
+  if (len) memcpy(ret->str, data, len);
+  ret->str[len] = '\0';
+  return ret;
+}
+
+struct upb_fielddef {
+  const upb_filedef *file;
+  const upb_msgdef *msgdef;
+  const char *full_name;
+  const char *json_name;
+  union {
+    int64_t sint;
+    uint64_t uint;
+    double dbl;
+    float flt;
+    bool boolean;
+    str_t *str;
+  } defaultval;
+  const upb_oneofdef *oneof;
+  union {
+    const upb_msgdef *msgdef;
+    const upb_enumdef *enumdef;
+    const google_protobuf_FieldDescriptorProto *unresolved;
+  } sub;
+  uint32_t number_;
+  uint16_t index_;
+  uint16_t layout_index;
+  uint32_t selector_base;  /* Used to index into a upb::Handlers table. */
+  bool is_extension_;
+  bool lazy_;
+  bool packed_;
+  bool proto3_optional_;
+  upb_descriptortype_t type_;
+  upb_label_t label_;
+};
+
+struct upb_msgdef {
+  const upb_msglayout *layout;
+  const upb_filedef *file;
+  const char *full_name;
+  uint32_t selector_count;
+  uint32_t submsg_field_count;
+
+  /* Tables for looking up fields by number and name. */
+  upb_inttable itof;
+  upb_strtable ntof;
+
+  const upb_fielddef *fields;
+  const upb_oneofdef *oneofs;
+  int field_count;
+  int oneof_count;
+  int real_oneof_count;
+
+  /* Is this a map-entry message? */
+  bool map_entry;
+  upb_wellknowntype_t well_known_type;
+
+  /* TODO(haberman): proper extension ranges (there can be multiple). */
+};
+
+struct upb_enumdef {
+  const upb_filedef *file;
+  const char *full_name;
+  upb_strtable ntoi;
+  upb_inttable iton;
+  int32_t defaultval;
+};
+
+struct upb_oneofdef {
+  const upb_msgdef *parent;
+  const char *full_name;
+  uint32_t index;
+  upb_strtable ntof;
+  upb_inttable itof;
+};
+
+struct upb_filedef {
+  const char *name;
+  const char *package;
+  const char *phpprefix;
+  const char *phpnamespace;
+  upb_syntax_t syntax;
+
+  const upb_filedef **deps;
+  const upb_msgdef *msgs;
+  const upb_enumdef *enums;
+  const upb_fielddef *exts;
+
+  int dep_count;
+  int msg_count;
+  int enum_count;
+  int ext_count;
+};
+
+struct upb_symtab {
+  upb_arena *arena;
+  upb_strtable syms;  /* full_name -> packed def ptr */
+  upb_strtable files;  /* file_name -> upb_filedef* */
+};
+
+/* Inside a symtab we store tagged pointers to specific def types. */
+typedef enum {
+  UPB_DEFTYPE_FIELD = 0,
+
+  /* Only inside symtab table. */
+  UPB_DEFTYPE_MSG = 1,
+  UPB_DEFTYPE_ENUM = 2,
+
+  /* Only inside message table. */
+  UPB_DEFTYPE_ONEOF = 1,
+  UPB_DEFTYPE_FIELD_JSONNAME = 2
+} upb_deftype_t;
+
+static const void *unpack_def(upb_value v, upb_deftype_t type) {
+  uintptr_t num = (uintptr_t)upb_value_getconstptr(v);
+  return (num & 3) == type ? (const void*)(num & ~3) : NULL;
+}
+
+static upb_value pack_def(const void *ptr, upb_deftype_t type) {
+  uintptr_t num = (uintptr_t)ptr | type;
+  return upb_value_constptr((const void*)num);
+}
+
+/* isalpha() etc. from <ctype.h> are locale-dependent, which we don't want. */
+static bool upb_isbetween(char c, char low, char high) {
+  return c >= low && c <= high;
+}
+
+static bool upb_isletter(char c) {
+  return upb_isbetween(c, 'A', 'Z') || upb_isbetween(c, 'a', 'z') || c == '_';
+}
+
+static bool upb_isalphanum(char c) {
+  return upb_isletter(c) || upb_isbetween(c, '0', '9');
+}
+
+static bool upb_isident(upb_strview name, bool full, upb_status *s) {
+  const char *str = name.data;
+  size_t len = name.size;
+  bool start = true;
+  size_t i;
+  for (i = 0; i < len; i++) {
+    char c = str[i];
+    if (c == '.') {
+      if (start || !full) {
+        upb_status_seterrf(s, "invalid name: unexpected '.' (%s)", str);
+        return false;
+      }
+      start = true;
+    } else if (start) {
+      if (!upb_isletter(c)) {
+        upb_status_seterrf(
+            s, "invalid name: path components must start with a letter (%s)",
+            str);
+        return false;
+      }
+      start = false;
+    } else {
+      if (!upb_isalphanum(c)) {
+        upb_status_seterrf(s, "invalid name: non-alphanumeric character (%s)",
+                           str);
+        return false;
+      }
+    }
+  }
+  return !start;
+}
+
+static const char *shortdefname(const char *fullname) {
+  const char *p;
+
+  if (fullname == NULL) {
+    return NULL;
+  } else if ((p = strrchr(fullname, '.')) == NULL) {
+    /* No '.' in the name, return the full string. */
+    return fullname;
+  } else {
+    /* Return one past the last '.'. */
+    return p + 1;
+  }
+}
+
+/* All submessage fields are lower than all other fields.
+ * Secondly, fields are increasing in order. */
+uint32_t field_rank(const upb_fielddef *f) {
+  uint32_t ret = upb_fielddef_number(f);
+  const uint32_t high_bit = 1 << 30;
+  UPB_ASSERT(ret < high_bit);
+  if (!upb_fielddef_issubmsg(f))
+    ret |= high_bit;
+  return ret;
+}
+
+int cmp_fields(const void *p1, const void *p2) {
+  const upb_fielddef *f1 = *(upb_fielddef*const*)p1;
+  const upb_fielddef *f2 = *(upb_fielddef*const*)p2;
+  return field_rank(f1) - field_rank(f2);
+}
+
+/* A few implementation details of handlers.  We put these here to avoid
+ * a def -> handlers dependency. */
+
+#define UPB_STATIC_SELECTOR_COUNT 3  /* Warning: also in upb/handlers.h. */
+
+static uint32_t upb_handlers_selectorbaseoffset(const upb_fielddef *f) {
+  return upb_fielddef_isseq(f) ? 2 : 0;
+}
+
+static uint32_t upb_handlers_selectorcount(const upb_fielddef *f) {
+  uint32_t ret = 1;
+  if (upb_fielddef_isseq(f)) ret += 2;    /* STARTSEQ/ENDSEQ */
+  if (upb_fielddef_isstring(f)) ret += 2; /* [STRING]/STARTSTR/ENDSTR */
+  if (upb_fielddef_issubmsg(f)) {
+    /* ENDSUBMSG (STARTSUBMSG is at table beginning) */
+    ret += 0;
+    if (upb_fielddef_lazy(f)) {
+      /* STARTSTR/ENDSTR/STRING (for lazy) */
+      ret += 3;
+    }
+  }
+  return ret;
+}
+
+static void upb_status_setoom(upb_status *status) {
+  upb_status_seterrmsg(status, "out of memory");
+}
+
+static bool assign_msg_indices(upb_msgdef *m, upb_status *s) {
+  /* Sort fields.  upb internally relies on UPB_TYPE_MESSAGE fields having the
+   * lowest indexes, but we do not publicly guarantee this. */
+  upb_msg_field_iter j;
+  int i;
+  uint32_t selector;
+  int n = upb_msgdef_numfields(m);
+  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_status_setoom(s);
+    return false;
+  }
+
+  m->submsg_field_count = 0;
+  for(i = 0, upb_msg_field_begin(&j, m);
+      !upb_msg_field_done(&j);
+      upb_msg_field_next(&j), i++) {
+    upb_fielddef *f = upb_msg_iter_field(&j);
+    UPB_ASSERT(f->msgdef == m);
+    if (upb_fielddef_issubmsg(f)) {
+      m->submsg_field_count++;
+    }
+    fields[i] = f;
+  }
+
+  qsort(fields, n, sizeof(*fields), cmp_fields);
+
+  selector = UPB_STATIC_SELECTOR_COUNT + m->submsg_field_count;
+  for (i = 0; i < n; i++) {
+    upb_fielddef *f = fields[i];
+    f->index_ = i;
+    f->selector_base = selector + upb_handlers_selectorbaseoffset(f);
+    selector += upb_handlers_selectorcount(f);
+  }
+  m->selector_count = selector;
+
+  upb_gfree(fields);
+  return true;
+}
+
+static bool check_oneofs(upb_msgdef *m, upb_status *s) {
+  int i;
+  int first_synthetic = -1;
+  upb_oneofdef *mutable_oneofs = (upb_oneofdef*)m->oneofs;
+
+  for (i = 0; i < m->oneof_count; i++) {
+    mutable_oneofs[i].index = i;
+
+    if (upb_oneofdef_issynthetic(&mutable_oneofs[i])) {
+      if (first_synthetic == -1) {
+        first_synthetic = i;
+      }
+    } else {
+      if (first_synthetic != -1) {
+        upb_status_seterrf(
+            s, "Synthetic oneofs must be after all other oneofs: %s",
+            upb_oneofdef_name(&mutable_oneofs[i]));
+        return false;
+      }
+    }
+  }
+
+  if (first_synthetic == -1) {
+    m->real_oneof_count = m->oneof_count;
+  } else {
+    m->real_oneof_count = first_synthetic;
+  }
+
+  return true;
+}
+
+static void assign_msg_wellknowntype(upb_msgdef *m) {
+  const char *name = upb_msgdef_fullname(m);
+  if (name == NULL) {
+    m->well_known_type = UPB_WELLKNOWN_UNSPECIFIED;
+    return;
+  }
+  if (!strcmp(name, "google.protobuf.Any")) {
+    m->well_known_type = UPB_WELLKNOWN_ANY;
+  } else if (!strcmp(name, "google.protobuf.FieldMask")) {
+    m->well_known_type = UPB_WELLKNOWN_FIELDMASK;
+  } else if (!strcmp(name, "google.protobuf.Duration")) {
+    m->well_known_type = UPB_WELLKNOWN_DURATION;
+  } else if (!strcmp(name, "google.protobuf.Timestamp")) {
+    m->well_known_type = UPB_WELLKNOWN_TIMESTAMP;
+  } else if (!strcmp(name, "google.protobuf.DoubleValue")) {
+    m->well_known_type = UPB_WELLKNOWN_DOUBLEVALUE;
+  } else if (!strcmp(name, "google.protobuf.FloatValue")) {
+    m->well_known_type = UPB_WELLKNOWN_FLOATVALUE;
+  } else if (!strcmp(name, "google.protobuf.Int64Value")) {
+    m->well_known_type = UPB_WELLKNOWN_INT64VALUE;
+  } else if (!strcmp(name, "google.protobuf.UInt64Value")) {
+    m->well_known_type = UPB_WELLKNOWN_UINT64VALUE;
+  } else if (!strcmp(name, "google.protobuf.Int32Value")) {
+    m->well_known_type = UPB_WELLKNOWN_INT32VALUE;
+  } else if (!strcmp(name, "google.protobuf.UInt32Value")) {
+    m->well_known_type = UPB_WELLKNOWN_UINT32VALUE;
+  } else if (!strcmp(name, "google.protobuf.BoolValue")) {
+    m->well_known_type = UPB_WELLKNOWN_BOOLVALUE;
+  } else if (!strcmp(name, "google.protobuf.StringValue")) {
+    m->well_known_type = UPB_WELLKNOWN_STRINGVALUE;
+  } else if (!strcmp(name, "google.protobuf.BytesValue")) {
+    m->well_known_type = UPB_WELLKNOWN_BYTESVALUE;
+  } else if (!strcmp(name, "google.protobuf.Value")) {
+    m->well_known_type = UPB_WELLKNOWN_VALUE;
+  } else if (!strcmp(name, "google.protobuf.ListValue")) {
+    m->well_known_type = UPB_WELLKNOWN_LISTVALUE;
+  } else if (!strcmp(name, "google.protobuf.Struct")) {
+    m->well_known_type = UPB_WELLKNOWN_STRUCT;
+  } else {
+    m->well_known_type = UPB_WELLKNOWN_UNSPECIFIED;
+  }
+}
+
+
+/* upb_enumdef ****************************************************************/
+
+const char *upb_enumdef_fullname(const upb_enumdef *e) {
+  return e->full_name;
+}
+
+const char *upb_enumdef_name(const upb_enumdef *e) {
+  return shortdefname(e->full_name);
+}
+
+const upb_filedef *upb_enumdef_file(const upb_enumdef *e) {
+  return e->file;
+}
+
+int32_t upb_enumdef_default(const upb_enumdef *e) {
+  UPB_ASSERT(upb_enumdef_iton(e, e->defaultval));
+  return e->defaultval;
+}
+
+int upb_enumdef_numvals(const upb_enumdef *e) {
+  return (int)upb_strtable_count(&e->ntoi);
+}
+
+void upb_enum_begin(upb_enum_iter *i, const upb_enumdef *e) {
+  /* We iterate over the ntoi table, to account for duplicate numbers. */
+  upb_strtable_begin(i, &e->ntoi);
+}
+
+void upb_enum_next(upb_enum_iter *iter) { upb_strtable_next(iter); }
+bool upb_enum_done(upb_enum_iter *iter) { return upb_strtable_done(iter); }
+
+bool upb_enumdef_ntoi(const upb_enumdef *def, const char *name,
+                      size_t len, int32_t *num) {
+  upb_value v;
+  if (!upb_strtable_lookup2(&def->ntoi, name, len, &v)) {
+    return false;
+  }
+  if (num) *num = upb_value_getint32(v);
+  return true;
+}
+
+const char *upb_enumdef_iton(const upb_enumdef *def, int32_t num) {
+  upb_value v;
+  return upb_inttable_lookup32(&def->iton, num, &v) ?
+      upb_value_getcstr(v) : NULL;
+}
+
+const char *upb_enum_iter_name(upb_enum_iter *iter) {
+  return upb_strtable_iter_key(iter).data;
+}
+
+int32_t upb_enum_iter_number(upb_enum_iter *iter) {
+  return upb_value_getint32(upb_strtable_iter_value(iter));
+}
+
+
+/* upb_fielddef ***************************************************************/
+
+const char *upb_fielddef_fullname(const upb_fielddef *f) {
+  return f->full_name;
+}
+
+upb_fieldtype_t upb_fielddef_type(const upb_fielddef *f) {
+  switch (f->type_) {
+    case UPB_DESCRIPTOR_TYPE_DOUBLE:
+      return UPB_TYPE_DOUBLE;
+    case UPB_DESCRIPTOR_TYPE_FLOAT:
+      return UPB_TYPE_FLOAT;
+    case UPB_DESCRIPTOR_TYPE_INT64:
+    case UPB_DESCRIPTOR_TYPE_SINT64:
+    case UPB_DESCRIPTOR_TYPE_SFIXED64:
+      return UPB_TYPE_INT64;
+    case UPB_DESCRIPTOR_TYPE_INT32:
+    case UPB_DESCRIPTOR_TYPE_SFIXED32:
+    case UPB_DESCRIPTOR_TYPE_SINT32:
+      return UPB_TYPE_INT32;
+    case UPB_DESCRIPTOR_TYPE_UINT64:
+    case UPB_DESCRIPTOR_TYPE_FIXED64:
+      return UPB_TYPE_UINT64;
+    case UPB_DESCRIPTOR_TYPE_UINT32:
+    case UPB_DESCRIPTOR_TYPE_FIXED32:
+      return UPB_TYPE_UINT32;
+    case UPB_DESCRIPTOR_TYPE_ENUM:
+      return UPB_TYPE_ENUM;
+    case UPB_DESCRIPTOR_TYPE_BOOL:
+      return UPB_TYPE_BOOL;
+    case UPB_DESCRIPTOR_TYPE_STRING:
+      return UPB_TYPE_STRING;
+    case UPB_DESCRIPTOR_TYPE_BYTES:
+      return UPB_TYPE_BYTES;
+    case UPB_DESCRIPTOR_TYPE_GROUP:
+    case UPB_DESCRIPTOR_TYPE_MESSAGE:
+      return UPB_TYPE_MESSAGE;
+  }
+  UPB_UNREACHABLE();
+}
+
+upb_descriptortype_t upb_fielddef_descriptortype(const upb_fielddef *f) {
+  return f->type_;
+}
+
+uint32_t upb_fielddef_index(const upb_fielddef *f) {
+  return f->index_;
+}
+
+upb_label_t upb_fielddef_label(const upb_fielddef *f) {
+  return f->label_;
+}
+
+uint32_t upb_fielddef_number(const upb_fielddef *f) {
+  return f->number_;
+}
+
+bool upb_fielddef_isextension(const upb_fielddef *f) {
+  return f->is_extension_;
+}
+
+bool upb_fielddef_lazy(const upb_fielddef *f) {
+  return f->lazy_;
+}
+
+bool upb_fielddef_packed(const upb_fielddef *f) {
+  return f->packed_;
+}
+
+const char *upb_fielddef_name(const upb_fielddef *f) {
+  return shortdefname(f->full_name);
+}
+
+const char *upb_fielddef_jsonname(const upb_fielddef *f) {
+  return f->json_name;
+}
+
+uint32_t upb_fielddef_selectorbase(const upb_fielddef *f) {
+  return f->selector_base;
+}
+
+const upb_filedef *upb_fielddef_file(const upb_fielddef *f) {
+  return f->file;
+}
+
+const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f) {
+  return f->msgdef;
+}
+
+const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f) {
+  return f->oneof;
+}
+
+const upb_oneofdef *upb_fielddef_realcontainingoneof(const upb_fielddef *f) {
+  if (!f->oneof || upb_oneofdef_issynthetic(f->oneof)) return NULL;
+  return f->oneof;
+}
+
+static void chkdefaulttype(const upb_fielddef *f, int ctype) {
+  UPB_UNUSED(f);
+  UPB_UNUSED(ctype);
+}
+
+int64_t upb_fielddef_defaultint64(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_INT64);
+  return f->defaultval.sint;
+}
+
+int32_t upb_fielddef_defaultint32(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_INT32);
+  return (int32_t)f->defaultval.sint;
+}
+
+uint64_t upb_fielddef_defaultuint64(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_UINT64);
+  return f->defaultval.uint;
+}
+
+uint32_t upb_fielddef_defaultuint32(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_UINT32);
+  return (uint32_t)f->defaultval.uint;
+}
+
+bool upb_fielddef_defaultbool(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_BOOL);
+  return f->defaultval.boolean;
+}
+
+float upb_fielddef_defaultfloat(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_FLOAT);
+  return f->defaultval.flt;
+}
+
+double upb_fielddef_defaultdouble(const upb_fielddef *f) {
+  chkdefaulttype(f, UPB_TYPE_DOUBLE);
+  return f->defaultval.dbl;
+}
+
+const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len) {
+  str_t *str = f->defaultval.str;
+  UPB_ASSERT(upb_fielddef_type(f) == UPB_TYPE_STRING ||
+         upb_fielddef_type(f) == UPB_TYPE_BYTES ||
+         upb_fielddef_type(f) == UPB_TYPE_ENUM);
+  if (str) {
+    if (len) *len = str->len;
+    return str->str;
+  } else {
+    if (len) *len = 0;
+    return NULL;
+  }
+}
+
+const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f) {
+  return upb_fielddef_type(f) == UPB_TYPE_MESSAGE ? f->sub.msgdef : NULL;
+}
+
+const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f) {
+  return upb_fielddef_type(f) == UPB_TYPE_ENUM ? f->sub.enumdef : NULL;
+}
+
+const upb_msglayout_field *upb_fielddef_layout(const upb_fielddef *f) {
+  return &f->msgdef->layout->fields[f->layout_index];
+}
+
+bool upb_fielddef_issubmsg(const upb_fielddef *f) {
+  return upb_fielddef_type(f) == UPB_TYPE_MESSAGE;
+}
+
+bool upb_fielddef_isstring(const upb_fielddef *f) {
+  return upb_fielddef_type(f) == UPB_TYPE_STRING ||
+         upb_fielddef_type(f) == UPB_TYPE_BYTES;
+}
+
+bool upb_fielddef_isseq(const upb_fielddef *f) {
+  return upb_fielddef_label(f) == UPB_LABEL_REPEATED;
+}
+
+bool upb_fielddef_isprimitive(const upb_fielddef *f) {
+  return !upb_fielddef_isstring(f) && !upb_fielddef_issubmsg(f);
+}
+
+bool upb_fielddef_ismap(const upb_fielddef *f) {
+  return upb_fielddef_isseq(f) && upb_fielddef_issubmsg(f) &&
+         upb_msgdef_mapentry(upb_fielddef_msgsubdef(f));
+}
+
+bool upb_fielddef_hassubdef(const upb_fielddef *f) {
+  return upb_fielddef_issubmsg(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM;
+}
+
+bool upb_fielddef_haspresence(const upb_fielddef *f) {
+  if (upb_fielddef_isseq(f)) return false;
+  return upb_fielddef_issubmsg(f) || upb_fielddef_containingoneof(f) ||
+         f->file->syntax == UPB_SYNTAX_PROTO2;
+}
+
+static bool between(int32_t x, int32_t low, int32_t high) {
+  return x >= low && x <= high;
+}
+
+bool upb_fielddef_checklabel(int32_t label) { return between(label, 1, 3); }
+bool upb_fielddef_checktype(int32_t type) { return between(type, 1, 11); }
+bool upb_fielddef_checkintfmt(int32_t fmt) { return between(fmt, 1, 3); }
+
+bool upb_fielddef_checkdescriptortype(int32_t type) {
+  return between(type, 1, 18);
+}
+
+/* upb_msgdef *****************************************************************/
+
+const char *upb_msgdef_fullname(const upb_msgdef *m) {
+  return m->full_name;
+}
+
+const upb_filedef *upb_msgdef_file(const upb_msgdef *m) {
+  return m->file;
+}
+
+const char *upb_msgdef_name(const upb_msgdef *m) {
+  return shortdefname(m->full_name);
+}
+
+upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m) {
+  return m->file->syntax;
+}
+
+size_t upb_msgdef_selectorcount(const upb_msgdef *m) {
+  return m->selector_count;
+}
+
+uint32_t upb_msgdef_submsgfieldcount(const upb_msgdef *m) {
+  return m->submsg_field_count;
+}
+
+const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) {
+  upb_value val;
+  return upb_inttable_lookup32(&m->itof, i, &val) ?
+      upb_value_getconstptr(val) : NULL;
+}
+
+const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name,
+                                    size_t len) {
+  upb_value val;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  return unpack_def(val, UPB_DEFTYPE_FIELD);
+}
+
+const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name,
+                                    size_t len) {
+  upb_value val;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  return unpack_def(val, UPB_DEFTYPE_ONEOF);
+}
+
+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 = unpack_def(val, UPB_DEFTYPE_ONEOF);
+  *f = unpack_def(val, UPB_DEFTYPE_FIELD);
+  return *o || *f;  /* False if this was a JSON name. */
+}
+
+const upb_fielddef *upb_msgdef_lookupjsonname(const upb_msgdef *m,
+                                              const char *name, size_t len) {
+  upb_value val;
+  const upb_fielddef* f;
+
+  if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) {
+    return NULL;
+  }
+
+  f = unpack_def(val, UPB_DEFTYPE_FIELD);
+  if (!f) f = unpack_def(val, UPB_DEFTYPE_FIELD_JSONNAME);
+
+  return f;
+}
+
+int upb_msgdef_numfields(const upb_msgdef *m) {
+  return m->field_count;
+}
+
+int upb_msgdef_numoneofs(const upb_msgdef *m) {
+  return m->oneof_count;
+}
+
+int upb_msgdef_numrealoneofs(const upb_msgdef *m) {
+  return m->real_oneof_count;
+}
+
+const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m) {
+  return m->layout;
+}
+
+const upb_fielddef *_upb_msgdef_field(const upb_msgdef *m, int i) {
+  if (i >= m->field_count) return NULL;
+  return &m->fields[i];
+}
+
+bool upb_msgdef_mapentry(const upb_msgdef *m) {
+  return m->map_entry;
+}
+
+upb_wellknowntype_t upb_msgdef_wellknowntype(const upb_msgdef *m) {
+  return m->well_known_type;
+}
+
+bool upb_msgdef_isnumberwrapper(const upb_msgdef *m) {
+  upb_wellknowntype_t type = upb_msgdef_wellknowntype(m);
+  return type >= UPB_WELLKNOWN_DOUBLEVALUE &&
+         type <= UPB_WELLKNOWN_UINT32VALUE;
+}
+
+bool upb_msgdef_iswrapper(const upb_msgdef *m) {
+  upb_wellknowntype_t type = upb_msgdef_wellknowntype(m);
+  return type >= UPB_WELLKNOWN_DOUBLEVALUE &&
+         type <= UPB_WELLKNOWN_BOOLVALUE;
+}
+
+void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m) {
+  upb_inttable_begin(iter, &m->itof);
+}
+
+void upb_msg_field_next(upb_msg_field_iter *iter) { upb_inttable_next(iter); }
+
+bool upb_msg_field_done(const upb_msg_field_iter *iter) {
+  return upb_inttable_done(iter);
+}
+
+upb_fielddef *upb_msg_iter_field(const upb_msg_field_iter *iter) {
+  return (upb_fielddef *)upb_value_getconstptr(upb_inttable_iter_value(iter));
+}
+
+void upb_msg_field_iter_setdone(upb_msg_field_iter *iter) {
+  upb_inttable_iter_setdone(iter);
+}
+
+bool upb_msg_field_iter_isequal(const upb_msg_field_iter * iter1,
+                                const upb_msg_field_iter * iter2) {
+  return upb_inttable_iter_isequal(iter1, iter2);
+}
+
+void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) {
+  upb_strtable_begin(iter, &m->ntof);
+  /* We need to skip past any initial fields. */
+  while (!upb_strtable_done(iter) &&
+         !unpack_def(upb_strtable_iter_value(iter), UPB_DEFTYPE_ONEOF)) {
+    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) &&
+           !unpack_def(upb_strtable_iter_value(iter), UPB_DEFTYPE_ONEOF));
+}
+
+bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) {
+  return upb_strtable_done(iter);
+}
+
+const upb_oneofdef *upb_msg_iter_oneof(const upb_msg_oneof_iter *iter) {
+  return unpack_def(upb_strtable_iter_value(iter), UPB_DEFTYPE_ONEOF);
+}
+
+void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter *iter) {
+  upb_strtable_iter_setdone(iter);
+}
+
+bool upb_msg_oneof_iter_isequal(const upb_msg_oneof_iter *iter1,
+                                const upb_msg_oneof_iter *iter2) {
+  return upb_strtable_iter_isequal(iter1, iter2);
+}
+
+/* upb_oneofdef ***************************************************************/
+
+const char *upb_oneofdef_name(const upb_oneofdef *o) {
+  return shortdefname(o->full_name);
+}
+
+const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o) {
+  return o->parent;
+}
+
+int upb_oneofdef_numfields(const upb_oneofdef *o) {
+  return (int)upb_strtable_count(&o->ntof);
+}
+
+uint32_t upb_oneofdef_index(const upb_oneofdef *o) {
+  return o->index;
+}
+
+bool upb_oneofdef_issynthetic(const upb_oneofdef *o) {
+  upb_inttable_iter iter;
+  const upb_fielddef *f;
+  upb_inttable_begin(&iter, &o->itof);
+  if (upb_oneofdef_numfields(o) != 1) return false;
+  f = upb_value_getptr(upb_inttable_iter_value(&iter));
+  UPB_ASSERT(f);
+  return f->proto3_optional_;
+}
+
+const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o,
+                                      const char *name, size_t length) {
+  upb_value val;
+  return upb_strtable_lookup2(&o->ntof, name, length, &val) ?
+      upb_value_getptr(val) : NULL;
+}
+
+const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num) {
+  upb_value val;
+  return upb_inttable_lookup32(&o->itof, num, &val) ?
+      upb_value_getptr(val) : NULL;
+}
+
+void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o) {
+  upb_inttable_begin(iter, &o->itof);
+}
+
+void upb_oneof_next(upb_oneof_iter *iter) {
+  upb_inttable_next(iter);
+}
+
+bool upb_oneof_done(upb_oneof_iter *iter) {
+  return upb_inttable_done(iter);
+}
+
+upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter) {
+  return (upb_fielddef *)upb_value_getconstptr(upb_inttable_iter_value(iter));
+}
+
+void upb_oneof_iter_setdone(upb_oneof_iter *iter) {
+  upb_inttable_iter_setdone(iter);
+}
+
+/* Dynamic Layout Generation. *************************************************/
+
+static size_t div_round_up(size_t n, size_t d) {
+  return (n + d - 1) / d;
+}
+
+static size_t upb_msgval_sizeof(upb_fieldtype_t type) {
+  switch (type) {
+    case UPB_TYPE_DOUBLE:
+    case UPB_TYPE_INT64:
+    case UPB_TYPE_UINT64:
+      return 8;
+    case UPB_TYPE_ENUM:
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_UINT32:
+    case UPB_TYPE_FLOAT:
+      return 4;
+    case UPB_TYPE_BOOL:
+      return 1;
+    case UPB_TYPE_MESSAGE:
+      return sizeof(void*);
+    case UPB_TYPE_BYTES:
+    case UPB_TYPE_STRING:
+      return sizeof(upb_strview);
+  }
+  UPB_UNREACHABLE();
+}
+
+static uint8_t upb_msg_fielddefsize(const upb_fielddef *f) {
+  if (upb_msgdef_mapentry(upb_fielddef_containingtype(f))) {
+    upb_map_entry ent;
+    UPB_ASSERT(sizeof(ent.k) == sizeof(ent.v));
+    return sizeof(ent.k);
+  } else if (upb_fielddef_isseq(f)) {
+    return sizeof(void*);
+  } else {
+    return upb_msgval_sizeof(upb_fielddef_type(f));
+  }
+}
+
+static uint32_t upb_msglayout_place(upb_msglayout *l, size_t size) {
+  uint32_t ret;
+
+  l->size = UPB_ALIGN_UP(l->size, size);
+  ret = l->size;
+  l->size += size;
+  return ret;
+}
+
+static int field_number_cmp(const void *p1, const void *p2) {
+  const upb_msglayout_field *f1 = p1;
+  const upb_msglayout_field *f2 = p2;
+  return f1->number - f2->number;
+}
+
+static void assign_layout_indices(const upb_msgdef *m, upb_msglayout_field *fields) {
+  int i;
+  int n = upb_msgdef_numfields(m);
+  for (i = 0; i < n; i++) {
+    upb_fielddef *f = (upb_fielddef*)upb_msgdef_itof(m, fields[i].number);
+    UPB_ASSERT(f);
+    f->layout_index = i;
+  }
+}
+
+/* This function is the dynamic equivalent of message_layout.{cc,h} in upbc.
+ * It computes a dynamic layout for all of the fields in |m|. */
+static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) {
+  upb_msglayout *l = (upb_msglayout*)m->layout;
+  upb_msg_field_iter it;
+  upb_msg_oneof_iter oit;
+  size_t hasbit;
+  size_t submsg_count = m->submsg_field_count;
+  const upb_msglayout **submsgs;
+  upb_msglayout_field *fields;
+  upb_alloc *alloc = upb_arena_alloc(symtab->arena);
+
+  memset(l, 0, sizeof(*l));
+
+  fields = upb_malloc(alloc, upb_msgdef_numfields(m) * sizeof(*fields));
+  submsgs = upb_malloc(alloc, submsg_count * sizeof(*submsgs));
+
+  if ((!fields && upb_msgdef_numfields(m)) ||
+      (!submsgs && submsg_count)) {
+    /* OOM. */
+    return false;
+  }
+
+  l->field_count = upb_msgdef_numfields(m);
+  l->fields = fields;
+  l->submsgs = submsgs;
+
+  if (upb_msgdef_mapentry(m)) {
+    /* TODO(haberman): refactor this method so this special case is more
+     * elegant. */
+    const upb_fielddef *key = upb_msgdef_itof(m, 1);
+    const upb_fielddef *val = upb_msgdef_itof(m, 2);
+    fields[0].number = 1;
+    fields[1].number = 2;
+    fields[0].label = UPB_LABEL_OPTIONAL;
+    fields[1].label = UPB_LABEL_OPTIONAL;
+    fields[0].presence = 0;
+    fields[1].presence = 0;
+    fields[0].descriptortype = upb_fielddef_descriptortype(key);
+    fields[1].descriptortype = upb_fielddef_descriptortype(val);
+    fields[0].offset = 0;
+    fields[1].offset = sizeof(upb_strview);
+    fields[1].submsg_index = 0;
+
+    if (upb_fielddef_type(val) == UPB_TYPE_MESSAGE) {
+      submsgs[0] = upb_fielddef_msgsubdef(val)->layout;
+    }
+
+    l->field_count = 2;
+    l->size = 2 * sizeof(upb_strview);
+    l->size = UPB_ALIGN_UP(l->size, 8);
+    return true;
+  }
+
+  /* Allocate data offsets in three stages:
+   *
+   * 1. hasbits.
+   * 2. regular fields.
+   * 3. oneof fields.
+   *
+   * OPT: There is a lot of room for optimization here to minimize the size.
+   */
+
+  /* Allocate hasbits and set basic field attributes. */
+  submsg_count = 0;
+  for (upb_msg_field_begin(&it, m), hasbit = 0;
+       !upb_msg_field_done(&it);
+       upb_msg_field_next(&it)) {
+    upb_fielddef* f = upb_msg_iter_field(&it);
+    upb_msglayout_field *field = &fields[upb_fielddef_index(f)];
+
+    field->number = upb_fielddef_number(f);
+    field->descriptortype = upb_fielddef_descriptortype(f);
+    field->label = upb_fielddef_label(f);
+
+    if (upb_fielddef_ismap(f)) {
+      field->label = _UPB_LABEL_MAP;
+    } else if (upb_fielddef_packed(f)) {
+      field->label = _UPB_LABEL_PACKED;
+    }
+
+    if (upb_fielddef_issubmsg(f)) {
+      const upb_msgdef *subm = upb_fielddef_msgsubdef(f);
+      field->submsg_index = submsg_count++;
+      submsgs[field->submsg_index] = subm->layout;
+    }
+
+    if (upb_fielddef_haspresence(f) && !upb_fielddef_realcontainingoneof(f)) {
+      /* We don't use hasbit 0, so that 0 can indicate "no presence" in the
+       * table. This wastes one hasbit, but we don't worry about it for now. */
+      field->presence = ++hasbit;
+    } else {
+      field->presence = 0;
+    }
+  }
+
+  /* Account for space used by hasbits. */
+  l->size = div_round_up(hasbit, 8);
+
+  /* Allocate non-oneof fields. */
+  for (upb_msg_field_begin(&it, m); !upb_msg_field_done(&it);
+       upb_msg_field_next(&it)) {
+    const upb_fielddef* f = upb_msg_iter_field(&it);
+    size_t field_size = upb_msg_fielddefsize(f);
+    size_t index = upb_fielddef_index(f);
+
+    if (upb_fielddef_realcontainingoneof(f)) {
+      /* Oneofs are handled separately below. */
+      continue;
+    }
+
+    fields[index].offset = upb_msglayout_place(l, field_size);
+  }
+
+  /* Allocate oneof fields.  Each oneof field consists of a uint32 for the case
+   * and space for the actual data. */
+  for (upb_msg_oneof_begin(&oit, m); !upb_msg_oneof_done(&oit);
+       upb_msg_oneof_next(&oit)) {
+    const upb_oneofdef* o = upb_msg_iter_oneof(&oit);
+    upb_oneof_iter fit;
+
+    size_t case_size = sizeof(uint32_t);  /* Could potentially optimize this. */
+    size_t field_size = 0;
+    uint32_t case_offset;
+    uint32_t data_offset;
+
+    if (upb_oneofdef_issynthetic(o)) continue;
+
+    /* Calculate field size: the max of all field sizes. */
+    for (upb_oneof_begin(&fit, o);
+         !upb_oneof_done(&fit);
+         upb_oneof_next(&fit)) {
+      const upb_fielddef* f = upb_oneof_iter_field(&fit);
+      field_size = UPB_MAX(field_size, upb_msg_fielddefsize(f));
+    }
+
+    /* Align and allocate case offset. */
+    case_offset = upb_msglayout_place(l, case_size);
+    data_offset = upb_msglayout_place(l, field_size);
+
+    for (upb_oneof_begin(&fit, o);
+         !upb_oneof_done(&fit);
+         upb_oneof_next(&fit)) {
+      const upb_fielddef* f = upb_oneof_iter_field(&fit);
+      fields[upb_fielddef_index(f)].offset = data_offset;
+      fields[upb_fielddef_index(f)].presence = ~case_offset;
+    }
+  }
+
+  /* Size of the entire structure should be a multiple of its greatest
+   * alignment.  TODO: track overall alignment for real? */
+  l->size = UPB_ALIGN_UP(l->size, 8);
+
+  /* Sort fields by number. */
+  qsort(fields, upb_msgdef_numfields(m), sizeof(*fields), field_number_cmp);
+  assign_layout_indices(m, fields);
+
+  return true;
+}
+
+/* Code to build defs from descriptor protos. *********************************/
+
+/* There is a question of how much validation to do here.  It will be difficult
+ * to perfectly match the amount of validation performed by proto2.  But since
+ * this code is used to directly build defs from Ruby (for example) we do need
+ * to validate important constraints like uniqueness of names and numbers. */
+
+#define CHK(x) if (!(x)) { return false; }
+#define CHK_OOM(x) if (!(x)) { upb_status_setoom(ctx->status); return false; }
+
+typedef struct {
+  const upb_symtab *symtab;
+  upb_filedef *file;              /* File we are building. */
+  upb_alloc *alloc;               /* Allocate defs here. */
+  upb_alloc *tmp;                 /* Alloc for addtab and any other tmp data. */
+  upb_strtable *addtab;           /* full_name -> packed def ptr for new defs */
+  const upb_msglayout **layouts;  /* NULL if we should build layouts. */
+  upb_status *status;             /* Record errors here. */
+} symtab_addctx;
+
+static char* strviewdup(const symtab_addctx *ctx, upb_strview view) {
+  return upb_strdup2(view.data, view.size, ctx->alloc);
+}
+
+static bool streql2(const char *a, size_t n, const char *b) {
+  return n == strlen(b) && memcmp(a, b, n) == 0;
+}
+
+static bool streql_view(upb_strview view, const char *b) {
+  return streql2(view.data, view.size, b);
+}
+
+static const char *makefullname(const symtab_addctx *ctx, const char *prefix,
+                                upb_strview name) {
+  if (prefix) {
+    /* ret = prefix + '.' + name; */
+    size_t n = strlen(prefix);
+    char *ret = upb_malloc(ctx->alloc, n + name.size + 2);
+    CHK_OOM(ret);
+    strcpy(ret, prefix);
+    ret[n] = '.';
+    memcpy(&ret[n + 1], name.data, name.size);
+    ret[n + 1 + name.size] = '\0';
+    return ret;
+  } else {
+    return strviewdup(ctx, name);
+  }
+}
+
+size_t getjsonname(const char *name, char *buf, size_t len) {
+  size_t src, dst = 0;
+  bool ucase_next = false;
+
+#define WRITE(byte) \
+  ++dst; \
+  if (dst < len) buf[dst - 1] = byte; \
+  else if (dst == len) buf[dst - 1] = '\0'
+
+  if (!name) {
+    WRITE('\0');
+    return 0;
+  }
+
+  /* Implement the transformation as described in the spec:
+   *   1. upper case all letters after an underscore.
+   *   2. remove all underscores.
+   */
+  for (src = 0; name[src]; src++) {
+    if (name[src] == '_') {
+      ucase_next = true;
+      continue;
+    }
+
+    if (ucase_next) {
+      WRITE(toupper(name[src]));
+      ucase_next = false;
+    } else {
+      WRITE(name[src]);
+    }
+  }
+
+  WRITE('\0');
+  return dst;
+
+#undef WRITE
+}
+
+static char* makejsonname(const char* name, upb_alloc *alloc) {
+  size_t size = getjsonname(name, NULL, 0);
+  char* json_name = upb_malloc(alloc, size);
+  getjsonname(name, json_name, size);
+  return json_name;
+}
+
+static bool symtab_add(const symtab_addctx *ctx, const char *name,
+                       upb_value v) {
+  upb_value tmp;
+  if (upb_strtable_lookup(ctx->addtab, name, &tmp) ||
+      upb_strtable_lookup(&ctx->symtab->syms, name, &tmp)) {
+    upb_status_seterrf(ctx->status, "duplicate symbol '%s'", name);
+    return false;
+  }
+
+  CHK_OOM(upb_strtable_insert3(ctx->addtab, name, strlen(name), v, ctx->tmp));
+  return true;
+}
+
+/* Given a symbol and the base symbol inside which it is defined, find the
+ * symbol's definition in t. */
+static bool resolvename(const upb_strtable *t, const upb_fielddef *f,
+                        const char *base, upb_strview sym,
+                        upb_deftype_t type, upb_status *status,
+                        const void **def) {
+  if(sym.size == 0) return false;
+  if(sym.data[0] == '.') {
+    /* Symbols starting with '.' are absolute, so we do a single lookup.
+     * Slice to omit the leading '.' */
+    upb_value v;
+    if (!upb_strtable_lookup2(t, sym.data + 1, sym.size - 1, &v)) {
+      return false;
+    }
+
+    *def = unpack_def(v, type);
+
+    if (!*def) {
+      upb_status_seterrf(status,
+                         "type mismatch when resolving field %s, name %s",
+                         f->full_name, sym.data);
+      return false;
+    }
+
+    return true;
+  } else {
+    /* Remove components from base until we find an entry or run out.
+     * TODO: This branch is totally broken, but currently not used. */
+    (void)base;
+    UPB_ASSERT(false);
+    return false;
+  }
+}
+
+const void *symtab_resolve(const symtab_addctx *ctx, const upb_fielddef *f,
+                           const char *base, upb_strview sym,
+                           upb_deftype_t type) {
+  const void *ret;
+  if (!resolvename(ctx->addtab, f, base, sym, type, ctx->status, &ret) &&
+      !resolvename(&ctx->symtab->syms, f, base, sym, type, ctx->status, &ret)) {
+    if (upb_ok(ctx->status)) {
+      upb_status_seterrf(ctx->status, "couldn't resolve name '%s'", sym.data);
+    }
+    return false;
+  }
+  return ret;
+}
+
+static bool create_oneofdef(
+    const symtab_addctx *ctx, upb_msgdef *m,
+    const google_protobuf_OneofDescriptorProto *oneof_proto) {
+  upb_oneofdef *o;
+  upb_strview name = google_protobuf_OneofDescriptorProto_name(oneof_proto);
+  upb_value v;
+
+  o = (upb_oneofdef*)&m->oneofs[m->oneof_count++];
+  o->parent = m;
+  o->full_name = makefullname(ctx, m->full_name, name);
+
+  v = pack_def(o, UPB_DEFTYPE_ONEOF);
+  CHK_OOM(symtab_add(ctx, o->full_name, v));
+  CHK_OOM(upb_strtable_insert3(&m->ntof, name.data, name.size, v, ctx->alloc));
+
+  CHK_OOM(upb_inttable_init2(&o->itof, UPB_CTYPE_CONSTPTR, ctx->alloc));
+  CHK_OOM(upb_strtable_init2(&o->ntof, UPB_CTYPE_CONSTPTR, ctx->alloc));
+
+  return true;
+}
+
+static bool parse_default(const symtab_addctx *ctx, const char *str, size_t len,
+                          upb_fielddef *f) {
+  char *end;
+  char nullz[64];
+  errno = 0;
+
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_INT64:
+    case UPB_TYPE_UINT32:
+    case UPB_TYPE_UINT64:
+    case UPB_TYPE_DOUBLE:
+    case UPB_TYPE_FLOAT:
+      /* Standard C number parsing functions expect null-terminated strings. */
+      if (len >= sizeof(nullz) - 1) {
+        return false;
+      }
+      memcpy(nullz, str, len);
+      nullz[len] = '\0';
+      str = nullz;
+      break;
+    default:
+      break;
+  }
+
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_INT32: {
+      long val = strtol(str, &end, 0);
+      CHK(val <= INT32_MAX && val >= INT32_MIN && errno != ERANGE && !*end);
+      f->defaultval.sint = val;
+      break;
+    }
+    case UPB_TYPE_ENUM: {
+      const upb_enumdef *e = f->sub.enumdef;
+      int32_t val;
+      CHK(upb_enumdef_ntoi(e, str, len, &val));
+      f->defaultval.sint = val;
+      break;
+    }
+    case UPB_TYPE_INT64: {
+      /* XXX: Need to write our own strtoll, since it's not available in c89. */
+      int64_t val = strtol(str, &end, 0);
+      CHK(val <= INT64_MAX && val >= INT64_MIN && errno != ERANGE && !*end);
+      f->defaultval.sint = val;
+      break;
+    }
+    case UPB_TYPE_UINT32: {
+      unsigned long val = strtoul(str, &end, 0);
+      CHK(val <= UINT32_MAX && errno != ERANGE && !*end);
+      f->defaultval.uint = val;
+      break;
+    }
+    case UPB_TYPE_UINT64: {
+      /* XXX: Need to write our own strtoull, since it's not available in c89. */
+      uint64_t val = strtoul(str, &end, 0);
+      CHK(val <= UINT64_MAX && errno != ERANGE && !*end);
+      f->defaultval.uint = val;
+      break;
+    }
+    case UPB_TYPE_DOUBLE: {
+      double val = strtod(str, &end);
+      CHK(errno != ERANGE && !*end);
+      f->defaultval.dbl = val;
+      break;
+    }
+    case UPB_TYPE_FLOAT: {
+      /* XXX: Need to write our own strtof, since it's not available in c89. */
+      float val = strtod(str, &end);
+      CHK(errno != ERANGE && !*end);
+      f->defaultval.flt = val;
+      break;
+    }
+    case UPB_TYPE_BOOL: {
+      if (streql2(str, len, "false")) {
+        f->defaultval.boolean = false;
+      } else if (streql2(str, len, "true")) {
+        f->defaultval.boolean = true;
+      } else {
+        return false;
+      }
+      break;
+    }
+    case UPB_TYPE_STRING:
+      f->defaultval.str = newstr(ctx->alloc, str, len);
+      break;
+    case UPB_TYPE_BYTES:
+      /* XXX: need to interpret the C-escaped value. */
+      f->defaultval.str = newstr(ctx->alloc, str, len);
+      break;
+    case UPB_TYPE_MESSAGE:
+      /* Should not have a default value. */
+      return false;
+  }
+  return true;
+}
+
+static void set_default_default(const symtab_addctx *ctx, upb_fielddef *f) {
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_INT64:
+    case UPB_TYPE_ENUM:
+      f->defaultval.sint = 0;
+      break;
+    case UPB_TYPE_UINT64:
+    case UPB_TYPE_UINT32:
+      f->defaultval.uint = 0;
+      break;
+    case UPB_TYPE_DOUBLE:
+    case UPB_TYPE_FLOAT:
+      f->defaultval.dbl = 0;
+      break;
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      f->defaultval.str = newstr(ctx->alloc, NULL, 0);
+      break;
+    case UPB_TYPE_BOOL:
+      f->defaultval.boolean = false;
+      break;
+    case UPB_TYPE_MESSAGE:
+      break;
+  }
+}
+
+static bool create_fielddef(
+    const symtab_addctx *ctx, const char *prefix, upb_msgdef *m,
+    const google_protobuf_FieldDescriptorProto *field_proto) {
+  upb_alloc *alloc = ctx->alloc;
+  upb_fielddef *f;
+  const google_protobuf_FieldOptions *options;
+  upb_strview name;
+  const char *full_name;
+  const char *json_name;
+  const char *shortname;
+  uint32_t field_number;
+
+  if (!google_protobuf_FieldDescriptorProto_has_name(field_proto)) {
+    upb_status_seterrmsg(ctx->status, "field has no name");
+    return false;
+  }
+
+  name = google_protobuf_FieldDescriptorProto_name(field_proto);
+  CHK(upb_isident(name, false, ctx->status));
+  full_name = makefullname(ctx, prefix, name);
+  shortname = shortdefname(full_name);
+
+  if (google_protobuf_FieldDescriptorProto_has_json_name(field_proto)) {
+    json_name = strviewdup(
+        ctx, google_protobuf_FieldDescriptorProto_json_name(field_proto));
+  } else {
+    json_name = makejsonname(shortname, ctx->alloc);
+  }
+
+  field_number = google_protobuf_FieldDescriptorProto_number(field_proto);
+
+  if (field_number == 0 || field_number > UPB_MAX_FIELDNUMBER) {
+    upb_status_seterrf(ctx->status, "invalid field number (%u)", field_number);
+    return false;
+  }
+
+  if (m) {
+    /* direct message field. */
+    upb_value v, field_v, json_v;
+    size_t json_size;
+
+    f = (upb_fielddef*)&m->fields[m->field_count++];
+    f->msgdef = m;
+    f->is_extension_ = false;
+
+    if (upb_strtable_lookup(&m->ntof, shortname, NULL)) {
+      upb_status_seterrf(ctx->status, "duplicate field name (%s)", shortname);
+      return false;
+    }
+
+    if (upb_strtable_lookup(&m->ntof, json_name, NULL)) {
+      upb_status_seterrf(ctx->status, "duplicate json_name (%s)", json_name);
+      return false;
+    }
+
+    if (upb_inttable_lookup(&m->itof, field_number, NULL)) {
+      upb_status_seterrf(ctx->status, "duplicate field number (%u)",
+                         field_number);
+      return false;
+    }
+
+    field_v = pack_def(f, UPB_DEFTYPE_FIELD);
+    json_v = pack_def(f, UPB_DEFTYPE_FIELD_JSONNAME);
+    v = upb_value_constptr(f);
+    json_size = strlen(json_name);
+
+    CHK_OOM(
+        upb_strtable_insert3(&m->ntof, name.data, name.size, field_v, alloc));
+    CHK_OOM(upb_inttable_insert2(&m->itof, field_number, v, alloc));
+
+    if (strcmp(shortname, json_name) != 0) {
+      upb_strtable_insert3(&m->ntof, json_name, json_size, json_v, alloc);
+    }
+
+    if (ctx->layouts) {
+      const upb_msglayout_field *fields = m->layout->fields;
+      int count = m->layout->field_count;
+      bool found = false;
+      int i;
+      for (i = 0; i < count; i++) {
+        if (fields[i].number == field_number) {
+          f->layout_index = i;
+          found = true;
+          break;
+        }
+      }
+      UPB_ASSERT(found);
+    }
+  } else {
+    /* extension field. */
+    f = (upb_fielddef*)&ctx->file->exts[ctx->file->ext_count++];
+    f->is_extension_ = true;
+    CHK_OOM(symtab_add(ctx, full_name, pack_def(f, UPB_DEFTYPE_FIELD)));
+  }
+
+  f->full_name = full_name;
+  f->json_name = json_name;
+  f->file = ctx->file;
+  f->type_ = (int)google_protobuf_FieldDescriptorProto_type(field_proto);
+  f->label_ = (int)google_protobuf_FieldDescriptorProto_label(field_proto);
+  f->number_ = field_number;
+  f->oneof = NULL;
+  f->proto3_optional_ =
+      google_protobuf_FieldDescriptorProto_proto3_optional(field_proto);
+
+  /* We can't resolve the subdef or (in the case of extensions) the containing
+   * message yet, because it may not have been defined yet.  We stash a pointer
+   * to the field_proto until later when we can properly resolve it. */
+  f->sub.unresolved = field_proto;
+
+  if (f->label_ == UPB_LABEL_REQUIRED && f->file->syntax == UPB_SYNTAX_PROTO3) {
+    upb_status_seterrf(ctx->status, "proto3 fields cannot be required (%s)",
+                       f->full_name);
+    return false;
+  }
+
+  if (google_protobuf_FieldDescriptorProto_has_oneof_index(field_proto)) {
+    int oneof_index =
+        google_protobuf_FieldDescriptorProto_oneof_index(field_proto);
+    upb_oneofdef *oneof;
+    upb_value v = upb_value_constptr(f);
+
+    if (upb_fielddef_label(f) != UPB_LABEL_OPTIONAL) {
+      upb_status_seterrf(ctx->status,
+                         "fields in oneof must have OPTIONAL label (%s)",
+                         f->full_name);
+      return false;
+    }
+
+    if (!m) {
+      upb_status_seterrf(ctx->status,
+                         "oneof_index provided for extension field (%s)",
+                         f->full_name);
+      return false;
+    }
+
+    if (oneof_index >= m->oneof_count) {
+      upb_status_seterrf(ctx->status, "oneof_index out of range (%s)",
+                         f->full_name);
+      return false;
+    }
+
+    oneof = (upb_oneofdef*)&m->oneofs[oneof_index];
+    f->oneof = oneof;
+
+    CHK(upb_inttable_insert2(&oneof->itof, f->number_, v, alloc));
+    CHK(upb_strtable_insert3(&oneof->ntof, name.data, name.size, v, alloc));
+  } else {
+    f->oneof = NULL;
+  }
+
+  if (google_protobuf_FieldDescriptorProto_has_options(field_proto)) {
+    options = google_protobuf_FieldDescriptorProto_options(field_proto);
+    f->lazy_ = google_protobuf_FieldOptions_lazy(options);
+    f->packed_ = google_protobuf_FieldOptions_packed(options);
+  } else {
+    f->lazy_ = false;
+    f->packed_ = false;
+  }
+
+  return true;
+}
+
+static bool create_enumdef(
+    const symtab_addctx *ctx, const char *prefix,
+    const google_protobuf_EnumDescriptorProto *enum_proto) {
+  upb_enumdef *e;
+  const google_protobuf_EnumValueDescriptorProto *const *values;
+  upb_strview name;
+  size_t i, n;
+
+  name = google_protobuf_EnumDescriptorProto_name(enum_proto);
+  CHK(upb_isident(name, false, ctx->status));
+
+  e = (upb_enumdef*)&ctx->file->enums[ctx->file->enum_count++];
+  e->full_name = makefullname(ctx, prefix, name);
+  CHK_OOM(symtab_add(ctx, e->full_name, pack_def(e, UPB_DEFTYPE_ENUM)));
+
+  CHK_OOM(upb_strtable_init2(&e->ntoi, UPB_CTYPE_INT32, ctx->alloc));
+  CHK_OOM(upb_inttable_init2(&e->iton, UPB_CTYPE_CSTR, ctx->alloc));
+
+  e->file = ctx->file;
+  e->defaultval = 0;
+
+  values = google_protobuf_EnumDescriptorProto_value(enum_proto, &n);
+
+  if (n == 0) {
+    upb_status_seterrf(ctx->status,
+                       "enums must contain at least one value (%s)",
+                       e->full_name);
+    return false;
+  }
+
+  for (i = 0; i < n; i++) {
+    const google_protobuf_EnumValueDescriptorProto *value = values[i];
+    upb_strview name = google_protobuf_EnumValueDescriptorProto_name(value);
+    char *name2 = strviewdup(ctx, name);
+    int32_t num = google_protobuf_EnumValueDescriptorProto_number(value);
+    upb_value v = upb_value_int32(num);
+
+    if (i == 0 && e->file->syntax == UPB_SYNTAX_PROTO3 && num != 0) {
+      upb_status_seterrf(ctx->status,
+                         "for proto3, the first enum value must be zero (%s)",
+                         e->full_name);
+      return false;
+    }
+
+    if (upb_strtable_lookup(&e->ntoi, name2, NULL)) {
+      upb_status_seterrf(ctx->status, "duplicate enum label '%s'", name2);
+      return false;
+    }
+
+    CHK_OOM(name2)
+    CHK_OOM(
+        upb_strtable_insert3(&e->ntoi, name2, strlen(name2), v, ctx->alloc));
+
+    if (!upb_inttable_lookup(&e->iton, num, NULL)) {
+      upb_value v = upb_value_cstr(name2);
+      CHK_OOM(upb_inttable_insert2(&e->iton, num, v, ctx->alloc));
+    }
+  }
+
+  upb_inttable_compact2(&e->iton, ctx->alloc);
+
+  return true;
+}
+
+static bool create_msgdef(symtab_addctx *ctx, const char *prefix,
+                          const google_protobuf_DescriptorProto *msg_proto) {
+  upb_msgdef *m;
+  const google_protobuf_MessageOptions *options;
+  const google_protobuf_OneofDescriptorProto *const *oneofs;
+  const google_protobuf_FieldDescriptorProto *const *fields;
+  const google_protobuf_EnumDescriptorProto *const *enums;
+  const google_protobuf_DescriptorProto *const *msgs;
+  size_t i, n;
+  upb_strview name;
+
+  name = google_protobuf_DescriptorProto_name(msg_proto);
+  CHK(upb_isident(name, false, ctx->status));
+
+  m = (upb_msgdef*)&ctx->file->msgs[ctx->file->msg_count++];
+  m->full_name = makefullname(ctx, prefix, name);
+  CHK_OOM(symtab_add(ctx, m->full_name, pack_def(m, UPB_DEFTYPE_MSG)));
+
+  CHK_OOM(upb_inttable_init2(&m->itof, UPB_CTYPE_CONSTPTR, ctx->alloc));
+  CHK_OOM(upb_strtable_init2(&m->ntof, UPB_CTYPE_CONSTPTR, ctx->alloc));
+
+  m->file = ctx->file;
+  m->map_entry = false;
+
+  options = google_protobuf_DescriptorProto_options(msg_proto);
+
+  if (options) {
+    m->map_entry = google_protobuf_MessageOptions_map_entry(options);
+  }
+
+  if (ctx->layouts) {
+    m->layout = *ctx->layouts;
+    ctx->layouts++;
+  } else {
+    /* Allocate now (to allow cross-linking), populate later. */
+    m->layout = upb_malloc(ctx->alloc, sizeof(*m->layout));
+  }
+
+  oneofs = google_protobuf_DescriptorProto_oneof_decl(msg_proto, &n);
+  m->oneof_count = 0;
+  m->oneofs = upb_malloc(ctx->alloc, sizeof(*m->oneofs) * n);
+  for (i = 0; i < n; i++) {
+    CHK(create_oneofdef(ctx, m, oneofs[i]));
+  }
+
+  fields = google_protobuf_DescriptorProto_field(msg_proto, &n);
+  m->field_count = 0;
+  m->fields = upb_malloc(ctx->alloc, sizeof(*m->fields) * n);
+  for (i = 0; i < n; i++) {
+    CHK(create_fielddef(ctx, m->full_name, m, fields[i]));
+  }
+
+  CHK(assign_msg_indices(m, ctx->status));
+  CHK(check_oneofs(m, ctx->status));
+  assign_msg_wellknowntype(m);
+  upb_inttable_compact2(&m->itof, ctx->alloc);
+
+  /* This message is built.  Now build nested messages and enums. */
+
+  enums = google_protobuf_DescriptorProto_enum_type(msg_proto, &n);
+  for (i = 0; i < n; i++) {
+    CHK(create_enumdef(ctx, m->full_name, enums[i]));
+  }
+
+  msgs = google_protobuf_DescriptorProto_nested_type(msg_proto, &n);
+  for (i = 0; i < n; i++) {
+    CHK(create_msgdef(ctx, m->full_name, msgs[i]));
+  }
+
+  return true;
+}
+
+typedef struct {
+  int msg_count;
+  int enum_count;
+  int ext_count;
+} decl_counts;
+
+static void count_types_in_msg(const google_protobuf_DescriptorProto *msg_proto,
+                               decl_counts *counts) {
+  const google_protobuf_DescriptorProto *const *msgs;
+  size_t i, n;
+
+  counts->msg_count++;
+
+  msgs = google_protobuf_DescriptorProto_nested_type(msg_proto, &n);
+  for (i = 0; i < n; i++) {
+    count_types_in_msg(msgs[i], counts);
+  }
+
+  google_protobuf_DescriptorProto_enum_type(msg_proto, &n);
+  counts->enum_count += n;
+
+  google_protobuf_DescriptorProto_extension(msg_proto, &n);
+  counts->ext_count += n;
+}
+
+static void count_types_in_file(
+    const google_protobuf_FileDescriptorProto *file_proto,
+    decl_counts *counts) {
+  const google_protobuf_DescriptorProto *const *msgs;
+  size_t i, n;
+
+  msgs = google_protobuf_FileDescriptorProto_message_type(file_proto, &n);
+  for (i = 0; i < n; i++) {
+    count_types_in_msg(msgs[i], counts);
+  }
+
+  google_protobuf_FileDescriptorProto_enum_type(file_proto, &n);
+  counts->enum_count += n;
+
+  google_protobuf_FileDescriptorProto_extension(file_proto, &n);
+  counts->ext_count += n;
+}
+
+static bool resolve_fielddef(const symtab_addctx *ctx, const char *prefix,
+                             upb_fielddef *f) {
+  upb_strview name;
+  const google_protobuf_FieldDescriptorProto *field_proto = f->sub.unresolved;
+
+  if (f->is_extension_) {
+    if (!google_protobuf_FieldDescriptorProto_has_extendee(field_proto)) {
+      upb_status_seterrf(ctx->status,
+                         "extension for field '%s' had no extendee",
+                         f->full_name);
+      return false;
+    }
+
+    name = google_protobuf_FieldDescriptorProto_extendee(field_proto);
+    f->msgdef = symtab_resolve(ctx, f, prefix, name, UPB_DEFTYPE_MSG);
+    CHK(f->msgdef);
+  }
+
+  if ((upb_fielddef_issubmsg(f) || f->type_ == UPB_DESCRIPTOR_TYPE_ENUM) &&
+      !google_protobuf_FieldDescriptorProto_has_type_name(field_proto)) {
+    upb_status_seterrf(ctx->status, "field '%s' is missing type name",
+                       f->full_name);
+    return false;
+  }
+
+  name = google_protobuf_FieldDescriptorProto_type_name(field_proto);
+
+  if (upb_fielddef_issubmsg(f)) {
+    f->sub.msgdef = symtab_resolve(ctx, f, prefix, name, UPB_DEFTYPE_MSG);
+    CHK(f->sub.msgdef);
+  } else if (f->type_ == UPB_DESCRIPTOR_TYPE_ENUM) {
+    f->sub.enumdef = symtab_resolve(ctx, f, prefix, name, UPB_DEFTYPE_ENUM);
+    CHK(f->sub.enumdef);
+  }
+
+  /* Have to delay resolving of the default value until now because of the enum
+   * case, since enum defaults are specified with a label. */
+  if (google_protobuf_FieldDescriptorProto_has_default_value(field_proto)) {
+    upb_strview defaultval =
+        google_protobuf_FieldDescriptorProto_default_value(field_proto);
+
+    if (f->file->syntax == UPB_SYNTAX_PROTO3) {
+      upb_status_seterrf(ctx->status,
+                         "proto3 fields cannot have explicit defaults (%s)",
+                         f->full_name);
+      return false;
+    }
+
+    if (upb_fielddef_issubmsg(f)) {
+      upb_status_seterrf(ctx->status,
+                         "message fields cannot have explicit defaults (%s)",
+                         f->full_name);
+      return false;
+    }
+
+    if (!parse_default(ctx, defaultval.data, defaultval.size, f)) {
+      upb_status_seterrf(ctx->status,
+                         "couldn't parse default '" UPB_STRVIEW_FORMAT
+                         "' for field (%s)",
+                         UPB_STRVIEW_ARGS(defaultval), f->full_name);
+      return false;
+    }
+  } else {
+    set_default_default(ctx, f);
+  }
+
+  return true;
+}
+
+static bool build_filedef(
+    symtab_addctx *ctx, upb_filedef *file,
+    const google_protobuf_FileDescriptorProto *file_proto) {
+  upb_alloc *alloc = ctx->alloc;
+  const google_protobuf_FileOptions *file_options_proto;
+  const google_protobuf_DescriptorProto *const *msgs;
+  const google_protobuf_EnumDescriptorProto *const *enums;
+  const google_protobuf_FieldDescriptorProto *const *exts;
+  const upb_strview* strs;
+  size_t i, n;
+  decl_counts counts = {0};
+
+  count_types_in_file(file_proto, &counts);
+
+  file->msgs = upb_malloc(alloc, sizeof(*file->msgs) * counts.msg_count);
+  file->enums = upb_malloc(alloc, sizeof(*file->enums) * counts.enum_count);
+  file->exts = upb_malloc(alloc, sizeof(*file->exts) * counts.ext_count);
+
+  CHK_OOM(counts.msg_count == 0 || file->msgs);
+  CHK_OOM(counts.enum_count == 0 || file->enums);
+  CHK_OOM(counts.ext_count == 0 || file->exts);
+
+  /* We increment these as defs are added. */
+  file->msg_count = 0;
+  file->enum_count = 0;
+  file->ext_count = 0;
+
+  if (!google_protobuf_FileDescriptorProto_has_name(file_proto)) {
+    upb_status_seterrmsg(ctx->status, "File has no name");
+    return false;
+  }
+
+  file->name =
+      strviewdup(ctx, google_protobuf_FileDescriptorProto_name(file_proto));
+  file->phpprefix = NULL;
+  file->phpnamespace = NULL;
+
+  if (google_protobuf_FileDescriptorProto_has_package(file_proto)) {
+    upb_strview package =
+        google_protobuf_FileDescriptorProto_package(file_proto);
+    CHK(upb_isident(package, true, ctx->status));
+    file->package = strviewdup(ctx, package);
+  } else {
+    file->package = NULL;
+  }
+
+  if (google_protobuf_FileDescriptorProto_has_syntax(file_proto)) {
+    upb_strview syntax =
+        google_protobuf_FileDescriptorProto_syntax(file_proto);
+
+    if (streql_view(syntax, "proto2")) {
+      file->syntax = UPB_SYNTAX_PROTO2;
+    } else if (streql_view(syntax, "proto3")) {
+      file->syntax = UPB_SYNTAX_PROTO3;
+    } else {
+      upb_status_seterrf(ctx->status, "Invalid syntax '" UPB_STRVIEW_FORMAT "'",
+                         UPB_STRVIEW_ARGS(syntax));
+      return false;
+    }
+  } else {
+    file->syntax = UPB_SYNTAX_PROTO2;
+  }
+
+  /* Read options. */
+  file_options_proto = google_protobuf_FileDescriptorProto_options(file_proto);
+  if (file_options_proto) {
+    if (google_protobuf_FileOptions_has_php_class_prefix(file_options_proto)) {
+      file->phpprefix = strviewdup(
+          ctx,
+          google_protobuf_FileOptions_php_class_prefix(file_options_proto));
+    }
+    if (google_protobuf_FileOptions_has_php_namespace(file_options_proto)) {
+      file->phpnamespace = strviewdup(
+          ctx, google_protobuf_FileOptions_php_namespace(file_options_proto));
+    }
+  }
+
+  /* Verify dependencies. */
+  strs = google_protobuf_FileDescriptorProto_dependency(file_proto, &n);
+  file->deps = upb_malloc(alloc, sizeof(*file->deps) * n) ;
+  CHK_OOM(n == 0 || file->deps);
+
+  for (i = 0; i < n; i++) {
+    upb_strview dep_name = strs[i];
+    upb_value v;
+    if (!upb_strtable_lookup2(&ctx->symtab->files, dep_name.data,
+                              dep_name.size, &v)) {
+      upb_status_seterrf(ctx->status,
+                         "Depends on file '" UPB_STRVIEW_FORMAT
+                         "', but it has not been loaded",
+                         UPB_STRVIEW_ARGS(dep_name));
+      return false;
+    }
+    file->deps[i] = upb_value_getconstptr(v);
+  }
+
+  /* Create messages. */
+  msgs = google_protobuf_FileDescriptorProto_message_type(file_proto, &n);
+  for (i = 0; i < n; i++) {
+    CHK(create_msgdef(ctx, file->package, msgs[i]));
+  }
+
+  /* Create enums. */
+  enums = google_protobuf_FileDescriptorProto_enum_type(file_proto, &n);
+  for (i = 0; i < n; i++) {
+    CHK(create_enumdef(ctx, file->package, enums[i]));
+  }
+
+  /* Create extensions. */
+  exts = google_protobuf_FileDescriptorProto_extension(file_proto, &n);
+  file->exts = upb_malloc(alloc, sizeof(*file->exts) * n);
+  CHK_OOM(n == 0 || file->exts);
+  for (i = 0; i < n; i++) {
+    CHK(create_fielddef(ctx, file->package, NULL, exts[i]));
+  }
+
+  /* Now that all names are in the table, build layouts and resolve refs. */
+  for (i = 0; i < (size_t)file->ext_count; i++) {
+    CHK(resolve_fielddef(ctx, file->package, (upb_fielddef*)&file->exts[i]));
+  }
+
+  for (i = 0; i < (size_t)file->msg_count; i++) {
+    const upb_msgdef *m = &file->msgs[i];
+    int j;
+    for (j = 0; j < m->field_count; j++) {
+      CHK(resolve_fielddef(ctx, m->full_name, (upb_fielddef*)&m->fields[j]));
+    }
+  }
+
+  if (!ctx->layouts) {
+    for (i = 0; i < (size_t)file->msg_count; i++) {
+      const upb_msgdef *m = &file->msgs[i];
+      make_layout(ctx->symtab, m);
+    }
+  }
+
+  return true;
+ }
+
+static bool upb_symtab_addtotabs(upb_symtab *s, symtab_addctx *ctx) {
+  const upb_filedef *file = ctx->file;
+  upb_alloc *alloc = upb_arena_alloc(s->arena);
+  upb_strtable_iter iter;
+
+  CHK_OOM(upb_strtable_insert3(&s->files, file->name, strlen(file->name),
+                               upb_value_constptr(file), alloc));
+
+  upb_strtable_begin(&iter, ctx->addtab);
+  for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) {
+    upb_strview key = upb_strtable_iter_key(&iter);
+    upb_value value = upb_strtable_iter_value(&iter);
+    CHK_OOM(upb_strtable_insert3(&s->syms, key.data, key.size, value, alloc));
+  }
+
+  return true;
+}
+
+/* upb_filedef ****************************************************************/
+
+const char *upb_filedef_name(const upb_filedef *f) {
+  return f->name;
+}
+
+const char *upb_filedef_package(const upb_filedef *f) {
+  return f->package;
+}
+
+const char *upb_filedef_phpprefix(const upb_filedef *f) {
+  return f->phpprefix;
+}
+
+const char *upb_filedef_phpnamespace(const upb_filedef *f) {
+  return f->phpnamespace;
+}
+
+upb_syntax_t upb_filedef_syntax(const upb_filedef *f) {
+  return f->syntax;
+}
+
+int upb_filedef_msgcount(const upb_filedef *f) {
+  return f->msg_count;
+}
+
+int upb_filedef_depcount(const upb_filedef *f) {
+  return f->dep_count;
+}
+
+int upb_filedef_enumcount(const upb_filedef *f) {
+  return f->enum_count;
+}
+
+const upb_filedef *upb_filedef_dep(const upb_filedef *f, int i) {
+  return i < 0 || i >= f->dep_count ? NULL : f->deps[i];
+}
+
+const upb_msgdef *upb_filedef_msg(const upb_filedef *f, int i) {
+  return i < 0 || i >= f->msg_count ? NULL : &f->msgs[i];
+}
+
+const upb_enumdef *upb_filedef_enum(const upb_filedef *f, int i) {
+  return i < 0 || i >= f->enum_count ? NULL : &f->enums[i];
+}
+
+void upb_symtab_free(upb_symtab *s) {
+  upb_arena_free(s->arena);
+  upb_gfree(s);
+}
+
+upb_symtab *upb_symtab_new(void) {
+  upb_symtab *s = upb_gmalloc(sizeof(*s));
+  upb_alloc *alloc;
+
+  if (!s) {
+    return NULL;
+  }
+
+  s->arena = upb_arena_new();
+  alloc = upb_arena_alloc(s->arena);
+
+  if (!upb_strtable_init2(&s->syms, UPB_CTYPE_CONSTPTR, alloc) ||
+      !upb_strtable_init2(&s->files, UPB_CTYPE_CONSTPTR, alloc)) {
+    upb_arena_free(s->arena);
+    upb_gfree(s);
+    s = NULL;
+  }
+  return s;
+}
+
+const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym) {
+  upb_value v;
+  return upb_strtable_lookup(&s->syms, sym, &v) ?
+      unpack_def(v, UPB_DEFTYPE_MSG) : NULL;
+}
+
+const upb_msgdef *upb_symtab_lookupmsg2(const upb_symtab *s, const char *sym,
+                                        size_t len) {
+  upb_value v;
+  return upb_strtable_lookup2(&s->syms, sym, len, &v) ?
+      unpack_def(v, UPB_DEFTYPE_MSG) : NULL;
+}
+
+const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym) {
+  upb_value v;
+  return upb_strtable_lookup(&s->syms, sym, &v) ?
+      unpack_def(v, UPB_DEFTYPE_ENUM) : NULL;
+}
+
+const upb_filedef *upb_symtab_lookupfile(const upb_symtab *s, const char *name) {
+  upb_value v;
+  return upb_strtable_lookup(&s->files, name, &v) ? upb_value_getconstptr(v)
+                                                  : NULL;
+}
+
+const upb_filedef *upb_symtab_lookupfile2(
+    const upb_symtab *s, const char *name, size_t len) {
+  upb_value v;
+  return upb_strtable_lookup2(&s->files, name, len, &v) ?
+      upb_value_getconstptr(v) : NULL;
+}
+
+int upb_symtab_filecount(const upb_symtab *s) {
+  return (int)upb_strtable_count(&s->files);
+}
+
+static const upb_filedef *_upb_symtab_addfile(
+    upb_symtab *s, const google_protobuf_FileDescriptorProto *file_proto,
+    const upb_msglayout **layouts, upb_status *status) {
+  upb_arena *tmparena = upb_arena_new();
+  upb_strtable addtab;
+  upb_alloc *alloc = upb_arena_alloc(s->arena);
+  upb_filedef *file = upb_malloc(alloc, sizeof(*file));
+  bool ok;
+  symtab_addctx ctx;
+
+  ctx.file = file;
+  ctx.symtab = s;
+  ctx.alloc = alloc;
+  ctx.tmp = upb_arena_alloc(tmparena);
+  ctx.addtab = &addtab;
+  ctx.layouts = layouts;
+  ctx.status = status;
+
+  ok = file && upb_strtable_init2(&addtab, UPB_CTYPE_CONSTPTR, ctx.tmp) &&
+       build_filedef(&ctx, file, file_proto) && upb_symtab_addtotabs(s, &ctx);
+
+  upb_arena_free(tmparena);
+  return ok ? file : NULL;
+}
+
+const upb_filedef *upb_symtab_addfile(
+    upb_symtab *s, const google_protobuf_FileDescriptorProto *file_proto,
+    upb_status *status) {
+  return _upb_symtab_addfile(s, file_proto, NULL, status);
+}
+
+/* Include here since we want most of this file to be stdio-free. */
+#include <stdio.h>
+
+bool _upb_symtab_loaddefinit(upb_symtab *s, const upb_def_init *init) {
+  /* Since this function should never fail (it would indicate a bug in upb) we
+   * print errors to stderr instead of returning error status to the user. */
+  upb_def_init **deps = init->deps;
+  google_protobuf_FileDescriptorProto *file;
+  upb_arena *arena;
+  upb_status status;
+
+  upb_status_clear(&status);
+
+  if (upb_strtable_lookup(&s->files, init->filename, NULL)) {
+    return true;
+  }
+
+  arena = upb_arena_new();
+
+  for (; *deps; deps++) {
+    if (!_upb_symtab_loaddefinit(s, *deps)) goto err;
+  }
+
+  file = google_protobuf_FileDescriptorProto_parse(
+      init->descriptor.data, init->descriptor.size, arena);
+
+  if (!file) {
+    upb_status_seterrf(
+        &status,
+        "Failed to parse compiled-in descriptor for file '%s'. This should "
+        "never happen.",
+        init->filename);
+    goto err;
+  }
+
+  if (!_upb_symtab_addfile(s, file, init->layouts, &status)) goto err;
+
+  upb_arena_free(arena);
+  return true;
+
+err:
+  fprintf(stderr, "Error loading compiled-in descriptor: %s\n",
+          upb_status_errmsg(&status));
+  upb_arena_free(arena);
+  return false;
+}
+
+#undef CHK
+#undef CHK_OOM
+
+
+#include <string.h>
+
+
+static size_t get_field_size(const upb_msglayout_field *f) {
+  static unsigned char sizes[] = {
+    0,/* 0 */
+    8, /* UPB_DESCRIPTOR_TYPE_DOUBLE */
+    4, /* UPB_DESCRIPTOR_TYPE_FLOAT */
+    8, /* UPB_DESCRIPTOR_TYPE_INT64 */
+    8, /* UPB_DESCRIPTOR_TYPE_UINT64 */
+    4, /* UPB_DESCRIPTOR_TYPE_INT32 */
+    8, /* UPB_DESCRIPTOR_TYPE_FIXED64 */
+    4, /* UPB_DESCRIPTOR_TYPE_FIXED32 */
+    1, /* UPB_DESCRIPTOR_TYPE_BOOL */
+    sizeof(upb_strview), /* UPB_DESCRIPTOR_TYPE_STRING */
+    sizeof(void*), /* UPB_DESCRIPTOR_TYPE_GROUP */
+    sizeof(void*), /* UPB_DESCRIPTOR_TYPE_MESSAGE */
+    sizeof(upb_strview), /* UPB_DESCRIPTOR_TYPE_BYTES */
+    4, /* UPB_DESCRIPTOR_TYPE_UINT32 */
+    4, /* UPB_DESCRIPTOR_TYPE_ENUM */
+    4, /* UPB_DESCRIPTOR_TYPE_SFIXED32 */
+    8, /* UPB_DESCRIPTOR_TYPE_SFIXED64 */
+    4, /* UPB_DESCRIPTOR_TYPE_SINT32 */
+    8, /* UPB_DESCRIPTOR_TYPE_SINT64 */
+  };
+  return _upb_repeated_or_map(f) ? sizeof(void *) : sizes[f->descriptortype];
+}
+
+/* Strings/bytes are special-cased in maps. */
+static char _upb_fieldtype_to_mapsize[12] = {
+  0,
+  1,  /* UPB_TYPE_BOOL */
+  4,  /* UPB_TYPE_FLOAT */
+  4,  /* UPB_TYPE_INT32 */
+  4,  /* UPB_TYPE_UINT32 */
+  4,  /* UPB_TYPE_ENUM */
+  sizeof(void*),  /* UPB_TYPE_MESSAGE */
+  8,  /* UPB_TYPE_DOUBLE */
+  8,  /* UPB_TYPE_INT64 */
+  8,  /* UPB_TYPE_UINT64 */
+  0,  /* UPB_TYPE_STRING */
+  0,  /* UPB_TYPE_BYTES */
+};
+
+/** upb_msg *******************************************************************/
+
+upb_msg *upb_msg_new(const upb_msgdef *m, upb_arena *a) {
+  return _upb_msg_new(upb_msgdef_layout(m), a);
+}
+
+static bool in_oneof(const upb_msglayout_field *field) {
+  return field->presence < 0;
+}
+
+static upb_msgval _upb_msg_getraw(const upb_msg *msg, const upb_fielddef *f) {
+  const upb_msglayout_field *field = upb_fielddef_layout(f);
+  const char *mem = UPB_PTR_AT(msg, field->offset, char);
+  upb_msgval val = {0};
+  memcpy(&val, mem, get_field_size(field));
+  return val;
+}
+
+bool upb_msg_has(const upb_msg *msg, const upb_fielddef *f) {
+  const upb_msglayout_field *field = upb_fielddef_layout(f);
+  if (in_oneof(field)) {
+    return _upb_getoneofcase_field(msg, field) == field->number;
+  } else if (field->presence > 0) {
+    return _upb_hasbit_field(msg, field);
+  } else {
+    UPB_ASSERT(field->descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
+               field->descriptortype == UPB_DESCRIPTOR_TYPE_GROUP);
+    return _upb_msg_getraw(msg, f).msg_val != NULL;
+  }
+}
+
+const upb_fielddef *upb_msg_whichoneof(const upb_msg *msg,
+                                       const upb_oneofdef *o) {
+  upb_oneof_iter i;
+  const upb_fielddef *f;
+  const upb_msglayout_field *field;
+  const upb_msgdef *m = upb_oneofdef_containingtype(o);
+  uint32_t oneof_case;
+
+  /* This is far from optimal. */
+  upb_oneof_begin(&i, o);
+  if (upb_oneof_done(&i)) return false;
+  f = upb_oneof_iter_field(&i);
+  field = upb_fielddef_layout(f);
+  oneof_case = _upb_getoneofcase_field(msg, field);
+
+  return oneof_case ? upb_msgdef_itof(m, oneof_case) : NULL;
+}
+
+upb_msgval upb_msg_get(const upb_msg *msg, const upb_fielddef *f) {
+  if (!upb_fielddef_haspresence(f) || upb_msg_has(msg, f)) {
+    return _upb_msg_getraw(msg, f);
+  } else {
+    /* TODO(haberman): change upb_fielddef to not require this switch(). */
+    upb_msgval val = {0};
+    switch (upb_fielddef_type(f)) {
+      case UPB_TYPE_INT32:
+      case UPB_TYPE_ENUM:
+        val.int32_val = upb_fielddef_defaultint32(f);
+        break;
+      case UPB_TYPE_INT64:
+        val.int64_val = upb_fielddef_defaultint64(f);
+        break;
+      case UPB_TYPE_UINT32:
+        val.uint32_val = upb_fielddef_defaultuint32(f);
+        break;
+      case UPB_TYPE_UINT64:
+        val.uint64_val = upb_fielddef_defaultuint64(f);
+        break;
+      case UPB_TYPE_FLOAT:
+        val.float_val = upb_fielddef_defaultfloat(f);
+        break;
+      case UPB_TYPE_DOUBLE:
+        val.double_val = upb_fielddef_defaultdouble(f);
+        break;
+      case UPB_TYPE_BOOL:
+        val.double_val = upb_fielddef_defaultbool(f);
+        break;
+      case UPB_TYPE_STRING:
+      case UPB_TYPE_BYTES:
+        val.str_val.data = upb_fielddef_defaultstr(f, &val.str_val.size);
+        break;
+      case UPB_TYPE_MESSAGE:
+        val.msg_val = NULL;
+        break;
+    }
+    return val;
+  }
+}
+
+upb_mutmsgval upb_msg_mutable(upb_msg *msg, const upb_fielddef *f,
+                              upb_arena *a) {
+  const upb_msglayout_field *field = upb_fielddef_layout(f);
+  upb_mutmsgval ret;
+  char *mem = UPB_PTR_AT(msg, field->offset, char);
+  bool wrong_oneof =
+      in_oneof(field) && _upb_getoneofcase_field(msg, field) != field->number;
+
+  memcpy(&ret, mem, sizeof(void*));
+
+  if (a && (!ret.msg || wrong_oneof)) {
+    if (upb_fielddef_ismap(f)) {
+      const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+      const upb_fielddef *key = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
+      const upb_fielddef *value = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
+      ret.map = upb_map_new(a, upb_fielddef_type(key), upb_fielddef_type(value));
+    } else if (upb_fielddef_isseq(f)) {
+      ret.array = upb_array_new(a, upb_fielddef_type(f));
+    } else {
+      UPB_ASSERT(upb_fielddef_issubmsg(f));
+      ret.msg = upb_msg_new(upb_fielddef_msgsubdef(f), a);
+    }
+
+    memcpy(mem, &ret, sizeof(void*));
+
+    if (wrong_oneof) {
+      *_upb_oneofcase_field(msg, field) = field->number;
+    } else if (field->presence > 0) {
+      _upb_sethas_field(msg, field);
+    }
+  }
+  return ret;
+}
+
+void upb_msg_set(upb_msg *msg, const upb_fielddef *f, upb_msgval val,
+                 upb_arena *a) {
+  const upb_msglayout_field *field = upb_fielddef_layout(f);
+  char *mem = UPB_PTR_AT(msg, field->offset, char);
+  UPB_UNUSED(a);  /* We reserve the right to make set insert into a map. */
+  memcpy(mem, &val, get_field_size(field));
+  if (field->presence > 0) {
+    _upb_sethas_field(msg, field);
+  } else if (in_oneof(field)) {
+    *_upb_oneofcase_field(msg, field) = field->number;
+  }
+}
+
+void upb_msg_clearfield(upb_msg *msg, const upb_fielddef *f) {
+  const upb_msglayout_field *field = upb_fielddef_layout(f);
+  char *mem = UPB_PTR_AT(msg, field->offset, char);
+
+  if (field->presence > 0) {
+    _upb_clearhas_field(msg, field);
+  } else if (in_oneof(field)) {
+    uint32_t *oneof_case = _upb_oneofcase_field(msg, field);
+    if (*oneof_case != field->number) return;
+    *oneof_case = 0;
+  }
+
+  memset(mem, 0, get_field_size(field));
+}
+
+void upb_msg_clear(upb_msg *msg, const upb_msgdef *m) {
+  _upb_msg_clear(msg, upb_msgdef_layout(m));
+}
+
+bool upb_msg_next(const upb_msg *msg, const upb_msgdef *m,
+                  const upb_symtab *ext_pool, const upb_fielddef **out_f,
+                  upb_msgval *out_val, size_t *iter) {
+  size_t i = *iter;
+  const upb_msgval zero = {0};
+  const upb_fielddef *f;
+  UPB_UNUSED(ext_pool);
+  while ((f = _upb_msgdef_field(m, (int)++i)) != NULL) {
+    upb_msgval val = _upb_msg_getraw(msg, f);
+
+    /* Skip field if unset or empty. */
+    if (upb_fielddef_haspresence(f)) {
+      if (!upb_msg_has(msg, f)) continue;
+    } else {
+      upb_msgval test = val;
+      if (upb_fielddef_isstring(f) && !upb_fielddef_isseq(f)) {
+        /* Clear string pointer, only size matters (ptr could be non-NULL). */
+        test.str_val.data = NULL;
+      }
+      /* Continue if NULL or 0. */
+      if (memcmp(&test, &zero, sizeof(test)) == 0) continue;
+
+      /* Continue on empty array or map. */
+      if (upb_fielddef_ismap(f)) {
+        if (upb_map_size(test.map_val) == 0) continue;
+      } else if (upb_fielddef_isseq(f)) {
+        if (upb_array_size(test.array_val) == 0) continue;
+      }
+    }
+
+    *out_val = val;
+    *out_f = f;
+    *iter = i;
+    return true;
+  }
+  *iter = i;
+  return false;
+}
+
+bool _upb_msg_discardunknown(upb_msg *msg, const upb_msgdef *m, int depth) {
+  size_t iter = UPB_MSG_BEGIN;
+  const upb_fielddef *f;
+  upb_msgval val;
+  bool ret = true;
+
+  if (--depth == 0) return false;
+
+  _upb_msg_discardunknown_shallow(msg);
+
+  while (upb_msg_next(msg, m, NULL /*ext_pool*/, &f, &val, &iter)) {
+    const upb_msgdef *subm = upb_fielddef_msgsubdef(f);
+    if (!subm) continue;
+    if (upb_fielddef_ismap(f)) {
+      const upb_fielddef *val_f = upb_msgdef_itof(subm, 2);
+      const upb_msgdef *val_m = upb_fielddef_msgsubdef(val_f);
+      upb_map *map = (upb_map*)val.map_val;
+      size_t iter = UPB_MAP_BEGIN;
+
+      if (!val_m) continue;
+
+      while (upb_mapiter_next(map, &iter)) {
+        upb_msgval map_val = upb_mapiter_value(map, iter);
+        if (!_upb_msg_discardunknown((upb_msg*)map_val.msg_val, val_m, depth)) {
+          ret = false;
+        }
+      }
+    } else if (upb_fielddef_isseq(f)) {
+      const upb_array *arr = val.array_val;
+      size_t i, n = upb_array_size(arr);
+      for (i = 0; i < n; i++) {
+        upb_msgval elem = upb_array_get(arr, i);
+        if (!_upb_msg_discardunknown((upb_msg*)elem.msg_val, subm, depth)) {
+          ret = false;
+        }
+      }
+    } else {
+      if (!_upb_msg_discardunknown((upb_msg*)val.msg_val, subm, depth)) {
+        ret = false;
+      }
+    }
+  }
+
+  return ret;
+}
+
+bool upb_msg_discardunknown(upb_msg *msg, const upb_msgdef *m, int maxdepth) {
+  return _upb_msg_discardunknown(msg, m, maxdepth);
+}
+
+/** upb_array *****************************************************************/
+
+upb_array *upb_array_new(upb_arena *a, upb_fieldtype_t type) {
+  return _upb_array_new(a, type);
+}
+
+size_t upb_array_size(const upb_array *arr) {
+  return arr->len;
+}
+
+upb_msgval upb_array_get(const upb_array *arr, size_t i) {
+  upb_msgval ret;
+  const char* data = _upb_array_constptr(arr);
+  int lg2 = arr->data & 7;
+  UPB_ASSERT(i < arr->len);
+  memcpy(&ret, data + (i << lg2), 1 << lg2);
+  return ret;
+}
+
+void upb_array_set(upb_array *arr, size_t i, upb_msgval val) {
+  char* data = _upb_array_ptr(arr);
+  int lg2 = arr->data & 7;
+  UPB_ASSERT(i < arr->len);
+  memcpy(data + (i << lg2), &val, 1 << lg2);
+}
+
+bool upb_array_append(upb_array *arr, upb_msgval val, upb_arena *arena) {
+  if (!_upb_array_realloc(arr, arr->len + 1, arena)) {
+    return false;
+  }
+  arr->len++;
+  upb_array_set(arr, arr->len - 1, val);
+  return true;
+}
+
+bool upb_array_resize(upb_array *arr, size_t size, upb_arena *arena) {
+  return _upb_array_resize(arr, size, arena);
+}
+
+/** upb_map *******************************************************************/
+
+upb_map *upb_map_new(upb_arena *a, upb_fieldtype_t key_type,
+                     upb_fieldtype_t value_type) {
+  return _upb_map_new(a, _upb_fieldtype_to_mapsize[key_type],
+                      _upb_fieldtype_to_mapsize[value_type]);
+}
+
+size_t upb_map_size(const upb_map *map) {
+  return _upb_map_size(map);
+}
+
+bool upb_map_get(const upb_map *map, upb_msgval key, upb_msgval *val) {
+  return _upb_map_get(map, &key, map->key_size, val, map->val_size);
+}
+
+bool upb_map_set(upb_map *map, upb_msgval key, upb_msgval val,
+                 upb_arena *arena) {
+  return _upb_map_set(map, &key, map->key_size, &val, map->val_size, arena);
+}
+
+bool upb_map_delete(upb_map *map, upb_msgval key) {
+  return _upb_map_delete(map, &key, map->key_size);
+}
+
+bool upb_mapiter_next(const upb_map *map, size_t *iter) {
+  return _upb_map_next(map, iter);
+}
+
+bool upb_mapiter_done(const upb_map *map, size_t iter) {
+  upb_strtable_iter i;
+  UPB_ASSERT(iter != UPB_MAP_BEGIN);
+  i.t = &map->table;
+  i.index = iter;
+  return upb_strtable_done(&i);
+}
+
+/* Returns the key and value for this entry of the map. */
+upb_msgval upb_mapiter_key(const upb_map *map, size_t iter) {
+  upb_strtable_iter i;
+  upb_msgval ret;
+  i.t = &map->table;
+  i.index = iter;
+  _upb_map_fromkey(upb_strtable_iter_key(&i), &ret, map->key_size);
+  return ret;
+}
+
+upb_msgval upb_mapiter_value(const upb_map *map, size_t iter) {
+  upb_strtable_iter i;
+  upb_msgval ret;
+  i.t = &map->table;
+  i.index = iter;
+  _upb_map_fromvalue(upb_strtable_iter_value(&i), &ret, map->val_size);
+  return ret;
+}
+
+/* void upb_mapiter_setvalue(upb_map *map, size_t iter, upb_msgval value); */
+
+
+#ifdef UPB_MSVC_VSNPRINTF
+/* Visual C++ earlier than 2015 doesn't have standard C99 snprintf and
+ * vsnprintf. To support them, missing functions are manually implemented
+ * using the existing secure functions. */
+int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg) {
+  if (!s) {
+    return _vscprintf(format, arg);
+  }
+  int ret = _vsnprintf_s(s, n, _TRUNCATE, format, arg);
+  if (ret < 0) {
+	ret = _vscprintf(format, arg);
+  }
+  return ret;
+}
+
+int msvc_snprintf(char* s, size_t n, const char* format, ...) {
+  va_list arg;
+  va_start(arg, format);
+  int ret = msvc_vsnprintf(s, n, format, arg);
+  va_end(arg);
+  return ret;
+}
+#endif
+
+
+#include <errno.h>
+#include <float.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Special header, must be included last. */
+
+typedef struct {
+  const char *ptr, *end;
+  upb_arena *arena;  /* TODO: should we have a tmp arena for tmp data? */
+  const upb_symtab *any_pool;
+  int depth;
+  upb_status *status;
+  jmp_buf err;
+  int line;
+  const char *line_begin;
+  bool is_first;
+  int options;
+  const upb_fielddef *debug_field;
+} jsondec;
+
+enum { JD_OBJECT, JD_ARRAY, JD_STRING, JD_NUMBER, JD_TRUE, JD_FALSE, JD_NULL };
+
+/* Forward declarations of mutually-recursive functions. */
+static void jsondec_wellknown(jsondec *d, upb_msg *msg, const upb_msgdef *m);
+static upb_msgval jsondec_value(jsondec *d, const upb_fielddef *f);
+static void jsondec_wellknownvalue(jsondec *d, upb_msg *msg,
+                                   const upb_msgdef *m);
+static void jsondec_object(jsondec *d, upb_msg *msg, const upb_msgdef *m);
+
+static bool jsondec_streql(upb_strview str, const char *lit) {
+  return str.size == strlen(lit) && memcmp(str.data, lit, str.size) == 0;
+}
+
+UPB_NORETURN static void jsondec_err(jsondec *d, const char *msg) {
+  upb_status_seterrf(d->status, "Error parsing JSON @%d:%d: %s", d->line,
+                     (int)(d->ptr - d->line_begin), msg);
+  longjmp(d->err, 1);
+}
+
+UPB_NORETURN static void jsondec_errf(jsondec *d, const char *fmt, ...) {
+  va_list argp;
+  upb_status_seterrf(d->status, "Error parsing JSON @%d:%d: ", d->line,
+                     (int)(d->ptr - d->line_begin));
+  va_start(argp, fmt);
+  upb_status_vappenderrf(d->status, fmt, argp);
+  va_end(argp);
+  longjmp(d->err, 1);
+}
+
+static void jsondec_skipws(jsondec *d) {
+  while (d->ptr != d->end) {
+    switch (*d->ptr) {
+      case '\n':
+        d->line++;
+        d->line_begin = d->ptr;
+        /* Fallthrough. */
+      case '\r':
+      case '\t':
+      case ' ':
+        d->ptr++;
+        break;
+      default:
+        return;
+    }
+  }
+  jsondec_err(d, "Unexpected EOF");
+}
+
+static bool jsondec_tryparsech(jsondec *d, char ch) {
+  if (d->ptr == d->end || *d->ptr != ch) return false;
+  d->ptr++;
+  return true;
+}
+
+static void jsondec_parselit(jsondec *d, const char *lit) {
+  size_t avail = d->end - d->ptr;
+  size_t len = strlen(lit);
+  if (avail < len || memcmp(d->ptr, lit, len) != 0) {
+    jsondec_errf(d, "Expected: '%s'", lit);
+  }
+  d->ptr += len;
+}
+
+static void jsondec_wsch(jsondec *d, char ch) {
+  jsondec_skipws(d);
+  if (!jsondec_tryparsech(d, ch)) {
+    jsondec_errf(d, "Expected: '%c'", ch);
+  }
+}
+
+static void jsondec_true(jsondec *d) { jsondec_parselit(d, "true"); }
+static void jsondec_false(jsondec *d) { jsondec_parselit(d, "false"); }
+static void jsondec_null(jsondec *d) { jsondec_parselit(d, "null"); }
+
+static void jsondec_entrysep(jsondec *d) {
+  jsondec_skipws(d);
+  jsondec_parselit(d, ":");
+}
+
+static int jsondec_rawpeek(jsondec *d) {
+  switch (*d->ptr) {
+    case '{':
+      return JD_OBJECT;
+    case '[':
+      return JD_ARRAY;
+    case '"':
+      return JD_STRING;
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      return JD_NUMBER;
+    case 't':
+      return JD_TRUE;
+    case 'f':
+      return JD_FALSE;
+    case 'n':
+      return JD_NULL;
+    default:
+      jsondec_errf(d, "Unexpected character: '%c'", *d->ptr);
+  }
+}
+
+/* JSON object/array **********************************************************/
+
+/* These are used like so:
+ *
+ * jsondec_objstart(d);
+ * while (jsondec_objnext(d)) {
+ *   ...
+ * }
+ * jsondec_objend(d) */
+
+static int jsondec_peek(jsondec *d) {
+  jsondec_skipws(d);
+  return jsondec_rawpeek(d);
+}
+
+static void jsondec_push(jsondec *d) {
+  if (--d->depth < 0) {
+    jsondec_err(d, "Recursion limit exceeded");
+  }
+  d->is_first = true;
+}
+
+static bool jsondec_seqnext(jsondec *d, char end_ch) {
+  bool is_first = d->is_first;
+  d->is_first = false;
+  jsondec_skipws(d);
+  if (*d->ptr == end_ch) return false;
+  if (!is_first) jsondec_parselit(d, ",");
+  return true;
+}
+
+static void jsondec_arrstart(jsondec *d) {
+  jsondec_push(d);
+  jsondec_wsch(d, '[');
+}
+
+static void jsondec_arrend(jsondec *d) {
+  d->depth++;
+  jsondec_wsch(d, ']');
+}
+
+static bool jsondec_arrnext(jsondec *d) {
+  return jsondec_seqnext(d, ']');
+}
+
+static void jsondec_objstart(jsondec *d) {
+  jsondec_push(d);
+  jsondec_wsch(d, '{');
+}
+
+static void jsondec_objend(jsondec *d) {
+  d->depth++;
+  jsondec_wsch(d, '}');
+}
+
+static bool jsondec_objnext(jsondec *d) {
+  if (!jsondec_seqnext(d, '}')) return false;
+  if (jsondec_peek(d) != JD_STRING) {
+    jsondec_err(d, "Object must start with string");
+  }
+  return true;
+}
+
+/* JSON number ****************************************************************/
+
+static bool jsondec_tryskipdigits(jsondec *d) {
+  const char *start = d->ptr;
+
+  while (d->ptr < d->end) {
+    if (*d->ptr < '0' || *d->ptr > '9') {
+      break;
+    }
+    d->ptr++;
+  }
+
+  return d->ptr != start;
+}
+
+static void jsondec_skipdigits(jsondec *d) {
+  if (!jsondec_tryskipdigits(d)) {
+    jsondec_err(d, "Expected one or more digits");
+  }
+}
+
+static double jsondec_number(jsondec *d) {
+  const char *start = d->ptr;
+
+  assert(jsondec_rawpeek(d) == JD_NUMBER);
+
+  /* Skip over the syntax of a number, as specified by JSON. */
+  if (*d->ptr == '-') d->ptr++;
+
+  if (jsondec_tryparsech(d, '0')) {
+    if (jsondec_tryskipdigits(d)) {
+      jsondec_err(d, "number cannot have leading zero");
+    }
+  } else {
+    jsondec_skipdigits(d);
+  }
+
+  if (d->ptr == d->end) goto parse;
+  if (jsondec_tryparsech(d, '.')) {
+    jsondec_skipdigits(d);
+  }
+  if (d->ptr == d->end) goto parse;
+
+  if (*d->ptr == 'e' || *d->ptr == 'E') {
+    d->ptr++;
+    if (d->ptr == d->end) {
+      jsondec_err(d, "Unexpected EOF in number");
+    }
+    if (*d->ptr == '+' || *d->ptr == '-') {
+      d->ptr++;
+    }
+    jsondec_skipdigits(d);
+  }
+
+parse:
+  /* Having verified the syntax of a JSON number, use strtod() to parse
+   * (strtod() accepts a superset of JSON syntax). */
+  errno = 0;
+  {
+    char* end;
+    double val = strtod(start, &end);
+    assert(end == d->ptr);
+
+    /* Currently the min/max-val conformance tests fail if we check this.  Does
+     * this mean the conformance tests are wrong or strtod() is wrong, or
+     * something else?  Investigate further. */
+    /*
+    if (errno == ERANGE) {
+      jsondec_err(d, "Number out of range");
+    }
+    */
+
+    if (val > DBL_MAX || val < -DBL_MAX) {
+      jsondec_err(d, "Number out of range");
+    }
+
+    return val;
+  }
+}
+
+/* JSON string ****************************************************************/
+
+static char jsondec_escape(jsondec *d) {
+  switch (*d->ptr++) {
+    case '"':
+      return '\"';
+    case '\\':
+      return '\\';
+    case '/':
+      return '/';
+    case 'b':
+      return '\b';
+    case 'f':
+      return '\f';
+    case 'n':
+      return '\n';
+    case 'r':
+      return '\r';
+    case 't':
+      return '\t';
+    default:
+      jsondec_err(d, "Invalid escape char");
+  }
+}
+
+static uint32_t jsondec_codepoint(jsondec *d) {
+  uint32_t cp = 0;
+  const char *end;
+
+  if (d->end - d->ptr < 4) {
+    jsondec_err(d, "EOF inside string");
+  }
+
+  end = d->ptr + 4;
+  while (d->ptr < end) {
+    char ch = *d->ptr++;
+    if (ch >= '0' && ch <= '9') {
+      ch -= '0';
+    } else if (ch >= 'a' && ch <= 'f') {
+      ch = ch - 'a' + 10;
+    } else if (ch >= 'A' && ch <= 'F') {
+      ch = ch - 'A' + 10;
+    } else {
+      jsondec_err(d, "Invalid hex digit");
+    }
+    cp = (cp << 4) | ch;
+  }
+
+  return cp;
+}
+
+/* Parses a \uXXXX unicode escape (possibly a surrogate pair). */
+static size_t jsondec_unicode(jsondec *d, char* out) {
+  uint32_t cp = jsondec_codepoint(d);
+  if (cp >= 0xd800 && cp <= 0xdbff) {
+    /* Surrogate pair: two 16-bit codepoints become a 32-bit codepoint. */
+    uint32_t high = cp;
+    uint32_t low;
+    jsondec_parselit(d, "\\u");
+    low = jsondec_codepoint(d);
+    if (low < 0xdc00 || low > 0xdfff) {
+      jsondec_err(d, "Invalid low surrogate");
+    }
+    cp = (high & 0x3ff) << 10;
+    cp |= (low & 0x3ff);
+    cp += 0x10000;
+  } else if (cp >= 0xdc00 && cp <= 0xdfff) {
+    jsondec_err(d, "Unpaired low surrogate");
+  }
+
+  /* Write to UTF-8 */
+  if (cp <= 0x7f) {
+    out[0] = cp;
+    return 1;
+  } else if (cp <= 0x07FF) {
+    out[0] = ((cp >> 6) & 0x1F) | 0xC0;
+    out[1] = ((cp >> 0) & 0x3F) | 0x80;
+    return 2;
+  } else if (cp <= 0xFFFF) {
+    out[0] = ((cp >> 12) & 0x0F) | 0xE0;
+    out[1] = ((cp >> 6) & 0x3F) | 0x80;
+    out[2] = ((cp >> 0) & 0x3F) | 0x80;
+    return 3;
+  } else if (cp < 0x10FFFF) {
+    out[0] = ((cp >> 18) & 0x07) | 0xF0;
+    out[1] = ((cp >> 12) & 0x3f) | 0x80;
+    out[2] = ((cp >> 6) & 0x3f) | 0x80;
+    out[3] = ((cp >> 0) & 0x3f) | 0x80;
+    return 4;
+  } else {
+    jsondec_err(d, "Invalid codepoint");
+  }
+}
+
+static void jsondec_resize(jsondec *d, char **buf, char **end, char **buf_end) {
+  size_t oldsize = *buf_end - *buf;
+  size_t len = *end - *buf;
+  size_t size = UPB_MAX(8, 2 * oldsize);
+
+  *buf = upb_arena_realloc(d->arena, *buf, len, size);
+  *end = *buf + len;
+  *buf_end = *buf + size;
+}
+
+static upb_strview jsondec_string(jsondec *d) {
+  char *buf = NULL;
+  char *end = NULL;
+  char *buf_end = NULL;
+
+  jsondec_skipws(d);
+
+  if (*d->ptr++ != '"') {
+    jsondec_err(d, "Expected string");
+  }
+
+  while (d->ptr < d->end) {
+    char ch = *d->ptr++;
+
+    if (end == buf_end) {
+      jsondec_resize(d, &buf, &end, &buf_end);
+    }
+
+    switch (ch) {
+      case '"': {
+        upb_strview ret;
+        ret.data = buf;
+        ret.size = end - buf;
+        return ret;
+      }
+      case '\\':
+        if (d->ptr == d->end) goto eof;
+        if (*d->ptr == 'u') {
+          d->ptr++;
+          if (buf_end - end < 4) {
+            /* Allow space for maximum-sized code point (4 bytes). */
+            jsondec_resize(d, &buf, &end, &buf_end);
+          }
+          end += jsondec_unicode(d, end);
+        } else {
+          *end++ = jsondec_escape(d);
+        }
+        break;
+      default:
+        if ((unsigned char)*d->ptr < 0x20) {
+          jsondec_err(d, "Invalid char in JSON string");
+        }
+        *end++ = ch;
+        break;
+    }
+  }
+
+eof:
+  jsondec_err(d, "EOF inside string");
+}
+
+static void jsondec_skipval(jsondec *d) {
+  switch (jsondec_peek(d)) {
+    case JD_OBJECT:
+      jsondec_objstart(d);
+      while (jsondec_objnext(d)) {
+        jsondec_string(d);
+        jsondec_entrysep(d);
+        jsondec_skipval(d);
+      }
+      jsondec_objend(d);
+      break;
+    case JD_ARRAY:
+      jsondec_arrstart(d);
+      while (jsondec_arrnext(d)) {
+        jsondec_skipval(d);
+      }
+      jsondec_arrend(d);
+      break;
+    case JD_TRUE:
+      jsondec_true(d);
+      break;
+    case JD_FALSE:
+      jsondec_false(d);
+      break;
+    case JD_NULL:
+      jsondec_null(d);
+      break;
+    case JD_STRING:
+      jsondec_string(d);
+      break;
+    case JD_NUMBER:
+      jsondec_number(d);
+      break;
+  }
+}
+
+/* Base64 decoding for bytes fields. ******************************************/
+
+static unsigned int jsondec_base64_tablelookup(const char ch) {
+  /* Table includes the normal base64 chars plus the URL-safe variant. */
+  const signed char table[256] = {
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       62 /*+*/, -1,       62 /*-*/, -1,       63 /*/ */, 52 /*0*/,
+      53 /*1*/, 54 /*2*/, 55 /*3*/, 56 /*4*/, 57 /*5*/, 58 /*6*/,  59 /*7*/,
+      60 /*8*/, 61 /*9*/, -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       0 /*A*/,  1 /*B*/,  2 /*C*/,  3 /*D*/,   4 /*E*/,
+      5 /*F*/,  6 /*G*/,  07 /*H*/, 8 /*I*/,  9 /*J*/,  10 /*K*/,  11 /*L*/,
+      12 /*M*/, 13 /*N*/, 14 /*O*/, 15 /*P*/, 16 /*Q*/, 17 /*R*/,  18 /*S*/,
+      19 /*T*/, 20 /*U*/, 21 /*V*/, 22 /*W*/, 23 /*X*/, 24 /*Y*/,  25 /*Z*/,
+      -1,       -1,       -1,       -1,       63 /*_*/, -1,        26 /*a*/,
+      27 /*b*/, 28 /*c*/, 29 /*d*/, 30 /*e*/, 31 /*f*/, 32 /*g*/,  33 /*h*/,
+      34 /*i*/, 35 /*j*/, 36 /*k*/, 37 /*l*/, 38 /*m*/, 39 /*n*/,  40 /*o*/,
+      41 /*p*/, 42 /*q*/, 43 /*r*/, 44 /*s*/, 45 /*t*/, 46 /*u*/,  47 /*v*/,
+      48 /*w*/, 49 /*x*/, 50 /*y*/, 51 /*z*/, -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1,       -1,       -1,        -1,
+      -1,       -1,       -1,       -1};
+
+  /* Sign-extend return value so high bit will be set on any unexpected char. */
+  return table[(unsigned)ch];
+}
+
+static char *jsondec_partialbase64(jsondec *d, const char *ptr, const char *end,
+                                   char *out) {
+  int32_t val = -1;
+
+  switch (end - ptr) {
+    case 2:
+      val = jsondec_base64_tablelookup(ptr[0]) << 18 |
+            jsondec_base64_tablelookup(ptr[1]) << 12;
+      out[0] = val >> 16;
+      out += 1;
+      break;
+    case 3:
+      val = jsondec_base64_tablelookup(ptr[0]) << 18 |
+            jsondec_base64_tablelookup(ptr[1]) << 12 |
+            jsondec_base64_tablelookup(ptr[2]) << 6;
+      out[0] = val >> 16;
+      out[1] = (val >> 8) & 0xff;
+      out += 2;
+      break;
+  }
+
+  if (val < 0) {
+    jsondec_err(d, "Corrupt base64");
+  }
+
+  return out;
+}
+
+static size_t jsondec_base64(jsondec *d, upb_strview str) {
+  /* We decode in place. This is safe because this is a new buffer (not
+   * aliasing the input) and because base64 decoding shrinks 4 bytes into 3. */
+  char *out = (char*)str.data;
+  const char *ptr = str.data;
+  const char *end = ptr + str.size;
+  const char *end4 = ptr + (str.size & -4);  /* Round down to multiple of 4. */
+
+  for (; ptr < end4; ptr += 4, out += 3) {
+    int val = jsondec_base64_tablelookup(ptr[0]) << 18 |
+              jsondec_base64_tablelookup(ptr[1]) << 12 |
+              jsondec_base64_tablelookup(ptr[2]) << 6 |
+              jsondec_base64_tablelookup(ptr[3]) << 0;
+
+    if (val < 0) {
+      /* Junk chars or padding. Remove trailing padding, if any. */
+      if (end - ptr == 4 && ptr[3] == '=') {
+        if (ptr[2] == '=') {
+          end -= 2;
+        } else {
+          end -= 1;
+        }
+      }
+      break;
+    }
+
+    out[0] = val >> 16;
+    out[1] = (val >> 8) & 0xff;
+    out[2] = val & 0xff;
+  }
+
+  if (ptr < end) {
+    /* Process remaining chars. We do not require padding. */
+    out = jsondec_partialbase64(d, ptr, end, out);
+  }
+
+  return out - str.data;
+}
+
+/* Low-level integer parsing **************************************************/
+
+/* We use these hand-written routines instead of strto[u]l() because the "long
+ * long" variants aren't in c89. Also our version allows setting a ptr limit. */
+
+static const char *jsondec_buftouint64(jsondec *d, const char *ptr,
+                                       const char *end, uint64_t *val) {
+  uint64_t u64 = 0;
+  while (ptr < end) {
+    unsigned ch = *ptr - '0';
+    if (ch >= 10) break;
+    if (u64 > UINT64_MAX / 10 || u64 * 10 > UINT64_MAX - ch) {
+      jsondec_err(d, "Integer overflow");
+    }
+    u64 *= 10;
+    u64 += ch;
+    ptr++;
+  }
+
+  *val = u64;
+  return ptr;
+}
+
+static const char *jsondec_buftoint64(jsondec *d, const char *ptr,
+                                      const char *end, int64_t *val) {
+  bool neg = false;
+  uint64_t u64;
+
+  if (ptr != end && *ptr == '-') {
+    ptr++;
+    neg = true;
+  }
+
+  ptr = jsondec_buftouint64(d, ptr, end, &u64);
+  if (u64 > (uint64_t)INT64_MAX + neg) {
+    jsondec_err(d, "Integer overflow");
+  }
+
+  *val = neg ? -u64 : u64;
+  return ptr;
+}
+
+static uint64_t jsondec_strtouint64(jsondec *d, upb_strview str) {
+  const char *end = str.data + str.size;
+  uint64_t ret;
+  if (jsondec_buftouint64(d, str.data, end, &ret) != end) {
+    jsondec_err(d, "Non-number characters in quoted integer");
+  }
+  return ret;
+}
+
+static int64_t jsondec_strtoint64(jsondec *d, upb_strview str) {
+  const char *end = str.data + str.size;
+  int64_t ret;
+  if (jsondec_buftoint64(d, str.data, end, &ret) != end) {
+    jsondec_err(d, "Non-number characters in quoted integer");
+  }
+  return ret;
+}
+
+/* Primitive value types ******************************************************/
+
+/* Parse INT32 or INT64 value. */
+static upb_msgval jsondec_int(jsondec *d, const upb_fielddef *f) {
+  upb_msgval val;
+
+  switch (jsondec_peek(d)) {
+    case JD_NUMBER: {
+      double dbl = jsondec_number(d);
+      if (dbl > 9223372036854774784.0 || dbl < -9223372036854775808.0) {
+        jsondec_err(d, "JSON number is out of range.");
+      }
+      val.int64_val = dbl;  /* must be guarded, overflow here is UB */
+      if (val.int64_val != dbl) {
+        jsondec_errf(d, "JSON number was not integral (%d != %" PRId64 ")", dbl,
+                     val.int64_val);
+      }
+      break;
+    }
+    case JD_STRING: {
+      upb_strview str = jsondec_string(d);
+      val.int64_val = jsondec_strtoint64(d, str);
+      break;
+    }
+    default:
+      jsondec_err(d, "Expected number or string");
+  }
+
+  if (upb_fielddef_type(f) == UPB_TYPE_INT32) {
+    if (val.int64_val > INT32_MAX || val.int64_val < INT32_MIN) {
+      jsondec_err(d, "Integer out of range.");
+    }
+    val.int32_val = (int32_t)val.int64_val;
+  }
+
+  return val;
+}
+
+/* Parse UINT32 or UINT64 value. */
+static upb_msgval jsondec_uint(jsondec *d, const upb_fielddef *f) {
+  upb_msgval val;
+
+  switch (jsondec_peek(d)) {
+    case JD_NUMBER: {
+      double dbl = jsondec_number(d);
+      if (dbl > 18446744073709549568.0 || dbl < 0) {
+        jsondec_err(d, "JSON number is out of range.");
+      }
+      val.uint64_val = dbl;  /* must be guarded, overflow here is UB */
+      if (val.uint64_val != dbl) {
+        jsondec_errf(d, "JSON number was not integral (%d != %" PRIu64 ")", dbl,
+                     val.uint64_val);
+      }
+      break;
+    }
+    case JD_STRING: {
+      upb_strview str = jsondec_string(d);
+      val.uint64_val = jsondec_strtouint64(d, str);
+      break;
+    }
+    default:
+      jsondec_err(d, "Expected number or string");
+  }
+
+  if (upb_fielddef_type(f) == UPB_TYPE_UINT32) {
+    if (val.uint64_val > UINT32_MAX) {
+      jsondec_err(d, "Integer out of range.");
+    }
+    val.uint32_val = (uint32_t)val.uint64_val;
+  }
+
+  return val;
+}
+
+/* Parse DOUBLE or FLOAT value. */
+static upb_msgval jsondec_double(jsondec *d, const upb_fielddef *f) {
+  upb_strview str;
+  upb_msgval val;
+
+  switch (jsondec_peek(d)) {
+    case JD_NUMBER:
+      val.double_val = jsondec_number(d);
+      break;
+    case JD_STRING:
+      str = jsondec_string(d);
+      if (jsondec_streql(str, "NaN")) {
+        val.double_val = 0.0 / 0.0;
+      } else if (jsondec_streql(str, "Infinity")) {
+        val.double_val = UPB_INFINITY;
+      } else if (jsondec_streql(str, "-Infinity")) {
+        val.double_val = -UPB_INFINITY;
+      } else {
+        val.double_val = strtod(str.data, NULL);
+      }
+      break;
+    default:
+      jsondec_err(d, "Expected number or string");
+  }
+
+  if (upb_fielddef_type(f) == UPB_TYPE_FLOAT) {
+    if (val.double_val != UPB_INFINITY && val.double_val != -UPB_INFINITY &&
+        (val.double_val > FLT_MAX || val.double_val < -FLT_MAX)) {
+      jsondec_err(d, "Float out of range");
+    }
+    val.float_val = val.double_val;
+  }
+
+  return val;
+}
+
+/* Parse STRING or BYTES value. */
+static upb_msgval jsondec_strfield(jsondec *d, const upb_fielddef *f) {
+  upb_msgval val;
+  val.str_val = jsondec_string(d);
+  if (upb_fielddef_type(f) == UPB_TYPE_BYTES) {
+    val.str_val.size = jsondec_base64(d, val.str_val);
+  }
+  return val;
+}
+
+static upb_msgval jsondec_enum(jsondec *d, const upb_fielddef *f) {
+  if (jsondec_peek(d) == JD_STRING) {
+    const upb_enumdef *e = upb_fielddef_enumsubdef(f);
+    upb_strview str = jsondec_string(d);
+    upb_msgval val;
+    if (!upb_enumdef_ntoi(e, str.data, str.size, &val.int32_val)) {
+      if (d->options & UPB_JSONDEC_IGNOREUNKNOWN) {
+        val.int32_val = 0;
+      } else {
+        jsondec_errf(d, "Unknown enumerator: '" UPB_STRVIEW_FORMAT "'",
+                     UPB_STRVIEW_ARGS(str));
+      }
+    }
+    return val;
+  } else {
+    return jsondec_int(d, f);
+  }
+}
+
+static upb_msgval jsondec_bool(jsondec *d, const upb_fielddef *f) {
+  bool is_map_key = upb_fielddef_number(f) == 1 &&
+                    upb_msgdef_mapentry(upb_fielddef_containingtype(f));
+  upb_msgval val;
+
+  if (is_map_key) {
+    upb_strview str = jsondec_string(d);
+    if (jsondec_streql(str, "true")) {
+      val.bool_val = true;
+    } else if (jsondec_streql(str, "false")) {
+      val.bool_val = false;
+    } else {
+      jsondec_err(d, "Invalid boolean map key");
+    }
+  } else {
+    switch (jsondec_peek(d)) {
+      case JD_TRUE:
+        val.bool_val = true;
+        jsondec_true(d);
+        break;
+      case JD_FALSE:
+        val.bool_val = false;
+        jsondec_false(d);
+        break;
+      default:
+        jsondec_err(d, "Expected true or false");
+    }
+  }
+
+  return val;
+}
+
+/* Composite types (array/message/map) ****************************************/
+
+static void jsondec_array(jsondec *d, upb_msg *msg, const upb_fielddef *f) {
+  upb_array *arr = upb_msg_mutable(msg, f, d->arena).array;
+
+  jsondec_arrstart(d);
+  while (jsondec_arrnext(d)) {
+    upb_msgval elem = jsondec_value(d, f);
+    upb_array_append(arr, elem, d->arena);
+  }
+  jsondec_arrend(d);
+}
+
+static void jsondec_map(jsondec *d, upb_msg *msg, const upb_fielddef *f) {
+  upb_map *map = upb_msg_mutable(msg, f, d->arena).map;
+  const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+  const upb_fielddef *key_f = upb_msgdef_itof(entry, 1);
+  const upb_fielddef *val_f = upb_msgdef_itof(entry, 2);
+
+  jsondec_objstart(d);
+  while (jsondec_objnext(d)) {
+    upb_msgval key, val;
+    key = jsondec_value(d, key_f);
+    jsondec_entrysep(d);
+    val = jsondec_value(d, val_f);
+    upb_map_set(map, key, val, d->arena);
+  }
+  jsondec_objend(d);
+}
+
+static void jsondec_tomsg(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  if (upb_msgdef_wellknowntype(m) == UPB_WELLKNOWN_UNSPECIFIED) {
+    jsondec_object(d, msg, m);
+  } else {
+    jsondec_wellknown(d, msg, m);
+  }
+}
+
+static upb_msgval jsondec_msg(jsondec *d, const upb_fielddef *f) {
+  const upb_msgdef *m = upb_fielddef_msgsubdef(f);
+  upb_msg *msg = upb_msg_new(m, d->arena);
+  upb_msgval val;
+
+  jsondec_tomsg(d, msg, m);
+  val.msg_val = msg;
+  return val;
+}
+
+static bool jsondec_isvalue(const upb_fielddef *f) {
+  return upb_fielddef_type(f) == UPB_TYPE_MESSAGE &&
+         upb_msgdef_wellknowntype(upb_fielddef_msgsubdef(f)) ==
+             UPB_WELLKNOWN_VALUE;
+}
+
+static void jsondec_field(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  upb_strview name;
+  const upb_fielddef *f;
+  const upb_fielddef *preserved;
+
+  name = jsondec_string(d);
+  jsondec_entrysep(d);
+  f = upb_msgdef_lookupjsonname(m, name.data, name.size);
+
+  if (!f) {
+    if ((d->options & UPB_JSONDEC_IGNOREUNKNOWN) == 0) {
+      jsondec_errf(d, "Unknown field: '" UPB_STRVIEW_FORMAT "'",
+                   UPB_STRVIEW_ARGS(name));
+    }
+    jsondec_skipval(d);
+    return;
+  }
+
+  if (upb_fielddef_containingoneof(f) &&
+      upb_msg_whichoneof(msg, upb_fielddef_containingoneof(f))) {
+    jsondec_err(d, "More than one field for this oneof.");
+  }
+
+  if (jsondec_peek(d) == JD_NULL && !jsondec_isvalue(f)) {
+    /* JSON "null" indicates a default value, so no need to set anything. */
+    jsondec_null(d);
+    return;
+  }
+
+  preserved = d->debug_field;
+  d->debug_field = f;
+
+  if (upb_fielddef_ismap(f)) {
+    jsondec_map(d, msg, f);
+  } else if (upb_fielddef_isseq(f)) {
+    jsondec_array(d, msg, f);
+  } else if (upb_fielddef_issubmsg(f)) {
+    upb_msg *submsg = upb_msg_mutable(msg, f, d->arena).msg;
+    const upb_msgdef *subm = upb_fielddef_msgsubdef(f);
+    jsondec_tomsg(d, submsg, subm);
+  } else {
+    upb_msgval val = jsondec_value(d, f);
+    upb_msg_set(msg, f, val, d->arena);
+  }
+
+  d->debug_field = preserved;
+}
+
+static void jsondec_object(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  jsondec_objstart(d);
+  while (jsondec_objnext(d)) {
+    jsondec_field(d, msg, m);
+  }
+  jsondec_objend(d);
+}
+
+static upb_msgval jsondec_value(jsondec *d, const upb_fielddef *f) {
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_BOOL:
+      return jsondec_bool(d, f);
+    case UPB_TYPE_FLOAT:
+    case UPB_TYPE_DOUBLE:
+      return jsondec_double(d, f);
+    case UPB_TYPE_UINT32:
+    case UPB_TYPE_UINT64:
+      return jsondec_uint(d, f);
+    case UPB_TYPE_INT32:
+    case UPB_TYPE_INT64:
+      return jsondec_int(d, f);
+    case UPB_TYPE_STRING:
+    case UPB_TYPE_BYTES:
+      return jsondec_strfield(d, f);
+    case UPB_TYPE_ENUM:
+      return jsondec_enum(d, f);
+    case UPB_TYPE_MESSAGE:
+      return jsondec_msg(d, f);
+    default:
+      UPB_UNREACHABLE();
+  }
+}
+
+/* Well-known types ***********************************************************/
+
+static int jsondec_tsdigits(jsondec *d, const char **ptr, size_t digits,
+                            const char *after) {
+  uint64_t val;
+  const char *p = *ptr;
+  const char *end = p + digits;
+  size_t after_len = after ? strlen(after) : 0;
+
+  UPB_ASSERT(digits <= 9);  /* int can't overflow. */
+
+  if (jsondec_buftouint64(d, p, end, &val) != end ||
+      (after_len && memcmp(end, after, after_len) != 0)) {
+    jsondec_err(d, "Malformed timestamp");
+  }
+
+  UPB_ASSERT(val < INT_MAX);
+
+  *ptr = end + after_len;
+  return (int)val;
+}
+
+static int jsondec_nanos(jsondec *d, const char **ptr, const char *end) {
+  uint64_t nanos = 0;
+  const char *p = *ptr;
+
+  if (p != end && *p == '.') {
+    const char *nano_end = jsondec_buftouint64(d, p + 1, end, &nanos);
+    int digits = (int)(nano_end - p - 1);
+    int exp_lg10 = 9 - digits;
+    if (digits > 9) {
+      jsondec_err(d, "Too many digits for partial seconds");
+    }
+    while (exp_lg10--) nanos *= 10;
+    *ptr = nano_end;
+  }
+
+  UPB_ASSERT(nanos < INT_MAX);
+
+  return (int)nanos;
+}
+
+/* jsondec_epochdays(1970, 1, 1) == 1970-01-01 == 0. */
+int jsondec_epochdays(int y, int m, int d) {
+  const uint32_t year_base = 4800;    /* Before min year, multiple of 400. */
+  const uint32_t m_adj = m - 3;       /* March-based month. */
+  const uint32_t carry = m_adj > (uint32_t)m ? 1 : 0;
+  const uint32_t adjust = carry ? 12 : 0;
+  const uint32_t y_adj = y + year_base - carry;
+  const uint32_t month_days = ((m_adj + adjust) * 62719 + 769) / 2048;
+  const uint32_t leap_days = y_adj / 4 - y_adj / 100 + y_adj / 400;
+  return y_adj * 365 + leap_days + month_days + (d - 1) - 2472632;
+}
+
+static int64_t jsondec_unixtime(int y, int m, int d, int h, int min, int s) {
+  return (int64_t)jsondec_epochdays(y, m, d) * 86400 + h * 3600 + min * 60 + s;
+}
+
+static void jsondec_timestamp(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  upb_msgval seconds;
+  upb_msgval nanos;
+  upb_strview str = jsondec_string(d);
+  const char *ptr = str.data;
+  const char *end = ptr + str.size;
+
+  if (str.size < 20) goto malformed;
+
+  {
+    /* 1972-01-01T01:00:00 */
+    int year = jsondec_tsdigits(d, &ptr, 4, "-");
+    int mon = jsondec_tsdigits(d, &ptr, 2, "-");
+    int day = jsondec_tsdigits(d, &ptr, 2, "T");
+    int hour = jsondec_tsdigits(d, &ptr, 2, ":");
+    int min = jsondec_tsdigits(d, &ptr, 2, ":");
+    int sec = jsondec_tsdigits(d, &ptr, 2, NULL);
+
+    seconds.int64_val = jsondec_unixtime(year, mon, day, hour, min, sec);
+  }
+
+  nanos.int32_val = jsondec_nanos(d, &ptr, end);
+
+  {
+    /* [+-]08:00 or Z */
+    int ofs = 0;
+    bool neg = false;
+
+    if (ptr == end) goto malformed;
+
+    switch (*ptr++) {
+      case '-':
+        neg = true;
+        /* fallthrough */
+      case '+':
+        if ((end - ptr) != 5) goto malformed;
+        ofs = jsondec_tsdigits(d, &ptr, 2, ":00");
+        ofs *= 60 * 60;
+        seconds.int64_val += (neg ? ofs : -ofs);
+        break;
+      case 'Z':
+        if (ptr != end) goto malformed;
+        break;
+      default:
+        goto malformed;
+    }
+  }
+
+  if (seconds.int64_val < -62135596800) {
+    jsondec_err(d, "Timestamp out of range");
+  }
+
+  upb_msg_set(msg, upb_msgdef_itof(m, 1), seconds, d->arena);
+  upb_msg_set(msg, upb_msgdef_itof(m, 2), nanos, d->arena);
+  return;
+
+malformed:
+  jsondec_err(d, "Malformed timestamp");
+}
+
+static void jsondec_duration(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  upb_msgval seconds;
+  upb_msgval nanos;
+  upb_strview str = jsondec_string(d);
+  const char *ptr = str.data;
+  const char *end = ptr + str.size;
+
+  /* "3.000000001s", "3s", etc. */
+  ptr = jsondec_buftoint64(d, ptr, end, &seconds.int64_val);
+  nanos.int32_val = jsondec_nanos(d, &ptr, end);
+
+  if (end - ptr != 1 || *ptr != 's') {
+    jsondec_err(d, "Malformed duration");
+  }
+
+  if (seconds.int64_val < -315576000000LL || seconds.int64_val > 315576000000LL) {
+    jsondec_err(d, "Duration out of range");
+  }
+
+  if (seconds.int64_val < 0) {
+    nanos.int32_val = - nanos.int32_val;
+  }
+
+  upb_msg_set(msg, upb_msgdef_itof(m, 1), seconds, d->arena);
+  upb_msg_set(msg, upb_msgdef_itof(m, 2), nanos, d->arena);
+}
+
+static void jsondec_listvalue(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  const upb_fielddef *values_f = upb_msgdef_itof(m, 1);
+  const upb_msgdef *value_m = upb_fielddef_msgsubdef(values_f);
+  upb_array *values = upb_msg_mutable(msg, values_f, d->arena).array;
+
+  jsondec_arrstart(d);
+  while (jsondec_arrnext(d)) {
+    upb_msg *value_msg = upb_msg_new(value_m, d->arena);
+    upb_msgval value;
+    value.msg_val = value_msg;
+    upb_array_append(values, value, d->arena);
+    jsondec_wellknownvalue(d, value_msg, value_m);
+  }
+  jsondec_arrend(d);
+}
+
+static void jsondec_struct(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  const upb_fielddef *fields_f = upb_msgdef_itof(m, 1);
+  const upb_msgdef *entry_m = upb_fielddef_msgsubdef(fields_f);
+  const upb_fielddef *value_f = upb_msgdef_itof(entry_m, 2);
+  const upb_msgdef *value_m = upb_fielddef_msgsubdef(value_f);
+  upb_map *fields = upb_msg_mutable(msg, fields_f, d->arena).map;
+
+  jsondec_objstart(d);
+  while (jsondec_objnext(d)) {
+    upb_msgval key, value;
+    upb_msg *value_msg = upb_msg_new(value_m, d->arena);
+    key.str_val = jsondec_string(d);
+    value.msg_val = value_msg;
+    upb_map_set(fields, key, value, d->arena);
+    jsondec_entrysep(d);
+    jsondec_wellknownvalue(d, value_msg, value_m);
+  }
+  jsondec_objend(d);
+}
+
+static void jsondec_wellknownvalue(jsondec *d, upb_msg *msg,
+                                   const upb_msgdef *m) {
+  upb_msgval val;
+  const upb_fielddef *f;
+  upb_msg *submsg;
+
+  switch (jsondec_peek(d)) {
+    case JD_NUMBER:
+      /* double number_value = 2; */
+      f = upb_msgdef_itof(m, 2);
+      val.double_val = jsondec_number(d);
+      break;
+    case JD_STRING:
+      /* string string_value = 3; */
+      f = upb_msgdef_itof(m, 3);
+      val.str_val = jsondec_string(d);
+      break;
+    case JD_FALSE:
+      /* bool bool_value = 4; */
+      f = upb_msgdef_itof(m, 4);
+      val.bool_val = false;
+      jsondec_false(d);
+      break;
+    case JD_TRUE:
+      /* bool bool_value = 4; */
+      f = upb_msgdef_itof(m, 4);
+      val.bool_val = true;
+      jsondec_true(d);
+      break;
+    case JD_NULL:
+      /* NullValue null_value = 1; */
+      f = upb_msgdef_itof(m, 1);
+      val.int32_val = 0;
+      jsondec_null(d);
+      break;
+    /* Note: these cases return, because upb_msg_mutable() is enough. */
+    case JD_OBJECT:
+      /* Struct struct_value = 5; */
+      f = upb_msgdef_itof(m, 5);
+      submsg = upb_msg_mutable(msg, f, d->arena).msg;
+      jsondec_struct(d, submsg, upb_fielddef_msgsubdef(f));
+      return;
+    case JD_ARRAY:
+      /* ListValue list_value = 6; */
+      f = upb_msgdef_itof(m, 6);
+      submsg = upb_msg_mutable(msg, f, d->arena).msg;
+      jsondec_listvalue(d, submsg, upb_fielddef_msgsubdef(f));
+      return;
+    default:
+      UPB_UNREACHABLE();
+  }
+
+  upb_msg_set(msg, f, val, d->arena);
+}
+
+static upb_strview jsondec_mask(jsondec *d, const char *buf, const char *end) {
+  /* FieldMask fields grow due to inserted '_' characters, so we can't do the
+   * transform in place. */
+  const char *ptr = buf;
+  upb_strview ret;
+  char *out;
+
+  ret.size = end - ptr;
+  while (ptr < end) {
+    ret.size += (*ptr >= 'A' && *ptr <= 'Z');
+    ptr++;
+  }
+
+  out = upb_arena_malloc(d->arena, ret.size);
+  ptr = buf;
+  ret.data = out;
+
+  while (ptr < end) {
+    char ch = *ptr++;
+    if (ch >= 'A' && ch <= 'Z') {
+      *out++ = '_';
+      *out++ = ch + 32;
+    } else if (ch == '_') {
+      jsondec_err(d, "field mask may not contain '_'");
+    } else {
+      *out++ = ch;
+    }
+  }
+
+  return ret;
+}
+
+static void jsondec_fieldmask(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  /* repeated string paths = 1; */
+  const upb_fielddef *paths_f = upb_msgdef_itof(m, 1);
+  upb_array *arr = upb_msg_mutable(msg, paths_f, d->arena).array;
+  upb_strview str = jsondec_string(d);
+  const char *ptr = str.data;
+  const char *end = ptr + str.size;
+  upb_msgval val;
+
+  while (ptr < end) {
+    const char *elem_end = memchr(ptr, ',', end - ptr);
+    if (elem_end) {
+      val.str_val = jsondec_mask(d, ptr, elem_end);
+      ptr = elem_end + 1;
+    } else {
+      val.str_val = jsondec_mask(d, ptr, end);
+      ptr = end;
+    }
+    upb_array_append(arr, val, d->arena);
+  }
+}
+
+static void jsondec_anyfield(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  if (upb_msgdef_wellknowntype(m) == UPB_WELLKNOWN_UNSPECIFIED) {
+    /* For regular types: {"@type": "[user type]", "f1": <V1>, "f2": <V2>}
+     * where f1, f2, etc. are the normal fields of this type. */
+    jsondec_field(d, msg, m);
+  } else {
+    /* For well-known types: {"@type": "[well-known type]", "value": <X>}
+     * where <X> is whatever encoding the WKT normally uses. */
+    upb_strview str = jsondec_string(d);
+    jsondec_entrysep(d);
+    if (!jsondec_streql(str, "value")) {
+      jsondec_err(d, "Key for well-known type must be 'value'");
+    }
+    jsondec_wellknown(d, msg, m);
+  }
+}
+
+static const upb_msgdef *jsondec_typeurl(jsondec *d, upb_msg *msg,
+                                         const upb_msgdef *m) {
+  const upb_fielddef *type_url_f = upb_msgdef_itof(m, 1);
+  const upb_msgdef *type_m;
+  upb_strview type_url = jsondec_string(d);
+  const char *end = type_url.data + type_url.size;
+  const char *ptr = end;
+  upb_msgval val;
+
+  val.str_val = type_url;
+  upb_msg_set(msg, type_url_f, val, d->arena);
+
+  /* Find message name after the last '/' */
+  while (ptr > type_url.data && *--ptr != '/') {}
+
+  if (ptr == type_url.data || ptr == end) {
+    jsondec_err(d, "Type url must have at least one '/' and non-empty host");
+  }
+
+  ptr++;
+  type_m = upb_symtab_lookupmsg2(d->any_pool, ptr, end - ptr);
+
+  if (!type_m) {
+    jsondec_err(d, "Type was not found");
+  }
+
+  return type_m;
+}
+
+static void jsondec_any(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  /* string type_url = 1;
+   * bytes value = 2; */
+  const upb_fielddef *value_f = upb_msgdef_itof(m, 2);
+  upb_msg *any_msg;
+  const upb_msgdef *any_m = NULL;
+  const char *pre_type_data = NULL;
+  const char *pre_type_end = NULL;
+  upb_msgval encoded;
+
+  jsondec_objstart(d);
+
+  /* Scan looking for "@type", which is not necessarily first. */
+  while (!any_m && jsondec_objnext(d)) {
+    const char *start = d->ptr;
+    upb_strview name = jsondec_string(d);
+    jsondec_entrysep(d);
+    if (jsondec_streql(name, "@type")) {
+      any_m = jsondec_typeurl(d, msg, m);
+      if (pre_type_data) {
+        pre_type_end = start;
+        while (*pre_type_end != ',') pre_type_end--;
+      }
+    } else {
+      if (!pre_type_data) pre_type_data = start;
+      jsondec_skipval(d);
+    }
+  }
+
+  if (!any_m) {
+    jsondec_err(d, "Any object didn't contain a '@type' field");
+  }
+
+  any_msg = upb_msg_new(any_m, d->arena);
+
+  if (pre_type_data) {
+    size_t len = pre_type_end - pre_type_data + 1;
+    char *tmp = upb_arena_malloc(d->arena, len);
+    const char *saved_ptr = d->ptr;
+    const char *saved_end = d->end;
+    memcpy(tmp, pre_type_data, len - 1);
+    tmp[len - 1] = '}';
+    d->ptr = tmp;
+    d->end = tmp + len;
+    d->is_first = true;
+    while (jsondec_objnext(d)) {
+      jsondec_anyfield(d, any_msg, any_m);
+    }
+    d->ptr = saved_ptr;
+    d->end = saved_end;
+  }
+
+  while (jsondec_objnext(d)) {
+    jsondec_anyfield(d, any_msg, any_m);
+  }
+
+  jsondec_objend(d);
+
+  encoded.str_val.data = upb_encode(any_msg, upb_msgdef_layout(any_m), d->arena,
+                                    &encoded.str_val.size);
+  upb_msg_set(msg, value_f, encoded, d->arena);
+}
+
+static void jsondec_wrapper(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  const upb_fielddef *value_f = upb_msgdef_itof(m, 1);
+  upb_msgval val = jsondec_value(d, value_f);
+  upb_msg_set(msg, value_f, val, d->arena);
+}
+
+static void jsondec_wellknown(jsondec *d, upb_msg *msg, const upb_msgdef *m) {
+  switch (upb_msgdef_wellknowntype(m)) {
+    case UPB_WELLKNOWN_ANY:
+      jsondec_any(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_FIELDMASK:
+      jsondec_fieldmask(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_DURATION:
+      jsondec_duration(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_TIMESTAMP:
+      jsondec_timestamp(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_VALUE:
+      jsondec_wellknownvalue(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_LISTVALUE:
+      jsondec_listvalue(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_STRUCT:
+      jsondec_struct(d, msg, m);
+      break;
+    case UPB_WELLKNOWN_DOUBLEVALUE:
+    case UPB_WELLKNOWN_FLOATVALUE:
+    case UPB_WELLKNOWN_INT64VALUE:
+    case UPB_WELLKNOWN_UINT64VALUE:
+    case UPB_WELLKNOWN_INT32VALUE:
+    case UPB_WELLKNOWN_UINT32VALUE:
+    case UPB_WELLKNOWN_STRINGVALUE:
+    case UPB_WELLKNOWN_BYTESVALUE:
+    case UPB_WELLKNOWN_BOOLVALUE:
+      jsondec_wrapper(d, msg, m);
+      break;
+    default:
+      UPB_UNREACHABLE();
+  }
+}
+
+bool upb_json_decode(const char *buf, size_t size, upb_msg *msg,
+                     const upb_msgdef *m, const upb_symtab *any_pool,
+                     int options, upb_arena *arena, upb_status *status) {
+  jsondec d;
+  d.ptr = buf;
+  d.end = buf + size;
+  d.arena = arena;
+  d.any_pool = any_pool;
+  d.status = status;
+  d.options = options;
+  d.depth = 64;
+  d.line = 1;
+  d.line_begin = d.ptr;
+  d.debug_field = NULL;
+  d.is_first = false;
+
+  if (setjmp(d.err)) return false;
+
+  jsondec_tomsg(&d, msg, m);
+  return true;
+}
+
+
+#include <ctype.h>
+#include <float.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+
+
+
+typedef struct {
+  char *buf, *ptr, *end;
+  size_t overflow;
+  int indent_depth;
+  int options;
+  const upb_symtab *ext_pool;
+  jmp_buf err;
+  upb_status *status;
+  upb_arena *arena;
+} jsonenc;
+
+static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m);
+static void jsonenc_scalar(jsonenc *e, upb_msgval val, const upb_fielddef *f);
+static void jsonenc_msgfield(jsonenc *e, const upb_msg *msg,
+                             const upb_msgdef *m);
+static void jsonenc_msgfields(jsonenc *e, const upb_msg *msg,
+                              const upb_msgdef *m);
+static void jsonenc_value(jsonenc *e, const upb_msg *msg, const upb_msgdef *m);
+
+UPB_NORETURN static void jsonenc_err(jsonenc *e, const char *msg) {
+  upb_status_seterrmsg(e->status, msg);
+  longjmp(e->err, 1);
+}
+
+UPB_NORETURN static void jsonenc_errf(jsonenc *e, const char *fmt, ...) {
+  va_list argp;
+  va_start(argp, fmt);
+  upb_status_vseterrf(e->status, fmt, argp);
+  va_end(argp);
+  longjmp(e->err, 1);
+}
+
+static upb_arena *jsonenc_arena(jsonenc *e) {
+  /* Create lazily, since it's only needed for Any */
+  if (!e->arena) {
+    e->arena = upb_arena_new();
+  }
+  return e->arena;
+}
+
+static void jsonenc_putbytes(jsonenc *e, const void *data, size_t len) {
+  size_t have = e->end - e->ptr;
+  if (UPB_LIKELY(have >= len)) {
+    memcpy(e->ptr, data, len);
+    e->ptr += len;
+  } else {
+    if (have) memcpy(e->ptr, data, have);
+    e->ptr += have;
+    e->overflow += (len - have);
+  }
+}
+
+static void jsonenc_putstr(jsonenc *e, const char *str) {
+  jsonenc_putbytes(e, str, strlen(str));
+}
+
+static void jsonenc_printf(jsonenc *e, const char *fmt, ...) {
+  size_t n;
+  size_t have = e->end - e->ptr;
+  va_list args;
+
+  va_start(args, fmt);
+  n = _upb_vsnprintf(e->ptr, have, fmt, args);
+  va_end(args);
+
+  if (UPB_LIKELY(have > n)) {
+    e->ptr += n;
+  } else {
+    e->ptr += have;
+    e->overflow += (n - have);
+  }
+}
+
+static void jsonenc_nanos(jsonenc *e, int32_t nanos) {
+  int digits = 9;
+
+  if (nanos == 0) return;
+  if (nanos < 0 || nanos >= 1000000000) {
+    jsonenc_err(e, "error formatting timestamp as JSON: invalid nanos");
+  }
+
+  while (nanos % 1000 == 0) {
+    nanos /= 1000;
+    digits -= 3;
+  }
+
+  jsonenc_printf(e, ".%0.*" PRId32, digits, nanos);
+}
+
+static void jsonenc_timestamp(jsonenc *e, const upb_msg *msg,
+                              const upb_msgdef *m) {
+  const upb_fielddef *seconds_f = upb_msgdef_itof(m, 1);
+  const upb_fielddef *nanos_f = upb_msgdef_itof(m, 2);
+  int64_t seconds = upb_msg_get(msg, seconds_f).int64_val;
+  int32_t nanos = upb_msg_get(msg, nanos_f).int32_val;
+  int L, N, I, J, K, hour, min, sec;
+
+  if (seconds < -62135596800) {
+    jsonenc_err(e,
+                "error formatting timestamp as JSON: minimum acceptable value "
+                "is 0001-01-01T00:00:00Z");
+  } else if (seconds > 253402300799) {
+    jsonenc_err(e,
+                "error formatting timestamp as JSON: maximum acceptable value "
+                "is 9999-12-31T23:59:59Z");
+  }
+
+  /* Julian Day -> Y/M/D, Algorithm from:
+   * Fliegel, H. F., and Van Flandern, T. C., "A Machine Algorithm for
+   *   Processing Calendar Dates," Communications of the Association of
+   *   Computing Machines, vol. 11 (1968), p. 657.  */
+  L = (int)(seconds / 86400) + 68569 + 2440588;
+  N = 4 * L / 146097;
+  L = L - (146097 * N + 3) / 4;
+  I = 4000 * (L + 1) / 1461001;
+  L = L - 1461 * I / 4 + 31;
+  J = 80 * L / 2447;
+  K = L - 2447 * J / 80;
+  L = J / 11;
+  J = J + 2 - 12 * L;
+  I = 100 * (N - 49) + I + L;
+
+  sec = seconds % 60;
+  min = (seconds / 60) % 60;
+  hour = (seconds / 3600) % 24;
+
+  jsonenc_printf(e, "\"%04d-%02d-%02dT%02d:%02d:%02d", I, J, K, hour, min, sec);
+  jsonenc_nanos(e, nanos);
+  jsonenc_putstr(e, "Z\"");
+}
+
+static void jsonenc_duration(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
+  const upb_fielddef *seconds_f = upb_msgdef_itof(m, 1);
+  const upb_fielddef *nanos_f = upb_msgdef_itof(m, 2);
+  int64_t seconds = upb_msg_get(msg, seconds_f).int64_val;
+  int32_t nanos = upb_msg_get(msg, nanos_f).int32_val;
+
+  if (seconds > 315576000000 || seconds < -315576000000 ||
+      (seconds < 0) != (nanos < 0)) {
+    jsonenc_err(e, "bad duration");
+  }
+
+  if (nanos < 0) {
+    nanos = -nanos;
+  }
+
+  jsonenc_printf(e, "\"%" PRId64, seconds);
+  jsonenc_nanos(e, nanos);
+  jsonenc_putstr(e, "s\"");
+}
+
+static void jsonenc_enum(int32_t val, const upb_fielddef *f, jsonenc *e) {
+  const upb_enumdef *e_def = upb_fielddef_enumsubdef(f);
+  const char *name = upb_enumdef_iton(e_def, val);
+
+  if (name) {
+    jsonenc_printf(e, "\"%s\"", name);
+  } else {
+    jsonenc_printf(e, "%" PRId32, val);
+  }
+}
+
+static void jsonenc_bytes(jsonenc *e, upb_strview str) {
+  /* This is the regular base64, not the "web-safe" version. */
+  static const char base64[] =
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  const unsigned char *ptr = (unsigned char*)str.data;
+  const unsigned char *end = ptr + str.size;
+  char buf[4];
+
+  jsonenc_putstr(e, "\"");
+
+  while (end - ptr >= 3) {
+    buf[0] = base64[ptr[0] >> 2];
+    buf[1] = base64[((ptr[0] & 0x3) << 4) | (ptr[1] >> 4)];
+    buf[2] = base64[((ptr[1] & 0xf) << 2) | (ptr[2] >> 6)];
+    buf[3] = base64[ptr[2] & 0x3f];
+    jsonenc_putbytes(e, buf, 4);
+    ptr += 3;
+  }
+
+  switch (end - ptr) {
+    case 2:
+      buf[0] = base64[ptr[0] >> 2];
+      buf[1] = base64[((ptr[0] & 0x3) << 4) | (ptr[1] >> 4)];
+      buf[2] = base64[(ptr[1] & 0xf) << 2];
+      buf[3] = '=';
+      jsonenc_putbytes(e, buf, 4);
+      break;
+    case 1:
+      buf[0] = base64[ptr[0] >> 2];
+      buf[1] = base64[((ptr[0] & 0x3) << 4)];
+      buf[2] = '=';
+      buf[3] = '=';
+      jsonenc_putbytes(e, buf, 4);
+      break;
+  }
+
+  jsonenc_putstr(e, "\"");
+}
+
+static void jsonenc_stringbody(jsonenc *e, upb_strview str) {
+  const char *ptr = str.data;
+  const char *end = ptr + str.size;
+
+  while (ptr < end) {
+    switch (*ptr) {
+      case '\n':
+        jsonenc_putstr(e, "\\n");
+        break;
+      case '\r':
+        jsonenc_putstr(e, "\\r");
+        break;
+      case '\t':
+        jsonenc_putstr(e, "\\t");
+        break;
+      case '\"':
+        jsonenc_putstr(e, "\\\"");
+        break;
+      case '\f':
+        jsonenc_putstr(e, "\\f");
+        break;
+      case '\b':
+        jsonenc_putstr(e, "\\b");
+        break;
+      case '\\':
+        jsonenc_putstr(e, "\\\\");
+        break;
+      default:
+        if ((uint8_t)*ptr < 0x20) {
+          jsonenc_printf(e, "\\u%04x", (int)(uint8_t)*ptr);
+        } else {
+          /* This could be a non-ASCII byte.  We rely on the string being valid
+           * UTF-8. */
+          jsonenc_putbytes(e, ptr, 1);
+        }
+        break;
+    }
+    ptr++;
+  }
+}
+
+static void jsonenc_string(jsonenc *e, upb_strview str) {
+  jsonenc_putstr(e, "\"");
+  jsonenc_stringbody(e, str);
+  jsonenc_putstr(e, "\"");
+}
+
+static void jsonenc_double(jsonenc *e, const char *fmt, double val) {
+  if (val == UPB_INFINITY) {
+    jsonenc_putstr(e, "\"Infinity\"");
+  } else if (val == -UPB_INFINITY) {
+    jsonenc_putstr(e, "\"-Infinity\"");
+  } else if (val != val) {
+    jsonenc_putstr(e, "\"NaN\"");
+  } else {
+    jsonenc_printf(e, fmt, val);
+  }
+}
+
+static void jsonenc_wrapper(jsonenc *e, const upb_msg *msg,
+                            const upb_msgdef *m) {
+  const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
+  upb_msgval val = upb_msg_get(msg, val_f);
+  jsonenc_scalar(e, val, val_f);
+}
+
+static const upb_msgdef *jsonenc_getanymsg(jsonenc *e, upb_strview type_url) {
+  /* Find last '/', if any. */
+  const char *end = type_url.data + type_url.size;
+  const char *ptr = end;
+  const upb_msgdef *ret;
+
+  if (!e->ext_pool) {
+    jsonenc_err(e, "Tried to encode Any, but no symtab was provided");
+  }
+
+  if (type_url.size == 0) goto badurl;
+
+  while (true) {
+    if (--ptr == type_url.data) {
+      /* Type URL must contain at least one '/', with host before. */
+      goto badurl;
+    }
+    if (*ptr == '/') {
+      ptr++;
+      break;
+    }
+  }
+
+  ret = upb_symtab_lookupmsg2(e->ext_pool, ptr, end - ptr);
+
+  if (!ret) {
+    jsonenc_errf(e, "Couldn't find Any type: %.*s (full URL: " UPB_STRVIEW_FORMAT ")", (int)(end - ptr), ptr, UPB_STRVIEW_ARGS(type_url));
+  }
+
+  return ret;
+
+badurl:
+  jsonenc_errf(
+      e, "Bad type URL: " UPB_STRVIEW_FORMAT, UPB_STRVIEW_ARGS(type_url));
+}
+
+static void jsonenc_any(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
+  const upb_fielddef *type_url_f = upb_msgdef_itof(m, 1);
+  const upb_fielddef *value_f = upb_msgdef_itof(m, 2);
+  upb_strview type_url = upb_msg_get(msg, type_url_f).str_val;
+  upb_strview value = upb_msg_get(msg, value_f).str_val;
+  const upb_msgdef *any_m = jsonenc_getanymsg(e, type_url);
+  const upb_msglayout *any_layout = upb_msgdef_layout(any_m);
+  upb_arena *arena = jsonenc_arena(e);
+  upb_msg *any = upb_msg_new(any_m, arena);
+
+  if (!upb_decode(value.data, value.size, any, any_layout, arena)) {
+    jsonenc_err(e, "Error decoding message in Any");
+  }
+
+  jsonenc_putstr(e, "{\"@type\":");
+  jsonenc_string(e, type_url);
+  jsonenc_putstr(e, ",");
+
+  if (upb_msgdef_wellknowntype(any_m) == UPB_WELLKNOWN_UNSPECIFIED) {
+    /* Regular messages: {"@type": "...","foo": 1, "bar": 2} */
+    jsonenc_msgfields(e, any, any_m);
+  } else {
+    /* Well-known type: {"@type": "...","value": <well-known encoding>} */
+    jsonenc_putstr(e, "\"value\":");
+    jsonenc_msgfield(e, any, any_m);
+  }
+
+  jsonenc_putstr(e, "}");
+}
+
+static void jsonenc_putsep(jsonenc *e, const char *str, bool *first) {
+  if (*first) {
+    *first = false;
+  } else {
+    jsonenc_putstr(e, str);
+  }
+}
+
+static void jsonenc_fieldpath(jsonenc *e, upb_strview path) {
+  const char *ptr = path.data;
+  const char *end = ptr + path.size;
+
+  while (ptr < end) {
+    char ch = *ptr;
+
+    if (ch >= 'A' && ch <= 'Z') {
+      jsonenc_err(e, "Field mask element may not have upper-case letter.");
+    } else if (ch == '_') {
+      if (ptr == end - 1 || *(ptr + 1) < 'a' || *(ptr + 1) > 'z') {
+        jsonenc_err(e, "Underscore must be followed by a lowercase letter.");
+      }
+      ch = *++ptr - 32;
+    }
+
+    jsonenc_putbytes(e, &ch, 1);
+    ptr++;
+  }
+}
+
+static void jsonenc_fieldmask(jsonenc *e, const upb_msg *msg,
+                              const upb_msgdef *m) {
+  const upb_fielddef *paths_f = upb_msgdef_itof(m, 1);
+  const upb_array *paths = upb_msg_get(msg, paths_f).array_val;
+  bool first = true;
+  size_t i, n = 0;
+
+  if (paths) n = upb_array_size(paths);
+
+  jsonenc_putstr(e, "\"");
+
+  for (i = 0; i < n; i++) {
+    jsonenc_putsep(e, ",", &first);
+    jsonenc_fieldpath(e, upb_array_get(paths, i).str_val);
+  }
+
+  jsonenc_putstr(e, "\"");
+}
+
+static void jsonenc_struct(jsonenc *e, const upb_msg *msg,
+                           const upb_msgdef *m) {
+  const upb_fielddef *fields_f = upb_msgdef_itof(m, 1);
+  const upb_map *fields = upb_msg_get(msg, fields_f).map_val;
+  const upb_msgdef *entry_m = upb_fielddef_msgsubdef(fields_f);
+  const upb_fielddef *value_f = upb_msgdef_itof(entry_m, 2);
+  size_t iter = UPB_MAP_BEGIN;
+  bool first = true;
+
+  jsonenc_putstr(e, "{");
+
+  if (fields) {
+    while (upb_mapiter_next(fields, &iter)) {
+      upb_msgval key = upb_mapiter_key(fields, iter);
+      upb_msgval val = upb_mapiter_value(fields, iter);
+
+      jsonenc_putsep(e, ",", &first);
+      jsonenc_string(e, key.str_val);
+      jsonenc_putstr(e, ":");
+      jsonenc_value(e, val.msg_val, upb_fielddef_msgsubdef(value_f));
+    }
+  }
+
+  jsonenc_putstr(e, "}");
+}
+
+static void jsonenc_listvalue(jsonenc *e, const upb_msg *msg,
+                              const upb_msgdef *m) {
+  const upb_fielddef *values_f = upb_msgdef_itof(m, 1);
+  const upb_msgdef *values_m = upb_fielddef_msgsubdef(values_f);
+  const upb_array *values = upb_msg_get(msg, values_f).array_val;
+  size_t i;
+  bool first = true;
+
+  jsonenc_putstr(e, "[");
+
+  if (values) {
+    const size_t size = upb_array_size(values);
+    for (i = 0; i < size; i++) {
+      upb_msgval elem = upb_array_get(values, i);
+
+      jsonenc_putsep(e, ",", &first);
+      jsonenc_value(e, elem.msg_val, values_m);
+    }
+  }
+
+  jsonenc_putstr(e, "]");
+}
+
+static void jsonenc_value(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
+  /* TODO(haberman): do we want a reflection method to get oneof case? */
+  size_t iter = UPB_MSG_BEGIN;
+  const upb_fielddef *f;
+  upb_msgval val;
+
+  if (!upb_msg_next(msg, m, NULL,  &f, &val, &iter)) {
+    jsonenc_err(e, "No value set in Value proto");
+  }
+
+  switch (upb_fielddef_number(f)) {
+    case 1:
+      jsonenc_putstr(e, "null");
+      break;
+    case 2:
+      jsonenc_double(e, "%.17g", val.double_val);
+      break;
+    case 3:
+      jsonenc_string(e, val.str_val);
+      break;
+    case 4:
+      jsonenc_putstr(e, val.bool_val ? "true" : "false");
+      break;
+    case 5:
+      jsonenc_struct(e, val.msg_val, upb_fielddef_msgsubdef(f));
+      break;
+    case 6:
+      jsonenc_listvalue(e, val.msg_val, upb_fielddef_msgsubdef(f));
+      break;
+  }
+}
+
+static void jsonenc_msgfield(jsonenc *e, const upb_msg *msg,
+                             const upb_msgdef *m) {
+  switch (upb_msgdef_wellknowntype(m)) {
+    case UPB_WELLKNOWN_UNSPECIFIED:
+      jsonenc_msg(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_ANY:
+      jsonenc_any(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_FIELDMASK:
+      jsonenc_fieldmask(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_DURATION:
+      jsonenc_duration(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_TIMESTAMP:
+      jsonenc_timestamp(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_DOUBLEVALUE:
+    case UPB_WELLKNOWN_FLOATVALUE:
+    case UPB_WELLKNOWN_INT64VALUE:
+    case UPB_WELLKNOWN_UINT64VALUE:
+    case UPB_WELLKNOWN_INT32VALUE:
+    case UPB_WELLKNOWN_UINT32VALUE:
+    case UPB_WELLKNOWN_STRINGVALUE:
+    case UPB_WELLKNOWN_BYTESVALUE:
+    case UPB_WELLKNOWN_BOOLVALUE:
+      jsonenc_wrapper(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_VALUE:
+      jsonenc_value(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_LISTVALUE:
+      jsonenc_listvalue(e, msg, m);
+      break;
+    case UPB_WELLKNOWN_STRUCT:
+      jsonenc_struct(e, msg, m);
+      break;
+  }
+}
+
+static void jsonenc_scalar(jsonenc *e, upb_msgval val, const upb_fielddef *f) {
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_BOOL:
+      jsonenc_putstr(e, val.bool_val ? "true" : "false");
+      break;
+    case UPB_TYPE_FLOAT:
+      jsonenc_double(e, "%.9g", val.float_val);
+      break;
+    case UPB_TYPE_DOUBLE:
+      jsonenc_double(e, "%.17g", val.double_val);
+      break;
+    case UPB_TYPE_INT32:
+      jsonenc_printf(e, "%" PRId32, val.int32_val);
+      break;
+    case UPB_TYPE_UINT32:
+      jsonenc_printf(e, "%" PRIu32, val.uint32_val);
+      break;
+    case UPB_TYPE_INT64:
+      jsonenc_printf(e, "\"%" PRId64 "\"", val.int64_val);
+      break;
+    case UPB_TYPE_UINT64:
+      jsonenc_printf(e, "\"%" PRIu64 "\"", val.uint64_val);
+      break;
+    case UPB_TYPE_STRING:
+      jsonenc_string(e, val.str_val);
+      break;
+    case UPB_TYPE_BYTES:
+      jsonenc_bytes(e, val.str_val);
+      break;
+    case UPB_TYPE_ENUM:
+      jsonenc_enum(val.int32_val, f, e);
+      break;
+    case UPB_TYPE_MESSAGE:
+      jsonenc_msgfield(e, val.msg_val, upb_fielddef_msgsubdef(f));
+      break;
+  }
+}
+
+static void jsonenc_mapkey(jsonenc *e, upb_msgval val, const upb_fielddef *f) {
+  jsonenc_putstr(e, "\"");
+
+  switch (upb_fielddef_type(f)) {
+    case UPB_TYPE_BOOL:
+      jsonenc_putstr(e, val.bool_val ? "true" : "false");
+      break;
+    case UPB_TYPE_INT32:
+      jsonenc_printf(e, "%" PRId32, val.int32_val);
+      break;
+    case UPB_TYPE_UINT32:
+      jsonenc_printf(e, "%" PRIu32, val.uint32_val);
+      break;
+    case UPB_TYPE_INT64:
+      jsonenc_printf(e, "%" PRId64, val.int64_val);
+      break;
+    case UPB_TYPE_UINT64:
+      jsonenc_printf(e, "%" PRIu64, val.uint64_val);
+      break;
+    case UPB_TYPE_STRING:
+      jsonenc_stringbody(e, val.str_val);
+      break;
+    default:
+      UPB_UNREACHABLE();
+  }
+
+  jsonenc_putstr(e, "\":");
+}
+
+static void jsonenc_array(jsonenc *e, const upb_array *arr,
+                         const upb_fielddef *f) {
+  size_t i;
+  size_t size = upb_array_size(arr);
+  bool first = true;
+
+  jsonenc_putstr(e, "[");
+
+  for (i = 0; i < size; i++) {
+    jsonenc_putsep(e, ",", &first);
+    jsonenc_scalar(e, upb_array_get(arr, i), f);
+  }
+
+  jsonenc_putstr(e, "]");
+}
+
+static void jsonenc_map(jsonenc *e, const upb_map *map, const upb_fielddef *f) {
+  const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+  const upb_fielddef *key_f = upb_msgdef_itof(entry, 1);
+  const upb_fielddef *val_f = upb_msgdef_itof(entry, 2);
+  size_t iter = UPB_MAP_BEGIN;
+  bool first = true;
+
+  jsonenc_putstr(e, "{");
+
+  while (upb_mapiter_next(map, &iter)) {
+    jsonenc_putsep(e, ",", &first);
+    jsonenc_mapkey(e, upb_mapiter_key(map, iter), key_f);
+    jsonenc_scalar(e, upb_mapiter_value(map, iter), val_f);
+  }
+
+  jsonenc_putstr(e, "}");
+}
+
+static void jsonenc_fieldval(jsonenc *e, const upb_fielddef *f,
+                             upb_msgval val, bool *first) {
+  const char *name;
+
+  if (e->options & UPB_JSONENC_PROTONAMES) {
+    name = upb_fielddef_name(f);
+  } else {
+    name = upb_fielddef_jsonname(f);
+  }
+
+  jsonenc_putsep(e, ",", first);
+  jsonenc_printf(e, "\"%s\":", name);
+
+  if (upb_fielddef_ismap(f)) {
+    jsonenc_map(e, val.map_val, f);
+  } else if (upb_fielddef_isseq(f)) {
+    jsonenc_array(e, val.array_val, f);
+  } else {
+    jsonenc_scalar(e, val, f);
+  }
+}
+
+static void jsonenc_msgfields(jsonenc *e, const upb_msg *msg,
+                              const upb_msgdef *m) {
+  upb_msgval val;
+  const upb_fielddef *f;
+  bool first = true;
+
+  if (e->options & UPB_JSONENC_EMITDEFAULTS) {
+    /* Iterate over all fields. */
+    upb_msg_field_iter i;
+    for (upb_msg_field_begin(&i, m); !upb_msg_field_done(&i);
+         upb_msg_field_next(&i)) {
+      f = upb_msg_iter_field(&i);
+      jsonenc_fieldval(e, f, upb_msg_get(msg, f), &first);
+    }
+  } else {
+    /* Iterate over non-empty fields. */
+    size_t iter = UPB_MSG_BEGIN;
+    while (upb_msg_next(msg, m, e->ext_pool, &f, &val, &iter)) {
+      jsonenc_fieldval(e, f, val, &first);
+    }
+  }
+}
+
+static void jsonenc_msg(jsonenc *e, const upb_msg *msg, const upb_msgdef *m) {
+  jsonenc_putstr(e, "{");
+  jsonenc_msgfields(e, msg, m);
+  jsonenc_putstr(e, "}");
+}
+
+static size_t jsonenc_nullz(jsonenc *e, size_t size) {
+  size_t ret = e->ptr - e->buf + e->overflow;
+
+  if (size > 0) {
+    if (e->ptr == e->end) e->ptr--;
+    *e->ptr = '\0';
+  }
+
+  return ret;
+}
+
+size_t upb_json_encode(const upb_msg *msg, const upb_msgdef *m,
+                       const upb_symtab *ext_pool, int options, char *buf,
+                       size_t size, upb_status *status) {
+  jsonenc e;
+
+  e.buf = buf;
+  e.ptr = buf;
+  e.end = buf + size;
+  e.overflow = 0;
+  e.options = options;
+  e.ext_pool = ext_pool;
+  e.status = status;
+  e.arena = NULL;
+
+  if (setjmp(e.err)) return -1;
+
+  jsonenc_msgfield(&e, msg, m);
+  if (e.arena) upb_arena_free(e.arena);
+  return jsonenc_nullz(&e, size);
+}
+/* See port_def.inc.  This should #undef all macros #defined there. */
+
+#undef UPB_MAPTYPE_STRING
+#undef UPB_SIZE
+#undef UPB_PTR_AT
+#undef UPB_READ_ONEOF
+#undef UPB_WRITE_ONEOF
+#undef UPB_INLINE
+#undef UPB_ALIGN_UP
+#undef UPB_ALIGN_DOWN
+#undef UPB_ALIGN_MALLOC
+#undef UPB_ALIGN_OF
+#undef UPB_FORCEINLINE
+#undef UPB_NOINLINE
+#undef UPB_NORETURN
+#undef UPB_MAX
+#undef UPB_MIN
+#undef UPB_UNUSED
+#undef UPB_ASSUME
+#undef UPB_ASSERT
+#undef UPB_ASSERT_DEBUGVAR
+#undef UPB_UNREACHABLE
+#undef UPB_INFINITY
+#undef UPB_MSVC_VSNPRINTF
+#undef _upb_snprintf
+#undef _upb_vsnprintf
+#undef _upb_va_copy
diff --git a/php/ext/google/protobuf2/php-upb.h b/php/ext/google/protobuf2/php-upb.h
new file mode 100644
index 0000000..a07885f
--- /dev/null
+++ b/php/ext/google/protobuf2/php-upb.h
@@ -0,0 +1,3885 @@
+/* Amalgamated source file */
+#include <stdint.h>/*
+* This is where we define macros used across upb.
+*
+* All of these macros are undef'd in port_undef.inc to avoid leaking them to
+* users.
+*
+* The correct usage is:
+*
+*   #include "upb/foobar.h"
+*   #include "upb/baz.h"
+*
+*   // MUST be last included header.
+*   #include "upb/port_def.inc"
+*
+*   // Code for this file.
+*   // <...>
+*
+*   // Can be omitted for .c files, required for .h.
+*   #include "upb/port_undef.inc"
+*
+* This file is private and must not be included by users!
+*/
+#include <stdint.h>
+#include <stddef.h>
+
+#if UINTPTR_MAX == 0xffffffff
+#define UPB_SIZE(size32, size64) size32
+#else
+#define UPB_SIZE(size32, size64) size64
+#endif
+
+/* If we always read/write as a consistent type to each address, this shouldn't
+ * violate aliasing.
+ */
+#define UPB_PTR_AT(msg, ofs, type) ((type*)((char*)(msg) + (ofs)))
+
+#define UPB_READ_ONEOF(msg, fieldtype, offset, case_offset, case_val, default) \
+  *UPB_PTR_AT(msg, case_offset, int) == case_val                              \
+      ? *UPB_PTR_AT(msg, offset, fieldtype)                                   \
+      : default
+
+#define UPB_WRITE_ONEOF(msg, fieldtype, offset, value, case_offset, case_val) \
+  *UPB_PTR_AT(msg, case_offset, int) = case_val;                             \
+  *UPB_PTR_AT(msg, offset, fieldtype) = value;
+
+#define UPB_MAPTYPE_STRING 0
+
+/* UPB_INLINE: inline if possible, emit standalone code if required. */
+#ifdef __cplusplus
+#define UPB_INLINE inline
+#elif defined (__GNUC__) || defined(__clang__)
+#define UPB_INLINE static __inline__
+#else
+#define UPB_INLINE static
+#endif
+
+#define UPB_ALIGN_UP(size, align) (((size) + (align) - 1) / (align) * (align))
+#define UPB_ALIGN_DOWN(size, align) ((size) / (align) * (align))
+#define UPB_ALIGN_MALLOC(size) UPB_ALIGN_UP(size, 16)
+#define UPB_ALIGN_OF(type) offsetof (struct { char c; type member; }, member)
+
+/* Hints to the compiler about likely/unlikely branches. */
+#if defined (__GNUC__) || defined(__clang__)
+#define UPB_LIKELY(x) __builtin_expect((x),1)
+#define UPB_UNLIKELY(x) __builtin_expect((x),0)
+#else
+#define UPB_LIKELY(x) (x)
+#define UPB_UNLIKELY(x) (x)
+#endif
+
+/* Define UPB_BIG_ENDIAN manually if you're on big endian and your compiler
+ * doesn't provide these preprocessor symbols. */
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define UPB_BIG_ENDIAN
+#endif
+
+/* Macros for function attributes on compilers that support them. */
+#ifdef __GNUC__
+#define UPB_FORCEINLINE __inline__ __attribute__((always_inline))
+#define UPB_NOINLINE __attribute__((noinline))
+#define UPB_NORETURN __attribute__((__noreturn__))
+#else  /* !defined(__GNUC__) */
+#define UPB_FORCEINLINE
+#define UPB_NOINLINE
+#define UPB_NORETURN
+#endif
+
+#if __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L
+/* C99/C++11 versions. */
+#include <stdio.h>
+#define _upb_snprintf snprintf
+#define _upb_vsnprintf vsnprintf
+#define _upb_va_copy(a, b) va_copy(a, b)
+#elif defined(_MSC_VER)
+/* Microsoft C/C++ versions. */
+#include <stdarg.h>
+#include <stdio.h>
+#if _MSC_VER < 1900
+int msvc_snprintf(char* s, size_t n, const char* format, ...);
+int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
+#define UPB_MSVC_VSNPRINTF
+#define _upb_snprintf msvc_snprintf
+#define _upb_vsnprintf msvc_vsnprintf
+#else
+#define _upb_snprintf snprintf
+#define _upb_vsnprintf vsnprintf
+#endif
+#define _upb_va_copy(a, b) va_copy(a, b)
+#elif defined __GNUC__
+/* A few hacky workarounds for functions not in C89.
+ * For internal use only!
+ * TODO(haberman): fix these by including our own implementations, or finding
+ * another workaround.
+ */
+#define _upb_snprintf __builtin_snprintf
+#define _upb_vsnprintf __builtin_vsnprintf
+#define _upb_va_copy(a, b) __va_copy(a, b)
+#else
+#error Need implementations of [v]snprintf and va_copy
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \
+    (defined(_MSC_VER) && _MSC_VER >= 1900)
+/* C++11 is present */
+#else
+#error upb requires C++11 for C++ support
+#endif
+#endif
+
+#define UPB_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define UPB_MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#define UPB_UNUSED(var) (void)var
+
+/* UPB_ASSUME(): in release mode, we tell the compiler to assume this is true.
+ */
+#ifdef NDEBUG
+#ifdef __GNUC__
+#define UPB_ASSUME(expr) if (!(expr)) __builtin_unreachable()
+#elif defined _MSC_VER
+#define UPB_ASSUME(expr) if (!(expr)) __assume(0)
+#else
+#define UPB_ASSUME(expr) do {} if (false && (expr))
+#endif
+#else
+#define UPB_ASSUME(expr) assert(expr)
+#endif
+
+/* UPB_ASSERT(): in release mode, we use the expression without letting it be
+ * evaluated.  This prevents "unused variable" warnings. */
+#ifdef NDEBUG
+#define UPB_ASSERT(expr) do {} while (false && (expr))
+#else
+#define UPB_ASSERT(expr) assert(expr)
+#endif
+
+/* UPB_ASSERT_DEBUGVAR(): assert that uses functions or variables that only
+ * exist in debug mode.  This turns into regular assert. */
+#define UPB_ASSERT_DEBUGVAR(expr) assert(expr)
+
+#if defined(__GNUC__) || defined(__clang__)
+#define UPB_UNREACHABLE() do { assert(0); __builtin_unreachable(); } while(0)
+#else
+#define UPB_UNREACHABLE() do { assert(0); } while(0)
+#endif
+
+/* UPB_INFINITY representing floating-point positive infinity. */
+#include <math.h>
+#ifdef INFINITY
+#define UPB_INFINITY INFINITY
+#else
+#define UPB_INFINITY (1.0 / 0.0)
+#endif
+/*
+** upb_decode: parsing into a upb_msg using a upb_msglayout.
+*/
+
+#ifndef UPB_DECODE_H_
+#define UPB_DECODE_H_
+
+/*
+** Our memory representation for parsing tables and messages themselves.
+** Functions in this file are used by generated code and possibly reflection.
+**
+** The definitions in this file are internal to upb.
+**/
+
+#ifndef UPB_MSG_H_
+#define UPB_MSG_H_
+
+#include <stdint.h>
+#include <string.h>
+
+/*
+** upb_table
+**
+** This header is INTERNAL-ONLY!  Its interfaces are not public or stable!
+** This file defines very fast int->upb_value (inttable) and string->upb_value
+** (strtable) hash tables.
+**
+** The table uses chained scatter with Brent's variation (inspired by the Lua
+** implementation of hash tables).  The hash function for strings is Austin
+** Appleby's "MurmurHash."
+**
+** The inttable uses uintptr_t as its key, which guarantees it can be used to
+** store pointers or integers of at least 32 bits (upb isn't really useful on
+** systems where sizeof(void*) < 4).
+**
+** The table must be homogenous (all values of the same type).  In debug
+** mode, we check this on insert and lookup.
+*/
+
+#ifndef UPB_TABLE_H_
+#define UPB_TABLE_H_
+
+#include <stdint.h>
+#include <string.h>
+/*
+** This file contains shared definitions that are widely used across upb.
+*/
+
+#ifndef UPB_H_
+#define UPB_H_
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* upb_status *****************************************************************/
+
+#define UPB_STATUS_MAX_MESSAGE 127
+
+typedef struct {
+  bool ok;
+  char msg[UPB_STATUS_MAX_MESSAGE];  /* Error message; NULL-terminated. */
+} upb_status;
+
+const char *upb_status_errmsg(const upb_status *status);
+bool upb_ok(const upb_status *status);
+
+/* These are no-op if |status| is NULL. */
+void upb_status_clear(upb_status *status);
+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_vappenderrf(upb_status *status, const char *fmt, va_list args);
+
+/** upb_strview ************************************************************/
+
+typedef struct {
+  const char *data;
+  size_t size;
+} upb_strview;
+
+UPB_INLINE upb_strview upb_strview_make(const char *data, size_t size) {
+  upb_strview ret;
+  ret.data = data;
+  ret.size = size;
+  return ret;
+}
+
+UPB_INLINE upb_strview upb_strview_makez(const char *data) {
+  return upb_strview_make(data, strlen(data));
+}
+
+UPB_INLINE bool upb_strview_eql(upb_strview a, upb_strview b) {
+  return a.size == b.size && memcmp(a.data, b.data, a.size) == 0;
+}
+
+#define UPB_STRVIEW_INIT(ptr, len) {ptr, len}
+
+#define UPB_STRVIEW_FORMAT "%.*s"
+#define UPB_STRVIEW_ARGS(view) (int)(view).size, (view).data
+
+/** upb_alloc *****************************************************************/
+
+/* A upb_alloc is a possibly-stateful allocator object.
+ *
+ * It could either be an arena allocator (which doesn't require individual
+ * free() calls) or a regular malloc() (which does).  The client must therefore
+ * free memory unless it knows that the allocator is an arena allocator. */
+
+struct upb_alloc;
+typedef struct upb_alloc upb_alloc;
+
+/* A malloc()/free() function.
+ * If "size" is 0 then the function acts like free(), otherwise it acts like
+ * realloc().  Only "oldsize" bytes from a previous allocation are preserved. */
+typedef void *upb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize,
+                             size_t size);
+
+struct upb_alloc {
+  upb_alloc_func *func;
+};
+
+UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) {
+  UPB_ASSERT(alloc);
+  return alloc->func(alloc, NULL, 0, size);
+}
+
+UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize,
+                             size_t size) {
+  UPB_ASSERT(alloc);
+  return alloc->func(alloc, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) {
+  assert(alloc);
+  alloc->func(alloc, ptr, 0, 0);
+}
+
+/* The global allocator used by upb.  Uses the standard malloc()/free(). */
+
+extern upb_alloc upb_alloc_global;
+
+/* Functions that hard-code the global malloc.
+ *
+ * We still get benefit because we can put custom logic into our global
+ * allocator, like injecting out-of-memory faults in debug/testing builds. */
+
+UPB_INLINE void *upb_gmalloc(size_t size) {
+  return upb_malloc(&upb_alloc_global, size);
+}
+
+UPB_INLINE void *upb_grealloc(void *ptr, size_t oldsize, size_t size) {
+  return upb_realloc(&upb_alloc_global, ptr, oldsize, size);
+}
+
+UPB_INLINE void upb_gfree(void *ptr) {
+  upb_free(&upb_alloc_global, ptr);
+}
+
+/* upb_arena ******************************************************************/
+
+/* upb_arena is a specific allocator implementation that uses arena allocation.
+ * The user provides an allocator that will be used to allocate the underlying
+ * arena blocks.  Arenas by nature do not require the individual allocations
+ * to be freed.  However the Arena does allow users to register cleanup
+ * functions that will run when the arena is destroyed.
+ *
+ * A upb_arena is *not* thread-safe.
+ *
+ * You could write a thread-safe arena allocator that satisfies the
+ * upb_alloc interface, but it would not be as efficient for the
+ * single-threaded case. */
+
+typedef void upb_cleanup_func(void *ud);
+
+struct upb_arena;
+typedef struct upb_arena upb_arena;
+
+typedef struct {
+  /* We implement the allocator interface.
+   * This must be the first member of upb_arena!
+   * TODO(haberman): remove once handlers are gone. */
+  upb_alloc alloc;
+
+  char *ptr, *end;
+} _upb_arena_head;
+
+/* Creates an arena from the given initial block (if any -- n may be 0).
+ * Additional blocks will be allocated from |alloc|.  If |alloc| is NULL, this
+ * is a fixed-size arena and cannot grow. */
+upb_arena *upb_arena_init(void *mem, size_t n, upb_alloc *alloc);
+void upb_arena_free(upb_arena *a);
+bool upb_arena_addcleanup(upb_arena *a, void *ud, upb_cleanup_func *func);
+void upb_arena_fuse(upb_arena *a, upb_arena *b);
+void *_upb_arena_slowmalloc(upb_arena *a, size_t size);
+
+UPB_INLINE upb_alloc *upb_arena_alloc(upb_arena *a) { return (upb_alloc*)a; }
+
+UPB_INLINE void *upb_arena_malloc(upb_arena *a, size_t size) {
+  _upb_arena_head *h = (_upb_arena_head*)a;
+  void* ret;
+  size = UPB_ALIGN_MALLOC(size);
+
+  if (UPB_UNLIKELY((size_t)(h->end - h->ptr) < size)) {
+    return _upb_arena_slowmalloc(a, size);
+  }
+
+  ret = h->ptr;
+  h->ptr += size;
+  return ret;
+}
+
+UPB_INLINE void *upb_arena_realloc(upb_arena *a, void *ptr, size_t oldsize,
+                                   size_t size) {
+  void *ret = upb_arena_malloc(a, size);
+
+  if (ret && oldsize > 0) {
+    memcpy(ret, ptr, oldsize);
+  }
+
+  return ret;
+}
+
+UPB_INLINE upb_arena *upb_arena_new(void) {
+  return upb_arena_init(NULL, 0, &upb_alloc_global);
+}
+
+/* Constants ******************************************************************/
+
+/* Generic function type. */
+typedef void upb_func(void);
+
+/* A list of types as they are encoded on-the-wire. */
+typedef enum {
+  UPB_WIRE_TYPE_VARINT      = 0,
+  UPB_WIRE_TYPE_64BIT       = 1,
+  UPB_WIRE_TYPE_DELIMITED   = 2,
+  UPB_WIRE_TYPE_START_GROUP = 3,
+  UPB_WIRE_TYPE_END_GROUP   = 4,
+  UPB_WIRE_TYPE_32BIT       = 5
+} upb_wiretype_t;
+
+/* The types a field can have.  Note that this list is not identical to the
+ * types defined in descriptor.proto, which gives INT32 and SINT32 separate
+ * types (we distinguish the two with the "integer encoding" enum below). */
+typedef enum {
+  UPB_TYPE_BOOL     = 1,
+  UPB_TYPE_FLOAT    = 2,
+  UPB_TYPE_INT32    = 3,
+  UPB_TYPE_UINT32   = 4,
+  UPB_TYPE_ENUM     = 5,  /* Enum values are int32. */
+  UPB_TYPE_MESSAGE  = 6,
+  UPB_TYPE_DOUBLE   = 7,
+  UPB_TYPE_INT64    = 8,
+  UPB_TYPE_UINT64   = 9,
+  UPB_TYPE_STRING   = 10,
+  UPB_TYPE_BYTES    = 11
+} upb_fieldtype_t;
+
+/* The repeated-ness of each field; this matches descriptor.proto. */
+typedef enum {
+  UPB_LABEL_OPTIONAL = 1,
+  UPB_LABEL_REQUIRED = 2,
+  UPB_LABEL_REPEATED = 3
+} upb_label_t;
+
+/* Descriptor types, as defined in descriptor.proto. */
+typedef enum {
+  /* Old (long) names.  TODO(haberman): remove */
+  UPB_DESCRIPTOR_TYPE_DOUBLE   = 1,
+  UPB_DESCRIPTOR_TYPE_FLOAT    = 2,
+  UPB_DESCRIPTOR_TYPE_INT64    = 3,
+  UPB_DESCRIPTOR_TYPE_UINT64   = 4,
+  UPB_DESCRIPTOR_TYPE_INT32    = 5,
+  UPB_DESCRIPTOR_TYPE_FIXED64  = 6,
+  UPB_DESCRIPTOR_TYPE_FIXED32  = 7,
+  UPB_DESCRIPTOR_TYPE_BOOL     = 8,
+  UPB_DESCRIPTOR_TYPE_STRING   = 9,
+  UPB_DESCRIPTOR_TYPE_GROUP    = 10,
+  UPB_DESCRIPTOR_TYPE_MESSAGE  = 11,
+  UPB_DESCRIPTOR_TYPE_BYTES    = 12,
+  UPB_DESCRIPTOR_TYPE_UINT32   = 13,
+  UPB_DESCRIPTOR_TYPE_ENUM     = 14,
+  UPB_DESCRIPTOR_TYPE_SFIXED32 = 15,
+  UPB_DESCRIPTOR_TYPE_SFIXED64 = 16,
+  UPB_DESCRIPTOR_TYPE_SINT32   = 17,
+  UPB_DESCRIPTOR_TYPE_SINT64   = 18,
+
+  UPB_DTYPE_DOUBLE   = 1,
+  UPB_DTYPE_FLOAT    = 2,
+  UPB_DTYPE_INT64    = 3,
+  UPB_DTYPE_UINT64   = 4,
+  UPB_DTYPE_INT32    = 5,
+  UPB_DTYPE_FIXED64  = 6,
+  UPB_DTYPE_FIXED32  = 7,
+  UPB_DTYPE_BOOL     = 8,
+  UPB_DTYPE_STRING   = 9,
+  UPB_DTYPE_GROUP    = 10,
+  UPB_DTYPE_MESSAGE  = 11,
+  UPB_DTYPE_BYTES    = 12,
+  UPB_DTYPE_UINT32   = 13,
+  UPB_DTYPE_ENUM     = 14,
+  UPB_DTYPE_SFIXED32 = 15,
+  UPB_DTYPE_SFIXED64 = 16,
+  UPB_DTYPE_SINT32   = 17,
+  UPB_DTYPE_SINT64   = 18
+} upb_descriptortype_t;
+
+#define UPB_MAP_BEGIN ((size_t)-1)
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* UPB_H_ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* upb_value ******************************************************************/
+
+/* A tagged union (stored untagged inside the table) so that we can check that
+ * clients calling table accessors are correctly typed without having to have
+ * an explosion of accessors. */
+typedef enum {
+  UPB_CTYPE_INT32    = 1,
+  UPB_CTYPE_INT64    = 2,
+  UPB_CTYPE_UINT32   = 3,
+  UPB_CTYPE_UINT64   = 4,
+  UPB_CTYPE_BOOL     = 5,
+  UPB_CTYPE_CSTR     = 6,
+  UPB_CTYPE_PTR      = 7,
+  UPB_CTYPE_CONSTPTR = 8,
+  UPB_CTYPE_FPTR     = 9,
+  UPB_CTYPE_FLOAT    = 10,
+  UPB_CTYPE_DOUBLE   = 11
+} upb_ctype_t;
+
+typedef struct {
+  uint64_t val;
+} upb_value;
+
+/* Like strdup(), which isn't always available since it's not ANSI C. */
+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, 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) {
+  v->val = val;
+}
+
+UPB_INLINE upb_value _upb_value_val(uint64_t val) {
+  upb_value ret;
+  _upb_value_setval(&ret, val);
+  return ret;
+}
+
+/* For each value ctype, define the following set of functions:
+ *
+ * // Get/set an int32 from a upb_value.
+ * int32_t upb_value_getint32(upb_value val);
+ * void upb_value_setint32(upb_value *val, int32_t cval);
+ *
+ * // Construct a new upb_value from an int32.
+ * upb_value upb_value_int32(int32_t val); */
+#define FUNCS(name, membername, type_t, converter, proto_type) \
+  UPB_INLINE void upb_value_set ## name(upb_value *val, type_t cval) { \
+    val->val = (converter)cval; \
+  } \
+  UPB_INLINE upb_value upb_value_ ## name(type_t val) { \
+    upb_value ret; \
+    upb_value_set ## name(&ret, val); \
+    return ret; \
+  } \
+  UPB_INLINE type_t upb_value_get ## name(upb_value val) { \
+    return (type_t)(converter)val.val; \
+  }
+
+FUNCS(int32,    int32,        int32_t,      int32_t,    UPB_CTYPE_INT32)
+FUNCS(int64,    int64,        int64_t,      int64_t,    UPB_CTYPE_INT64)
+FUNCS(uint32,   uint32,       uint32_t,     uint32_t,   UPB_CTYPE_UINT32)
+FUNCS(uint64,   uint64,       uint64_t,     uint64_t,   UPB_CTYPE_UINT64)
+FUNCS(bool,     _bool,        bool,         bool,       UPB_CTYPE_BOOL)
+FUNCS(cstr,     cstr,         char*,        uintptr_t,  UPB_CTYPE_CSTR)
+FUNCS(ptr,      ptr,          void*,        uintptr_t,  UPB_CTYPE_PTR)
+FUNCS(constptr, constptr,     const void*,  uintptr_t,  UPB_CTYPE_CONSTPTR)
+FUNCS(fptr,     fptr,         upb_func*,    uintptr_t,  UPB_CTYPE_FPTR)
+
+#undef FUNCS
+
+UPB_INLINE void upb_value_setfloat(upb_value *val, float cval) {
+  memcpy(&val->val, &cval, sizeof(cval));
+}
+
+UPB_INLINE void upb_value_setdouble(upb_value *val, double cval) {
+  memcpy(&val->val, &cval, sizeof(cval));
+}
+
+UPB_INLINE upb_value upb_value_float(float cval) {
+  upb_value ret;
+  upb_value_setfloat(&ret, cval);
+  return ret;
+}
+
+UPB_INLINE upb_value upb_value_double(double cval) {
+  upb_value ret;
+  upb_value_setdouble(&ret, cval);
+  return ret;
+}
+
+#undef SET_TYPE
+
+
+/* upb_tabkey *****************************************************************/
+
+/* Either:
+ *   1. an actual integer key, or
+ *   2. a pointer to a string prefixed by its uint32_t length, owned by us.
+ *
+ * ...depending on whether this is a string table or an int table.  We would
+ * make this a union of those two types, but C89 doesn't support statically
+ * initializing a non-first union member. */
+typedef uintptr_t upb_tabkey;
+
+UPB_INLINE char *upb_tabstr(upb_tabkey key, uint32_t *len) {
+  char* mem = (char*)key;
+  if (len) memcpy(len, mem, sizeof(*len));
+  return mem + sizeof(*len);
+}
+
+
+/* upb_tabval *****************************************************************/
+
+typedef struct {
+  uint64_t val;
+} upb_tabval;
+
+#define UPB_TABVALUE_EMPTY_INIT  {-1}
+
+/* upb_table ******************************************************************/
+
+typedef struct _upb_tabent {
+  upb_tabkey key;
+  upb_tabval val;
+
+  /* Internal chaining.  This is const so we can create static initializers for
+   * tables.  We cast away const sometimes, but *only* when the containing
+   * upb_table is known to be non-const.  This requires a bit of care, but
+   * the subtlety is confined to table.c. */
+  const struct _upb_tabent *next;
+} upb_tabent;
+
+typedef struct {
+  size_t count;          /* Number of entries in the hash part. */
+  size_t mask;           /* Mask to turn hash value -> bucket. */
+  uint8_t size_lg2;      /* Size of the hashtable part is 2^size_lg2 entries. */
+
+  /* Hash table entries.
+   * Making this const isn't entirely accurate; what we really want is for it to
+   * have the same const-ness as the table it's inside.  But there's no way to
+   * declare that in C.  So we have to make it const so that we can statically
+   * initialize const hash tables.  Then we cast away const when we have to.
+   */
+  const upb_tabent *entries;
+} upb_table;
+
+typedef struct {
+  upb_table t;
+} upb_strtable;
+
+typedef struct {
+  upb_table t;              /* For entries that don't fit in the array part. */
+  const upb_tabval *array;  /* Array part of the table. See const note above. */
+  size_t array_size;        /* Array part size. */
+  size_t array_count;       /* Array part number of elements. */
+} upb_inttable;
+
+#define UPB_ARRAY_EMPTYENT -1
+
+UPB_INLINE size_t upb_table_size(const upb_table *t) {
+  if (t->size_lg2 == 0)
+    return 0;
+  else
+    return 1 << t->size_lg2;
+}
+
+/* Internal-only functions, in .h file only out of necessity. */
+UPB_INLINE bool upb_tabent_isempty(const upb_tabent *e) {
+  return e->key == 0;
+}
+
+/* Used by some of the unit tests for generic hashing functionality. */
+uint32_t upb_murmur_hash2(const void * key, size_t len, uint32_t seed);
+
+UPB_INLINE uintptr_t upb_intkey(uintptr_t key) {
+  return key;
+}
+
+UPB_INLINE uint32_t upb_inthash(uintptr_t key) {
+  return (uint32_t)key;
+}
+
+static const upb_tabent *upb_getentry(const upb_table *t, uint32_t hash) {
+  return t->entries + (hash & t->mask);
+}
+
+UPB_INLINE bool upb_arrhas(upb_tabval key) {
+  return key.val != (uint64_t)-1;
+}
+
+/* Initialize and uninitialize a table, respectively.  If memory allocation
+ * failed, false is returned that the table is uninitialized. */
+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);
+UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) {
+  return t->t.count;
+}
+
+void upb_inttable_packedsize(const upb_inttable *t, size_t *size);
+void upb_strtable_packedsize(const upb_strtable *t, size_t *size);
+upb_inttable *upb_inttable_pack(const upb_inttable *t, void *p, size_t *ofs,
+                                size_t size);
+upb_strtable *upb_strtable_pack(const upb_strtable *t, void *p, size_t *ofs,
+                                size_t size);
+void upb_strtable_clear(upb_strtable *t);
+
+/* Inserts the given key into the hashtable with the given value.  The key must
+ * not already exist in the hash table.  For string tables, the key must be
+ * NULL-terminated, and the table will make an internal copy of the key.
+ * Inttables must not insert a value of UINTPTR_MAX.
+ *
+ * If a table resize was required but memory allocation failed, false is
+ * returned and the table is unchanged. */
+bool upb_inttable_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,
+                                    upb_value val) {
+  return upb_strtable_insert2(t, key, strlen(key), val);
+}
+
+/* Looks up key in this table, returning "true" if the key was found.
+ * If v is non-NULL, copies the value for this key into *v. */
+bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v);
+bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len,
+                          upb_value *v);
+
+/* For NULL-terminated strings. */
+UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key,
+                                    upb_value *v) {
+  return upb_strtable_lookup2(t, key, strlen(key), v);
+}
+
+/* Removes an item from the table.  Returns true if the remove was successful,
+ * and stores the removed item in *val if non-NULL. */
+bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val);
+bool upb_strtable_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,
+                                    upb_value *v) {
+  return upb_strtable_remove2(t, key, strlen(key), v);
+}
+
+/* Updates an existing entry in an inttable.  If the entry does not exist,
+ * returns false and does nothing.  Unlike insert/remove, this does not
+ * invalidate iterators. */
+bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val);
+
+/* Handy routines for treating an inttable like a stack.  May not be mixed with
+ * other insert/remove calls. */
+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_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_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. */
+UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key,
+                                      upb_value *v) {
+  *v = upb_value_int32(0);  /* Silence compiler warnings. */
+  if (key < t->array_size) {
+    upb_tabval arrval = t->array[key];
+    if (upb_arrhas(arrval)) {
+      _upb_value_setval(v, arrval.val);
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    const upb_tabent *e;
+    if (t->t.entries == NULL) return false;
+    for (e = upb_getentry(&t->t, upb_inthash(key)); true; e = e->next) {
+      if ((uint32_t)e->key == key) {
+        _upb_value_setval(v, e->val.val);
+        return true;
+      }
+      if (e->next == NULL) return false;
+    }
+  }
+}
+
+/* Exposed for testing only. */
+bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a);
+
+/* Iterators ******************************************************************/
+
+/* Iterators for int and string tables.  We are subject to some kind of unusual
+ * design constraints:
+ *
+ * For high-level languages:
+ *  - we must be able to guarantee that we don't crash or corrupt memory even if
+ *    the program accesses an invalidated iterator.
+ *
+ * For C++11 range-based for:
+ *  - iterators must be copyable
+ *  - iterators must be comparable
+ *  - it must be possible to construct an "end" value.
+ *
+ * Iteration order is undefined.
+ *
+ * Modifying the table invalidates iterators.  upb_{str,int}table_done() is
+ * guaranteed to work even on an invalidated iterator, as long as the table it
+ * is iterating over has not been freed.  Calling next() or accessing data from
+ * an invalidated iterator yields unspecified elements from the table, but it is
+ * guaranteed not to crash and to return real table elements (except when done()
+ * is true). */
+
+
+/* upb_strtable_iter **********************************************************/
+
+/*   upb_strtable_iter i;
+ *   upb_strtable_begin(&i, t);
+ *   for(; !upb_strtable_done(&i); upb_strtable_next(&i)) {
+ *     const char *key = upb_strtable_iter_key(&i);
+ *     const upb_value val = upb_strtable_iter_value(&i);
+ *     // ...
+ *   }
+ */
+
+typedef struct {
+  const upb_strtable *t;
+  size_t index;
+} upb_strtable_iter;
+
+void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t);
+void upb_strtable_next(upb_strtable_iter *i);
+bool upb_strtable_done(const upb_strtable_iter *i);
+upb_strview upb_strtable_iter_key(const upb_strtable_iter *i);
+upb_value upb_strtable_iter_value(const upb_strtable_iter *i);
+void upb_strtable_iter_setdone(upb_strtable_iter *i);
+bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,
+                               const upb_strtable_iter *i2);
+
+
+/* upb_inttable_iter **********************************************************/
+
+/*   upb_inttable_iter i;
+ *   upb_inttable_begin(&i, t);
+ *   for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+ *     uintptr_t key = upb_inttable_iter_key(&i);
+ *     upb_value val = upb_inttable_iter_value(&i);
+ *     // ...
+ *   }
+ */
+
+typedef struct {
+  const upb_inttable *t;
+  size_t index;
+  bool array_part;
+} upb_inttable_iter;
+
+UPB_INLINE const upb_tabent *str_tabent(const upb_strtable_iter *i) {
+  return &i->t->t.entries[i->index];
+}
+
+void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t);
+void upb_inttable_next(upb_inttable_iter *i);
+bool upb_inttable_done(const upb_inttable_iter *i);
+uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i);
+upb_value upb_inttable_iter_value(const upb_inttable_iter *i);
+void upb_inttable_iter_setdone(upb_inttable_iter *i);
+bool upb_inttable_iter_isequal(const upb_inttable_iter *i1,
+                               const upb_inttable_iter *i2);
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+
+#endif  /* UPB_TABLE_H_ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PTR_AT(msg, ofs, type) (type*)((const char*)msg + ofs)
+
+typedef void upb_msg;
+
+/** upb_msglayout *************************************************************/
+
+/* upb_msglayout represents the memory layout of a given upb_msgdef.  The
+ * members are public so generated code can initialize them, but users MUST NOT
+ * read or write any of its members. */
+
+/* These aren't real labels according to descriptor.proto, but in the table we
+ * use these for map/packed fields instead of UPB_LABEL_REPEATED. */
+enum {
+  _UPB_LABEL_MAP = 4,
+  _UPB_LABEL_PACKED = 7  /* Low 3 bits are common with UPB_LABEL_REPEATED. */
+};
+
+typedef struct {
+  uint32_t number;
+  uint16_t offset;
+  int16_t presence;       /* If >0, hasbit_index.  If <0, ~oneof_index. */
+  uint16_t submsg_index;  /* undefined if descriptortype != MESSAGE or GROUP. */
+  uint8_t descriptortype;
+  uint8_t label;          /* google.protobuf.Label or _UPB_LABEL_* above. */
+} upb_msglayout_field;
+
+typedef struct upb_msglayout {
+  const struct upb_msglayout *const* submsgs;
+  const upb_msglayout_field *fields;
+  /* Must be aligned to sizeof(void*).  Doesn't include internal members like
+   * unknown fields, extension dict, pointer to msglayout, etc. */
+  uint16_t size;
+  uint16_t field_count;
+  bool extendable;
+} upb_msglayout;
+
+/** upb_msg *******************************************************************/
+
+/* Internal members of a upb_msg.  We can change this without breaking binary
+ * compatibility.  We put these before the user's data.  The user's upb_msg*
+ * points after the upb_msg_internal. */
+
+/* Used when a message is not extendable. */
+typedef struct {
+  char *unknown;
+  size_t unknown_len;
+  size_t unknown_size;
+} upb_msg_internal;
+
+/* Used when a message is extendable. */
+typedef struct {
+  upb_inttable *extdict;
+  upb_msg_internal base;
+} upb_msg_internal_withext;
+
+/* Maps upb_fieldtype_t -> memory size. */
+extern char _upb_fieldtype_to_size[12];
+
+/* Creates a new messages with the given layout on the given arena. */
+upb_msg *_upb_msg_new(const upb_msglayout *l, upb_arena *a);
+
+/* Clears the given message. */
+void _upb_msg_clear(upb_msg *msg, const upb_msglayout *l);
+
+/* Discards the unknown fields for this message only. */
+void _upb_msg_discardunknown_shallow(upb_msg *msg);
+
+/* Adds unknown data (serialized protobuf data) to the given message.  The data
+ * is copied into the message instance. */
+bool _upb_msg_addunknown(upb_msg *msg, const char *data, size_t len,
+                         upb_arena *arena);
+
+/* Returns a reference to the message's unknown data. */
+const char *upb_msg_getunknown(const upb_msg *msg, size_t *len);
+
+/** Hasbit access *************************************************************/
+
+UPB_INLINE bool _upb_hasbit(const upb_msg *msg, size_t idx) {
+  return (*PTR_AT(msg, idx / 8, const char) & (1 << (idx % 8))) != 0;
+}
+
+UPB_INLINE void _upb_sethas(const upb_msg *msg, size_t idx) {
+  (*PTR_AT(msg, idx / 8, char)) |= (char)(1 << (idx % 8));
+}
+
+UPB_INLINE void _upb_clearhas(const upb_msg *msg, size_t idx) {
+  (*PTR_AT(msg, idx / 8, char)) &= (char)(~(1 << (idx % 8)));
+}
+
+UPB_INLINE size_t _upb_msg_hasidx(const upb_msglayout_field *f) {
+  UPB_ASSERT(f->presence > 0);
+  return f->presence;
+}
+
+UPB_INLINE bool _upb_hasbit_field(const upb_msg *msg,
+                                  const upb_msglayout_field *f) {
+  return _upb_hasbit(msg, _upb_msg_hasidx(f));
+}
+
+UPB_INLINE void _upb_sethas_field(const upb_msg *msg,
+                                  const upb_msglayout_field *f) {
+  _upb_sethas(msg, _upb_msg_hasidx(f));
+}
+
+UPB_INLINE void _upb_clearhas_field(const upb_msg *msg,
+                                    const upb_msglayout_field *f) {
+  _upb_clearhas(msg, _upb_msg_hasidx(f));
+}
+
+/** Oneof case access *********************************************************/
+
+UPB_INLINE uint32_t *_upb_oneofcase(upb_msg *msg, size_t case_ofs) {
+  return PTR_AT(msg, case_ofs, uint32_t);
+}
+
+UPB_INLINE uint32_t _upb_getoneofcase(const void *msg, size_t case_ofs) {
+  return *PTR_AT(msg, case_ofs, uint32_t);
+}
+
+UPB_INLINE size_t _upb_oneofcase_ofs(const upb_msglayout_field *f) {
+  UPB_ASSERT(f->presence < 0);
+  return ~(int64_t)f->presence;
+}
+
+UPB_INLINE uint32_t *_upb_oneofcase_field(upb_msg *msg,
+                                          const upb_msglayout_field *f) {
+  return _upb_oneofcase(msg, _upb_oneofcase_ofs(f));
+}
+
+UPB_INLINE uint32_t _upb_getoneofcase_field(const upb_msg *msg,
+                                            const upb_msglayout_field *f) {
+  return _upb_getoneofcase(msg, _upb_oneofcase_ofs(f));
+}
+
+UPB_INLINE bool _upb_has_submsg_nohasbit(const upb_msg *msg, size_t ofs) {
+  return *PTR_AT(msg, ofs, const upb_msg*) != NULL;
+}
+
+UPB_INLINE bool _upb_isrepeated(const upb_msglayout_field *field) {
+  return (field->label & 3) == UPB_LABEL_REPEATED;
+}
+
+UPB_INLINE bool _upb_repeated_or_map(const upb_msglayout_field *field) {
+  return field->label >= UPB_LABEL_REPEATED;
+}
+
+/** upb_array *****************************************************************/
+
+/* Our internal representation for repeated fields.  */
+typedef struct {
+  uintptr_t data;   /* Tagged ptr: low 3 bits of ptr are lg2(elem size). */
+  size_t len;   /* Measured in elements. */
+  size_t size;  /* Measured in elements. */
+} upb_array;
+
+UPB_INLINE const void *_upb_array_constptr(const upb_array *arr) {
+  return (void*)(arr->data & ~(uintptr_t)7);
+}
+
+UPB_INLINE void *_upb_array_ptr(upb_array *arr) {
+  return (void*)_upb_array_constptr(arr);
+}
+
+/* Creates a new array on the given arena. */
+upb_array *_upb_array_new(upb_arena *a, upb_fieldtype_t type);
+
+/* Resizes the capacity of the array to be at least min_size. */
+bool _upb_array_realloc(upb_array *arr, size_t min_size, upb_arena *arena);
+
+/* Fallback functions for when the accessors require a resize. */
+void *_upb_array_resize_fallback(upb_array **arr_ptr, size_t size,
+                                 upb_fieldtype_t type, upb_arena *arena);
+bool _upb_array_append_fallback(upb_array **arr_ptr, const void *value,
+                                upb_fieldtype_t type, upb_arena *arena);
+
+UPB_INLINE bool _upb_array_reserve(upb_array *arr, size_t size,
+                                   upb_arena *arena) {
+  if (arr->size < size) return _upb_array_realloc(arr, size, arena);
+  return true;
+}
+
+UPB_INLINE bool _upb_array_resize(upb_array *arr, size_t size,
+                                  upb_arena *arena) {
+  if (!_upb_array_reserve(arr, size, arena)) return false;
+  arr->len = size;
+  return true;
+}
+
+UPB_INLINE const void *_upb_array_accessor(const void *msg, size_t ofs,
+                                           size_t *size) {
+  const upb_array *arr = *PTR_AT(msg, ofs, const upb_array*);
+  if (arr) {
+    if (size) *size = arr->len;
+    return _upb_array_constptr(arr);
+  } else {
+    if (size) *size = 0;
+    return NULL;
+  }
+}
+
+UPB_INLINE void *_upb_array_mutable_accessor(void *msg, size_t ofs,
+                                             size_t *size) {
+  upb_array *arr = *PTR_AT(msg, ofs, upb_array*);
+  if (arr) {
+    if (size) *size = arr->len;
+    return _upb_array_ptr(arr);
+  } else {
+    if (size) *size = 0;
+    return NULL;
+  }
+}
+
+UPB_INLINE void *_upb_array_resize_accessor(void *msg, size_t ofs, size_t size,
+                                            upb_fieldtype_t type,
+                                            upb_arena *arena) {
+  upb_array **arr_ptr = PTR_AT(msg, ofs, upb_array*);
+  upb_array *arr = *arr_ptr;
+  if (!arr || arr->size < size) {
+    return _upb_array_resize_fallback(arr_ptr, size, type, arena);
+  }
+  arr->len = size;
+  return _upb_array_ptr(arr);
+}
+
+
+UPB_INLINE bool _upb_array_append_accessor(void *msg, size_t ofs,
+                                           size_t elem_size,
+                                           upb_fieldtype_t type,
+                                           const void *value,
+                                           upb_arena *arena) {
+  upb_array **arr_ptr = PTR_AT(msg, ofs, upb_array*);
+  upb_array *arr = *arr_ptr;
+  void* ptr;
+  if (!arr || arr->len == arr->size) {
+    return _upb_array_append_fallback(arr_ptr, value, type, arena);
+  }
+  ptr = _upb_array_ptr(arr);
+  memcpy(PTR_AT(ptr, arr->len * elem_size, char), value, elem_size);
+  arr->len++;
+  return true;
+}
+
+/** upb_map *******************************************************************/
+
+/* Right now we use strmaps for everything.  We'll likely want to use
+ * integer-specific maps for integer-keyed maps.*/
+typedef struct {
+  /* Size of key and val, based on the map type.  Strings are represented as '0'
+   * because they must be handled specially. */
+  char key_size;
+  char val_size;
+
+  upb_strtable table;
+} upb_map;
+
+/* Map entries aren't actually stored, they are only used during parsing.  For
+ * parsing, it helps a lot if all map entry messages have the same layout.
+ * The compiler and def.c must ensure that all map entries have this layout. */
+typedef struct {
+  upb_msg_internal internal;
+  union {
+    upb_strview str;  /* For str/bytes. */
+    upb_value val;    /* For all other types. */
+  } k;
+  union {
+    upb_strview str;  /* For str/bytes. */
+    upb_value val;    /* For all other types. */
+  } v;
+} upb_map_entry;
+
+/* Creates a new map on the given arena with this key/value type. */
+upb_map *_upb_map_new(upb_arena *a, size_t key_size, size_t value_size);
+
+/* Converting between internal table representation and user values.
+ *
+ * _upb_map_tokey() and _upb_map_fromkey() are inverses.
+ * _upb_map_tovalue() and _upb_map_fromvalue() are inverses.
+ *
+ * These functions account for the fact that strings are treated differently
+ * from other types when stored in a map.
+ */
+
+UPB_INLINE upb_strview _upb_map_tokey(const void *key, size_t size) {
+  if (size == UPB_MAPTYPE_STRING) {
+    return *(upb_strview*)key;
+  } else {
+    return upb_strview_make((const char*)key, size);
+  }
+}
+
+UPB_INLINE void _upb_map_fromkey(upb_strview key, void* out, size_t size) {
+  if (size == UPB_MAPTYPE_STRING) {
+    memcpy(out, &key, sizeof(key));
+  } else {
+    memcpy(out, key.data, size);
+  }
+}
+
+UPB_INLINE upb_value _upb_map_tovalue(const void *val, size_t size,
+                                      upb_arena *a) {
+  upb_value ret = {0};
+  if (size == UPB_MAPTYPE_STRING) {
+    upb_strview *strp = (upb_strview*)upb_arena_malloc(a, sizeof(*strp));
+    *strp = *(upb_strview*)val;
+    memcpy(&ret, &strp, sizeof(strp));
+  } else {
+    memcpy(&ret, val, size);
+  }
+  return ret;
+}
+
+UPB_INLINE void _upb_map_fromvalue(upb_value val, void* out, size_t size) {
+  if (size == UPB_MAPTYPE_STRING) {
+    const upb_strview *strp = (const upb_strview*)upb_value_getptr(val);
+    memcpy(out, strp, sizeof(upb_strview));
+  } else {
+    memcpy(out, &val, size);
+  }
+}
+
+/* Map operations, shared by reflection and generated code. */
+
+UPB_INLINE size_t _upb_map_size(const upb_map *map) {
+  return map->table.t.count;
+}
+
+UPB_INLINE bool _upb_map_get(const upb_map *map, const void *key,
+                             size_t key_size, void *val, size_t val_size) {
+  upb_value tabval;
+  upb_strview k = _upb_map_tokey(key, key_size);
+  bool ret = upb_strtable_lookup2(&map->table, k.data, k.size, &tabval);
+  if (ret && val) {
+    _upb_map_fromvalue(tabval, val, val_size);
+  }
+  return ret;
+}
+
+UPB_INLINE void* _upb_map_next(const upb_map *map, size_t *iter) {
+  upb_strtable_iter it;
+  it.t = &map->table;
+  it.index = *iter;
+  upb_strtable_next(&it);
+  *iter = it.index;
+  if (upb_strtable_done(&it)) return NULL;
+  return (void*)str_tabent(&it);
+}
+
+UPB_INLINE bool _upb_map_set(upb_map *map, const void *key, size_t key_size,
+                             void *val, size_t val_size, upb_arena *arena) {
+  upb_strview strkey = _upb_map_tokey(key, key_size);
+  upb_value tabval = _upb_map_tovalue(val, val_size, arena);
+  upb_alloc *a = upb_arena_alloc(arena);
+
+  /* TODO(haberman): add overwrite operation to minimize number of lookups. */
+  upb_strtable_remove3(&map->table, strkey.data, strkey.size, NULL, a);
+  return upb_strtable_insert3(&map->table, strkey.data, strkey.size, tabval, a);
+}
+
+UPB_INLINE bool _upb_map_delete(upb_map *map, const void *key, size_t key_size) {
+  upb_strview k = _upb_map_tokey(key, key_size);
+  return upb_strtable_remove3(&map->table, k.data, k.size, NULL, NULL);
+}
+
+UPB_INLINE void _upb_map_clear(upb_map *map) {
+  upb_strtable_clear(&map->table);
+}
+
+/* Message map operations, these get the map from the message first. */
+
+UPB_INLINE size_t _upb_msg_map_size(const upb_msg *msg, size_t ofs) {
+  upb_map *map = *UPB_PTR_AT(msg, ofs, upb_map *);
+  return map ? _upb_map_size(map) : 0;
+}
+
+UPB_INLINE bool _upb_msg_map_get(const upb_msg *msg, size_t ofs,
+                                 const void *key, size_t key_size, void *val,
+                                 size_t val_size) {
+  upb_map *map = *UPB_PTR_AT(msg, ofs, upb_map *);
+  if (!map) return false;
+  return _upb_map_get(map, key, key_size, val, val_size);
+}
+
+UPB_INLINE void *_upb_msg_map_next(const upb_msg *msg, size_t ofs,
+                                   size_t *iter) {
+  upb_map *map = *UPB_PTR_AT(msg, ofs, upb_map *);
+  if (!map) return NULL;
+  return _upb_map_next(map, iter);
+}
+
+UPB_INLINE bool _upb_msg_map_set(upb_msg *msg, size_t ofs, const void *key,
+                                 size_t key_size, void *val, size_t val_size,
+                                 upb_arena *arena) {
+  upb_map **map = PTR_AT(msg, ofs, upb_map *);
+  if (!*map) {
+    *map = _upb_map_new(arena, key_size, val_size);
+  }
+  return _upb_map_set(*map, key, key_size, val, val_size, arena);
+}
+
+UPB_INLINE bool _upb_msg_map_delete(upb_msg *msg, size_t ofs, const void *key,
+                                    size_t key_size) {
+  upb_map *map = *UPB_PTR_AT(msg, ofs, upb_map *);
+  if (!map) return false;
+  return _upb_map_delete(map, key, key_size);
+}
+
+UPB_INLINE void _upb_msg_map_clear(upb_msg *msg, size_t ofs) {
+  upb_map *map = *UPB_PTR_AT(msg, ofs, upb_map *);
+  if (!map) return;
+  _upb_map_clear(map);
+}
+
+/* Accessing map key/value from a pointer, used by generated code only. */
+
+UPB_INLINE void _upb_msg_map_key(const void* msg, void* key, size_t size) {
+  const upb_tabent *ent = (const upb_tabent*)msg;
+  uint32_t u32len;
+  upb_strview k;
+  k.data = upb_tabstr(ent->key, &u32len);
+  k.size = u32len;
+  _upb_map_fromkey(k, key, size);
+}
+
+UPB_INLINE void _upb_msg_map_value(const void* msg, void* val, size_t size) {
+  const upb_tabent *ent = (const upb_tabent*)msg;
+  upb_value v;
+  _upb_value_setval(&v, ent->val.val);
+  _upb_map_fromvalue(v, val, size);
+}
+
+UPB_INLINE void _upb_msg_map_set_value(void* msg, const void* val, size_t size) {
+  upb_tabent *ent = (upb_tabent*)msg;
+  /* This is like _upb_map_tovalue() except the entry already exists so we can
+   * reuse the allocated upb_strview for string fields. */
+  if (size == UPB_MAPTYPE_STRING) {
+    upb_strview *strp = (upb_strview*)ent->val.val;
+    memcpy(strp, val, sizeof(*strp));
+  } else {
+    memcpy(&ent->val.val, val, size);
+  }
+}
+
+#undef PTR_AT
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+
+#endif /* UPB_MSG_H_ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool upb_decode(const char *buf, size_t size, upb_msg *msg,
+                const upb_msglayout *l, upb_arena *arena);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* UPB_DECODE_H_ */
+/*
+** upb_encode: parsing into a upb_msg using a upb_msglayout.
+*/
+
+#ifndef UPB_ENCODE_H_
+#define UPB_ENCODE_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *upb_encode(const void *msg, const upb_msglayout *l, upb_arena *arena,
+                 size_t *size);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* UPB_ENCODE_H_ */
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     google/protobuf/descriptor.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H_
+#define GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H_
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct google_protobuf_FileDescriptorSet;
+struct google_protobuf_FileDescriptorProto;
+struct google_protobuf_DescriptorProto;
+struct google_protobuf_DescriptorProto_ExtensionRange;
+struct google_protobuf_DescriptorProto_ReservedRange;
+struct google_protobuf_ExtensionRangeOptions;
+struct google_protobuf_FieldDescriptorProto;
+struct google_protobuf_OneofDescriptorProto;
+struct google_protobuf_EnumDescriptorProto;
+struct google_protobuf_EnumDescriptorProto_EnumReservedRange;
+struct google_protobuf_EnumValueDescriptorProto;
+struct google_protobuf_ServiceDescriptorProto;
+struct google_protobuf_MethodDescriptorProto;
+struct google_protobuf_FileOptions;
+struct google_protobuf_MessageOptions;
+struct google_protobuf_FieldOptions;
+struct google_protobuf_OneofOptions;
+struct google_protobuf_EnumOptions;
+struct google_protobuf_EnumValueOptions;
+struct google_protobuf_ServiceOptions;
+struct google_protobuf_MethodOptions;
+struct google_protobuf_UninterpretedOption;
+struct google_protobuf_UninterpretedOption_NamePart;
+struct google_protobuf_SourceCodeInfo;
+struct google_protobuf_SourceCodeInfo_Location;
+struct google_protobuf_GeneratedCodeInfo;
+struct google_protobuf_GeneratedCodeInfo_Annotation;
+typedef struct google_protobuf_FileDescriptorSet google_protobuf_FileDescriptorSet;
+typedef struct google_protobuf_FileDescriptorProto google_protobuf_FileDescriptorProto;
+typedef struct google_protobuf_DescriptorProto google_protobuf_DescriptorProto;
+typedef struct google_protobuf_DescriptorProto_ExtensionRange google_protobuf_DescriptorProto_ExtensionRange;
+typedef struct google_protobuf_DescriptorProto_ReservedRange google_protobuf_DescriptorProto_ReservedRange;
+typedef struct google_protobuf_ExtensionRangeOptions google_protobuf_ExtensionRangeOptions;
+typedef struct google_protobuf_FieldDescriptorProto google_protobuf_FieldDescriptorProto;
+typedef struct google_protobuf_OneofDescriptorProto google_protobuf_OneofDescriptorProto;
+typedef struct google_protobuf_EnumDescriptorProto google_protobuf_EnumDescriptorProto;
+typedef struct google_protobuf_EnumDescriptorProto_EnumReservedRange google_protobuf_EnumDescriptorProto_EnumReservedRange;
+typedef struct google_protobuf_EnumValueDescriptorProto google_protobuf_EnumValueDescriptorProto;
+typedef struct google_protobuf_ServiceDescriptorProto google_protobuf_ServiceDescriptorProto;
+typedef struct google_protobuf_MethodDescriptorProto google_protobuf_MethodDescriptorProto;
+typedef struct google_protobuf_FileOptions google_protobuf_FileOptions;
+typedef struct google_protobuf_MessageOptions google_protobuf_MessageOptions;
+typedef struct google_protobuf_FieldOptions google_protobuf_FieldOptions;
+typedef struct google_protobuf_OneofOptions google_protobuf_OneofOptions;
+typedef struct google_protobuf_EnumOptions google_protobuf_EnumOptions;
+typedef struct google_protobuf_EnumValueOptions google_protobuf_EnumValueOptions;
+typedef struct google_protobuf_ServiceOptions google_protobuf_ServiceOptions;
+typedef struct google_protobuf_MethodOptions google_protobuf_MethodOptions;
+typedef struct google_protobuf_UninterpretedOption google_protobuf_UninterpretedOption;
+typedef struct google_protobuf_UninterpretedOption_NamePart google_protobuf_UninterpretedOption_NamePart;
+typedef struct google_protobuf_SourceCodeInfo google_protobuf_SourceCodeInfo;
+typedef struct google_protobuf_SourceCodeInfo_Location google_protobuf_SourceCodeInfo_Location;
+typedef struct google_protobuf_GeneratedCodeInfo google_protobuf_GeneratedCodeInfo;
+typedef struct google_protobuf_GeneratedCodeInfo_Annotation google_protobuf_GeneratedCodeInfo_Annotation;
+extern const upb_msglayout google_protobuf_FileDescriptorSet_msginit;
+extern const upb_msglayout google_protobuf_FileDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_DescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_DescriptorProto_ExtensionRange_msginit;
+extern const upb_msglayout google_protobuf_DescriptorProto_ReservedRange_msginit;
+extern const upb_msglayout google_protobuf_ExtensionRangeOptions_msginit;
+extern const upb_msglayout google_protobuf_FieldDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_OneofDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_EnumDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit;
+extern const upb_msglayout google_protobuf_EnumValueDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_ServiceDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_MethodDescriptorProto_msginit;
+extern const upb_msglayout google_protobuf_FileOptions_msginit;
+extern const upb_msglayout google_protobuf_MessageOptions_msginit;
+extern const upb_msglayout google_protobuf_FieldOptions_msginit;
+extern const upb_msglayout google_protobuf_OneofOptions_msginit;
+extern const upb_msglayout google_protobuf_EnumOptions_msginit;
+extern const upb_msglayout google_protobuf_EnumValueOptions_msginit;
+extern const upb_msglayout google_protobuf_ServiceOptions_msginit;
+extern const upb_msglayout google_protobuf_MethodOptions_msginit;
+extern const upb_msglayout google_protobuf_UninterpretedOption_msginit;
+extern const upb_msglayout google_protobuf_UninterpretedOption_NamePart_msginit;
+extern const upb_msglayout google_protobuf_SourceCodeInfo_msginit;
+extern const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit;
+extern const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit;
+extern const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit;
+
+typedef enum {
+  google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1,
+  google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2,
+  google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3
+} google_protobuf_FieldDescriptorProto_Label;
+
+typedef enum {
+  google_protobuf_FieldDescriptorProto_TYPE_DOUBLE = 1,
+  google_protobuf_FieldDescriptorProto_TYPE_FLOAT = 2,
+  google_protobuf_FieldDescriptorProto_TYPE_INT64 = 3,
+  google_protobuf_FieldDescriptorProto_TYPE_UINT64 = 4,
+  google_protobuf_FieldDescriptorProto_TYPE_INT32 = 5,
+  google_protobuf_FieldDescriptorProto_TYPE_FIXED64 = 6,
+  google_protobuf_FieldDescriptorProto_TYPE_FIXED32 = 7,
+  google_protobuf_FieldDescriptorProto_TYPE_BOOL = 8,
+  google_protobuf_FieldDescriptorProto_TYPE_STRING = 9,
+  google_protobuf_FieldDescriptorProto_TYPE_GROUP = 10,
+  google_protobuf_FieldDescriptorProto_TYPE_MESSAGE = 11,
+  google_protobuf_FieldDescriptorProto_TYPE_BYTES = 12,
+  google_protobuf_FieldDescriptorProto_TYPE_UINT32 = 13,
+  google_protobuf_FieldDescriptorProto_TYPE_ENUM = 14,
+  google_protobuf_FieldDescriptorProto_TYPE_SFIXED32 = 15,
+  google_protobuf_FieldDescriptorProto_TYPE_SFIXED64 = 16,
+  google_protobuf_FieldDescriptorProto_TYPE_SINT32 = 17,
+  google_protobuf_FieldDescriptorProto_TYPE_SINT64 = 18
+} google_protobuf_FieldDescriptorProto_Type;
+
+typedef enum {
+  google_protobuf_FieldOptions_STRING = 0,
+  google_protobuf_FieldOptions_CORD = 1,
+  google_protobuf_FieldOptions_STRING_PIECE = 2
+} google_protobuf_FieldOptions_CType;
+
+typedef enum {
+  google_protobuf_FieldOptions_JS_NORMAL = 0,
+  google_protobuf_FieldOptions_JS_STRING = 1,
+  google_protobuf_FieldOptions_JS_NUMBER = 2
+} google_protobuf_FieldOptions_JSType;
+
+typedef enum {
+  google_protobuf_FileOptions_SPEED = 1,
+  google_protobuf_FileOptions_CODE_SIZE = 2,
+  google_protobuf_FileOptions_LITE_RUNTIME = 3
+} google_protobuf_FileOptions_OptimizeMode;
+
+typedef enum {
+  google_protobuf_MethodOptions_IDEMPOTENCY_UNKNOWN = 0,
+  google_protobuf_MethodOptions_NO_SIDE_EFFECTS = 1,
+  google_protobuf_MethodOptions_IDEMPOTENT = 2
+} google_protobuf_MethodOptions_IdempotencyLevel;
+
+
+/* google.protobuf.FileDescriptorSet */
+
+UPB_INLINE google_protobuf_FileDescriptorSet *google_protobuf_FileDescriptorSet_new(upb_arena *arena) {
+  return (google_protobuf_FileDescriptorSet *)_upb_msg_new(&google_protobuf_FileDescriptorSet_msginit, arena);
+}
+UPB_INLINE google_protobuf_FileDescriptorSet *google_protobuf_FileDescriptorSet_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_FileDescriptorSet *ret = google_protobuf_FileDescriptorSet_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_FileDescriptorSet_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_FileDescriptorSet_serialize(const google_protobuf_FileDescriptorSet *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_FileDescriptorSet_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_FileDescriptorSet_has_file(const google_protobuf_FileDescriptorSet *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(0, 0)); }
+UPB_INLINE const google_protobuf_FileDescriptorProto* const* google_protobuf_FileDescriptorSet_file(const google_protobuf_FileDescriptorSet *msg, size_t *len) { return (const google_protobuf_FileDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
+
+UPB_INLINE google_protobuf_FileDescriptorProto** google_protobuf_FileDescriptorSet_mutable_file(google_protobuf_FileDescriptorSet *msg, size_t *len) {
+  return (google_protobuf_FileDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
+}
+UPB_INLINE google_protobuf_FileDescriptorProto** google_protobuf_FileDescriptorSet_resize_file(google_protobuf_FileDescriptorSet *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_FileDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_FileDescriptorProto* google_protobuf_FileDescriptorSet_add_file(google_protobuf_FileDescriptorSet *msg, upb_arena *arena) {
+  struct google_protobuf_FileDescriptorProto* sub = (struct google_protobuf_FileDescriptorProto*)_upb_msg_new(&google_protobuf_FileDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.FileDescriptorProto */
+
+UPB_INLINE google_protobuf_FileDescriptorProto *google_protobuf_FileDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_FileDescriptorProto *)_upb_msg_new(&google_protobuf_FileDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_FileDescriptorProto *google_protobuf_FileDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_FileDescriptorProto *ret = google_protobuf_FileDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_FileDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_FileDescriptorProto_serialize(const google_protobuf_FileDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_FileDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_name(const google_protobuf_FileDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE upb_strview google_protobuf_FileDescriptorProto_name(const google_protobuf_FileDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_package(const google_protobuf_FileDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE upb_strview google_protobuf_FileDescriptorProto_package(const google_protobuf_FileDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), upb_strview); }
+UPB_INLINE upb_strview const* google_protobuf_FileDescriptorProto_dependency(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(36, 72), len); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_message_type(const google_protobuf_FileDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(40, 80)); }
+UPB_INLINE const google_protobuf_DescriptorProto* const* google_protobuf_FileDescriptorProto_message_type(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (const google_protobuf_DescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(40, 80), len); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_enum_type(const google_protobuf_FileDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(44, 88)); }
+UPB_INLINE const google_protobuf_EnumDescriptorProto* const* google_protobuf_FileDescriptorProto_enum_type(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (const google_protobuf_EnumDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(44, 88), len); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_service(const google_protobuf_FileDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(48, 96)); }
+UPB_INLINE const google_protobuf_ServiceDescriptorProto* const* google_protobuf_FileDescriptorProto_service(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (const google_protobuf_ServiceDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(48, 96), len); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_extension(const google_protobuf_FileDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(52, 104)); }
+UPB_INLINE const google_protobuf_FieldDescriptorProto* const* google_protobuf_FileDescriptorProto_extension(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (const google_protobuf_FieldDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(52, 104), len); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_options(const google_protobuf_FileDescriptorProto *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE const google_protobuf_FileOptions* google_protobuf_FileDescriptorProto_options(const google_protobuf_FileDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(28, 56), const google_protobuf_FileOptions*); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_source_code_info(const google_protobuf_FileDescriptorProto *msg) { return _upb_hasbit(msg, 5); }
+UPB_INLINE const google_protobuf_SourceCodeInfo* google_protobuf_FileDescriptorProto_source_code_info(const google_protobuf_FileDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(32, 64), const google_protobuf_SourceCodeInfo*); }
+UPB_INLINE int32_t const* google_protobuf_FileDescriptorProto_public_dependency(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (int32_t const*)_upb_array_accessor(msg, UPB_SIZE(56, 112), len); }
+UPB_INLINE int32_t const* google_protobuf_FileDescriptorProto_weak_dependency(const google_protobuf_FileDescriptorProto *msg, size_t *len) { return (int32_t const*)_upb_array_accessor(msg, UPB_SIZE(60, 120), len); }
+UPB_INLINE bool google_protobuf_FileDescriptorProto_has_syntax(const google_protobuf_FileDescriptorProto *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE upb_strview google_protobuf_FileDescriptorProto_syntax(const google_protobuf_FileDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(20, 40), upb_strview); }
+
+UPB_INLINE void google_protobuf_FileDescriptorProto_set_name(google_protobuf_FileDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileDescriptorProto_set_package(google_protobuf_FileDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), upb_strview) = value;
+}
+UPB_INLINE upb_strview* google_protobuf_FileDescriptorProto_mutable_dependency(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(36, 72), len);
+}
+UPB_INLINE upb_strview* google_protobuf_FileDescriptorProto_resize_dependency(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(36, 72), len, UPB_TYPE_STRING, arena);
+}
+UPB_INLINE bool google_protobuf_FileDescriptorProto_add_dependency(google_protobuf_FileDescriptorProto *msg, upb_strview val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(36, 72), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+      arena);
+}
+UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_FileDescriptorProto_mutable_message_type(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_DescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(40, 80), len);
+}
+UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_FileDescriptorProto_resize_message_type(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_DescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(40, 80), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_DescriptorProto* google_protobuf_FileDescriptorProto_add_message_type(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_DescriptorProto* sub = (struct google_protobuf_DescriptorProto*)_upb_msg_new(&google_protobuf_DescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(40, 80), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto** google_protobuf_FileDescriptorProto_mutable_enum_type(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_EnumDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(44, 88), len);
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto** google_protobuf_FileDescriptorProto_resize_enum_type(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_EnumDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(44, 88), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_EnumDescriptorProto* google_protobuf_FileDescriptorProto_add_enum_type(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_EnumDescriptorProto* sub = (struct google_protobuf_EnumDescriptorProto*)_upb_msg_new(&google_protobuf_EnumDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(44, 88), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_ServiceDescriptorProto** google_protobuf_FileDescriptorProto_mutable_service(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_ServiceDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(48, 96), len);
+}
+UPB_INLINE google_protobuf_ServiceDescriptorProto** google_protobuf_FileDescriptorProto_resize_service(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_ServiceDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(48, 96), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_ServiceDescriptorProto* google_protobuf_FileDescriptorProto_add_service(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_ServiceDescriptorProto* sub = (struct google_protobuf_ServiceDescriptorProto*)_upb_msg_new(&google_protobuf_ServiceDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(48, 96), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_FileDescriptorProto_mutable_extension(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(52, 104), len);
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_FileDescriptorProto_resize_extension(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(52, 104), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_FieldDescriptorProto* google_protobuf_FileDescriptorProto_add_extension(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_FieldDescriptorProto* sub = (struct google_protobuf_FieldDescriptorProto*)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(52, 104), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void google_protobuf_FileDescriptorProto_set_options(google_protobuf_FileDescriptorProto *msg, google_protobuf_FileOptions* value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(28, 56), google_protobuf_FileOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_FileOptions* google_protobuf_FileDescriptorProto_mutable_options(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_FileOptions* sub = (struct google_protobuf_FileOptions*)google_protobuf_FileDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_FileOptions*)_upb_msg_new(&google_protobuf_FileOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_FileDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE void google_protobuf_FileDescriptorProto_set_source_code_info(google_protobuf_FileDescriptorProto *msg, google_protobuf_SourceCodeInfo* value) {
+  _upb_sethas(msg, 5);
+  *UPB_PTR_AT(msg, UPB_SIZE(32, 64), google_protobuf_SourceCodeInfo*) = value;
+}
+UPB_INLINE struct google_protobuf_SourceCodeInfo* google_protobuf_FileDescriptorProto_mutable_source_code_info(google_protobuf_FileDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_SourceCodeInfo* sub = (struct google_protobuf_SourceCodeInfo*)google_protobuf_FileDescriptorProto_source_code_info(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_SourceCodeInfo*)_upb_msg_new(&google_protobuf_SourceCodeInfo_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_FileDescriptorProto_set_source_code_info(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_mutable_public_dependency(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(56, 112), len);
+}
+UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_resize_public_dependency(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(56, 112), len, UPB_TYPE_INT32, arena);
+}
+UPB_INLINE bool google_protobuf_FileDescriptorProto_add_public_dependency(google_protobuf_FileDescriptorProto *msg, int32_t val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(56, 112), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+      arena);
+}
+UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_mutable_weak_dependency(google_protobuf_FileDescriptorProto *msg, size_t *len) {
+  return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(60, 120), len);
+}
+UPB_INLINE int32_t* google_protobuf_FileDescriptorProto_resize_weak_dependency(google_protobuf_FileDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(60, 120), len, UPB_TYPE_INT32, arena);
+}
+UPB_INLINE bool google_protobuf_FileDescriptorProto_add_weak_dependency(google_protobuf_FileDescriptorProto *msg, int32_t val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(60, 120), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+      arena);
+}
+UPB_INLINE void google_protobuf_FileDescriptorProto_set_syntax(google_protobuf_FileDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(20, 40), upb_strview) = value;
+}
+
+/* google.protobuf.DescriptorProto */
+
+UPB_INLINE google_protobuf_DescriptorProto *google_protobuf_DescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_DescriptorProto *)_upb_msg_new(&google_protobuf_DescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_DescriptorProto *google_protobuf_DescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_DescriptorProto *ret = google_protobuf_DescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_DescriptorProto_serialize(const google_protobuf_DescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_DescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_DescriptorProto_has_name(const google_protobuf_DescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE upb_strview google_protobuf_DescriptorProto_name(const google_protobuf_DescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_field(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(16, 32)); }
+UPB_INLINE const google_protobuf_FieldDescriptorProto* const* google_protobuf_DescriptorProto_field(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_FieldDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(16, 32), len); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_nested_type(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(20, 40)); }
+UPB_INLINE const google_protobuf_DescriptorProto* const* google_protobuf_DescriptorProto_nested_type(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_DescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(20, 40), len); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_enum_type(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(24, 48)); }
+UPB_INLINE const google_protobuf_EnumDescriptorProto* const* google_protobuf_DescriptorProto_enum_type(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_EnumDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(24, 48), len); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_extension_range(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(28, 56)); }
+UPB_INLINE const google_protobuf_DescriptorProto_ExtensionRange* const* google_protobuf_DescriptorProto_extension_range(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_DescriptorProto_ExtensionRange* const*)_upb_array_accessor(msg, UPB_SIZE(28, 56), len); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_extension(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(32, 64)); }
+UPB_INLINE const google_protobuf_FieldDescriptorProto* const* google_protobuf_DescriptorProto_extension(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_FieldDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(32, 64), len); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_options(const google_protobuf_DescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE const google_protobuf_MessageOptions* google_protobuf_DescriptorProto_options(const google_protobuf_DescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), const google_protobuf_MessageOptions*); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_oneof_decl(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(36, 72)); }
+UPB_INLINE const google_protobuf_OneofDescriptorProto* const* google_protobuf_DescriptorProto_oneof_decl(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_OneofDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(36, 72), len); }
+UPB_INLINE bool google_protobuf_DescriptorProto_has_reserved_range(const google_protobuf_DescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(40, 80)); }
+UPB_INLINE const google_protobuf_DescriptorProto_ReservedRange* const* google_protobuf_DescriptorProto_reserved_range(const google_protobuf_DescriptorProto *msg, size_t *len) { return (const google_protobuf_DescriptorProto_ReservedRange* const*)_upb_array_accessor(msg, UPB_SIZE(40, 80), len); }
+UPB_INLINE upb_strview const* google_protobuf_DescriptorProto_reserved_name(const google_protobuf_DescriptorProto *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(44, 88), len); }
+
+UPB_INLINE void google_protobuf_DescriptorProto_set_name(google_protobuf_DescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_DescriptorProto_mutable_field(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 32), len);
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_DescriptorProto_resize_field(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_FieldDescriptorProto* google_protobuf_DescriptorProto_add_field(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_FieldDescriptorProto* sub = (struct google_protobuf_FieldDescriptorProto*)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_DescriptorProto_mutable_nested_type(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_DescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 40), len);
+}
+UPB_INLINE google_protobuf_DescriptorProto** google_protobuf_DescriptorProto_resize_nested_type(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_DescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_DescriptorProto* google_protobuf_DescriptorProto_add_nested_type(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_DescriptorProto* sub = (struct google_protobuf_DescriptorProto*)_upb_msg_new(&google_protobuf_DescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(20, 40), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto** google_protobuf_DescriptorProto_mutable_enum_type(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_EnumDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 48), len);
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto** google_protobuf_DescriptorProto_resize_enum_type(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_EnumDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_EnumDescriptorProto* google_protobuf_DescriptorProto_add_enum_type(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_EnumDescriptorProto* sub = (struct google_protobuf_EnumDescriptorProto*)_upb_msg_new(&google_protobuf_EnumDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(24, 48), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_DescriptorProto_ExtensionRange** google_protobuf_DescriptorProto_mutable_extension_range(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_DescriptorProto_ExtensionRange**)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 56), len);
+}
+UPB_INLINE google_protobuf_DescriptorProto_ExtensionRange** google_protobuf_DescriptorProto_resize_extension_range(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_DescriptorProto_ExtensionRange**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 56), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_DescriptorProto_ExtensionRange* google_protobuf_DescriptorProto_add_extension_range(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_DescriptorProto_ExtensionRange* sub = (struct google_protobuf_DescriptorProto_ExtensionRange*)_upb_msg_new(&google_protobuf_DescriptorProto_ExtensionRange_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(28, 56), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_DescriptorProto_mutable_extension(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(32, 64), len);
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto** google_protobuf_DescriptorProto_resize_extension(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_FieldDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(32, 64), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_FieldDescriptorProto* google_protobuf_DescriptorProto_add_extension(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_FieldDescriptorProto* sub = (struct google_protobuf_FieldDescriptorProto*)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(32, 64), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void google_protobuf_DescriptorProto_set_options(google_protobuf_DescriptorProto *msg, google_protobuf_MessageOptions* value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), google_protobuf_MessageOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_MessageOptions* google_protobuf_DescriptorProto_mutable_options(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_MessageOptions* sub = (struct google_protobuf_MessageOptions*)google_protobuf_DescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_MessageOptions*)_upb_msg_new(&google_protobuf_MessageOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_DescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE google_protobuf_OneofDescriptorProto** google_protobuf_DescriptorProto_mutable_oneof_decl(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_OneofDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(36, 72), len);
+}
+UPB_INLINE google_protobuf_OneofDescriptorProto** google_protobuf_DescriptorProto_resize_oneof_decl(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_OneofDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(36, 72), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_OneofDescriptorProto* google_protobuf_DescriptorProto_add_oneof_decl(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_OneofDescriptorProto* sub = (struct google_protobuf_OneofDescriptorProto*)_upb_msg_new(&google_protobuf_OneofDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(36, 72), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE google_protobuf_DescriptorProto_ReservedRange** google_protobuf_DescriptorProto_mutable_reserved_range(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (google_protobuf_DescriptorProto_ReservedRange**)_upb_array_mutable_accessor(msg, UPB_SIZE(40, 80), len);
+}
+UPB_INLINE google_protobuf_DescriptorProto_ReservedRange** google_protobuf_DescriptorProto_resize_reserved_range(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_DescriptorProto_ReservedRange**)_upb_array_resize_accessor(msg, UPB_SIZE(40, 80), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_DescriptorProto_ReservedRange* google_protobuf_DescriptorProto_add_reserved_range(google_protobuf_DescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_DescriptorProto_ReservedRange* sub = (struct google_protobuf_DescriptorProto_ReservedRange*)_upb_msg_new(&google_protobuf_DescriptorProto_ReservedRange_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(40, 80), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE upb_strview* google_protobuf_DescriptorProto_mutable_reserved_name(google_protobuf_DescriptorProto *msg, size_t *len) {
+  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(44, 88), len);
+}
+UPB_INLINE upb_strview* google_protobuf_DescriptorProto_resize_reserved_name(google_protobuf_DescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(44, 88), len, UPB_TYPE_STRING, arena);
+}
+UPB_INLINE bool google_protobuf_DescriptorProto_add_reserved_name(google_protobuf_DescriptorProto *msg, upb_strview val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(44, 88), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+      arena);
+}
+
+/* google.protobuf.DescriptorProto.ExtensionRange */
+
+UPB_INLINE google_protobuf_DescriptorProto_ExtensionRange *google_protobuf_DescriptorProto_ExtensionRange_new(upb_arena *arena) {
+  return (google_protobuf_DescriptorProto_ExtensionRange *)_upb_msg_new(&google_protobuf_DescriptorProto_ExtensionRange_msginit, arena);
+}
+UPB_INLINE google_protobuf_DescriptorProto_ExtensionRange *google_protobuf_DescriptorProto_ExtensionRange_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_DescriptorProto_ExtensionRange *ret = google_protobuf_DescriptorProto_ExtensionRange_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ExtensionRange_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_DescriptorProto_ExtensionRange_serialize(const google_protobuf_DescriptorProto_ExtensionRange *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_DescriptorProto_ExtensionRange_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_DescriptorProto_ExtensionRange_has_start(const google_protobuf_DescriptorProto_ExtensionRange *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_DescriptorProto_ExtensionRange_start(const google_protobuf_DescriptorProto_ExtensionRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE bool google_protobuf_DescriptorProto_ExtensionRange_has_end(const google_protobuf_DescriptorProto_ExtensionRange *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int32_t google_protobuf_DescriptorProto_ExtensionRange_end(const google_protobuf_DescriptorProto_ExtensionRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+UPB_INLINE bool google_protobuf_DescriptorProto_ExtensionRange_has_options(const google_protobuf_DescriptorProto_ExtensionRange *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE const google_protobuf_ExtensionRangeOptions* google_protobuf_DescriptorProto_ExtensionRange_options(const google_protobuf_DescriptorProto_ExtensionRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 16), const google_protobuf_ExtensionRangeOptions*); }
+
+UPB_INLINE void google_protobuf_DescriptorProto_ExtensionRange_set_start(google_protobuf_DescriptorProto_ExtensionRange *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_DescriptorProto_ExtensionRange_set_end(google_protobuf_DescriptorProto_ExtensionRange *msg, int32_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_DescriptorProto_ExtensionRange_set_options(google_protobuf_DescriptorProto_ExtensionRange *msg, google_protobuf_ExtensionRangeOptions* value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 16), google_protobuf_ExtensionRangeOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_ExtensionRangeOptions* google_protobuf_DescriptorProto_ExtensionRange_mutable_options(google_protobuf_DescriptorProto_ExtensionRange *msg, upb_arena *arena) {
+  struct google_protobuf_ExtensionRangeOptions* sub = (struct google_protobuf_ExtensionRangeOptions*)google_protobuf_DescriptorProto_ExtensionRange_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_ExtensionRangeOptions*)_upb_msg_new(&google_protobuf_ExtensionRangeOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_DescriptorProto_ExtensionRange_set_options(msg, sub);
+  }
+  return sub;
+}
+
+/* google.protobuf.DescriptorProto.ReservedRange */
+
+UPB_INLINE google_protobuf_DescriptorProto_ReservedRange *google_protobuf_DescriptorProto_ReservedRange_new(upb_arena *arena) {
+  return (google_protobuf_DescriptorProto_ReservedRange *)_upb_msg_new(&google_protobuf_DescriptorProto_ReservedRange_msginit, arena);
+}
+UPB_INLINE google_protobuf_DescriptorProto_ReservedRange *google_protobuf_DescriptorProto_ReservedRange_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_DescriptorProto_ReservedRange *ret = google_protobuf_DescriptorProto_ReservedRange_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_DescriptorProto_ReservedRange_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_DescriptorProto_ReservedRange_serialize(const google_protobuf_DescriptorProto_ReservedRange *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_DescriptorProto_ReservedRange_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_DescriptorProto_ReservedRange_has_start(const google_protobuf_DescriptorProto_ReservedRange *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_DescriptorProto_ReservedRange_start(const google_protobuf_DescriptorProto_ReservedRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE bool google_protobuf_DescriptorProto_ReservedRange_has_end(const google_protobuf_DescriptorProto_ReservedRange *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int32_t google_protobuf_DescriptorProto_ReservedRange_end(const google_protobuf_DescriptorProto_ReservedRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+
+UPB_INLINE void google_protobuf_DescriptorProto_ReservedRange_set_start(google_protobuf_DescriptorProto_ReservedRange *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_DescriptorProto_ReservedRange_set_end(google_protobuf_DescriptorProto_ReservedRange *msg, int32_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+
+/* google.protobuf.ExtensionRangeOptions */
+
+UPB_INLINE google_protobuf_ExtensionRangeOptions *google_protobuf_ExtensionRangeOptions_new(upb_arena *arena) {
+  return (google_protobuf_ExtensionRangeOptions *)_upb_msg_new(&google_protobuf_ExtensionRangeOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_ExtensionRangeOptions *google_protobuf_ExtensionRangeOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_ExtensionRangeOptions *ret = google_protobuf_ExtensionRangeOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_ExtensionRangeOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_ExtensionRangeOptions_serialize(const google_protobuf_ExtensionRangeOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_ExtensionRangeOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_ExtensionRangeOptions_has_uninterpreted_option(const google_protobuf_ExtensionRangeOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(0, 0)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_ExtensionRangeOptions_uninterpreted_option(const google_protobuf_ExtensionRangeOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
+
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_ExtensionRangeOptions_mutable_uninterpreted_option(google_protobuf_ExtensionRangeOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_ExtensionRangeOptions_resize_uninterpreted_option(google_protobuf_ExtensionRangeOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_ExtensionRangeOptions_add_uninterpreted_option(google_protobuf_ExtensionRangeOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.FieldDescriptorProto */
+
+UPB_INLINE google_protobuf_FieldDescriptorProto *google_protobuf_FieldDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_FieldDescriptorProto *)_upb_msg_new(&google_protobuf_FieldDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_FieldDescriptorProto *google_protobuf_FieldDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_FieldDescriptorProto *ret = google_protobuf_FieldDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_FieldDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_FieldDescriptorProto_serialize(const google_protobuf_FieldDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_FieldDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_name(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 6); }
+UPB_INLINE upb_strview google_protobuf_FieldDescriptorProto_name(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(36, 40), upb_strview); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_extendee(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 7); }
+UPB_INLINE upb_strview google_protobuf_FieldDescriptorProto_extendee(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(44, 56), upb_strview); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_number(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_number(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(24, 24), int32_t); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_label(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_label(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_type(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_type(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 16), int32_t); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_type_name(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 8); }
+UPB_INLINE upb_strview google_protobuf_FieldDescriptorProto_type_name(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(52, 72), upb_strview); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_default_value(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 9); }
+UPB_INLINE upb_strview google_protobuf_FieldDescriptorProto_default_value(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(60, 88), upb_strview); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_options(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 11); }
+UPB_INLINE const google_protobuf_FieldOptions* google_protobuf_FieldDescriptorProto_options(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(76, 120), const google_protobuf_FieldOptions*); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_oneof_index(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE int32_t google_protobuf_FieldDescriptorProto_oneof_index(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(28, 28), int32_t); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_json_name(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 10); }
+UPB_INLINE upb_strview google_protobuf_FieldDescriptorProto_json_name(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(68, 104), upb_strview); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_has_proto3_optional(const google_protobuf_FieldDescriptorProto *msg) { return _upb_hasbit(msg, 5); }
+UPB_INLINE bool google_protobuf_FieldDescriptorProto_proto3_optional(const google_protobuf_FieldDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(32, 32), bool); }
+
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_name(google_protobuf_FieldDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 6);
+  *UPB_PTR_AT(msg, UPB_SIZE(36, 40), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_extendee(google_protobuf_FieldDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 7);
+  *UPB_PTR_AT(msg, UPB_SIZE(44, 56), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_number(google_protobuf_FieldDescriptorProto *msg, int32_t value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(24, 24), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_label(google_protobuf_FieldDescriptorProto *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_type(google_protobuf_FieldDescriptorProto *msg, int32_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(16, 16), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_type_name(google_protobuf_FieldDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 8);
+  *UPB_PTR_AT(msg, UPB_SIZE(52, 72), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_default_value(google_protobuf_FieldDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 9);
+  *UPB_PTR_AT(msg, UPB_SIZE(60, 88), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_options(google_protobuf_FieldDescriptorProto *msg, google_protobuf_FieldOptions* value) {
+  _upb_sethas(msg, 11);
+  *UPB_PTR_AT(msg, UPB_SIZE(76, 120), google_protobuf_FieldOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_FieldOptions* google_protobuf_FieldDescriptorProto_mutable_options(google_protobuf_FieldDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_FieldOptions* sub = (struct google_protobuf_FieldOptions*)google_protobuf_FieldDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_FieldOptions*)_upb_msg_new(&google_protobuf_FieldOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_FieldDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_oneof_index(google_protobuf_FieldDescriptorProto *msg, int32_t value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(28, 28), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_json_name(google_protobuf_FieldDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 10);
+  *UPB_PTR_AT(msg, UPB_SIZE(68, 104), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FieldDescriptorProto_set_proto3_optional(google_protobuf_FieldDescriptorProto *msg, bool value) {
+  _upb_sethas(msg, 5);
+  *UPB_PTR_AT(msg, UPB_SIZE(32, 32), bool) = value;
+}
+
+/* google.protobuf.OneofDescriptorProto */
+
+UPB_INLINE google_protobuf_OneofDescriptorProto *google_protobuf_OneofDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_OneofDescriptorProto *)_upb_msg_new(&google_protobuf_OneofDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_OneofDescriptorProto *google_protobuf_OneofDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_OneofDescriptorProto *ret = google_protobuf_OneofDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_OneofDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_OneofDescriptorProto_serialize(const google_protobuf_OneofDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_OneofDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_OneofDescriptorProto_has_name(const google_protobuf_OneofDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE upb_strview google_protobuf_OneofDescriptorProto_name(const google_protobuf_OneofDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_OneofDescriptorProto_has_options(const google_protobuf_OneofDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE const google_protobuf_OneofOptions* google_protobuf_OneofDescriptorProto_options(const google_protobuf_OneofDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), const google_protobuf_OneofOptions*); }
+
+UPB_INLINE void google_protobuf_OneofDescriptorProto_set_name(google_protobuf_OneofDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_OneofDescriptorProto_set_options(google_protobuf_OneofDescriptorProto *msg, google_protobuf_OneofOptions* value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), google_protobuf_OneofOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_OneofOptions* google_protobuf_OneofDescriptorProto_mutable_options(google_protobuf_OneofDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_OneofOptions* sub = (struct google_protobuf_OneofOptions*)google_protobuf_OneofDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_OneofOptions*)_upb_msg_new(&google_protobuf_OneofOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_OneofDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+
+/* google.protobuf.EnumDescriptorProto */
+
+UPB_INLINE google_protobuf_EnumDescriptorProto *google_protobuf_EnumDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_EnumDescriptorProto *)_upb_msg_new(&google_protobuf_EnumDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto *google_protobuf_EnumDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_EnumDescriptorProto *ret = google_protobuf_EnumDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_EnumDescriptorProto_serialize(const google_protobuf_EnumDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_EnumDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_has_name(const google_protobuf_EnumDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE upb_strview google_protobuf_EnumDescriptorProto_name(const google_protobuf_EnumDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_has_value(const google_protobuf_EnumDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(16, 32)); }
+UPB_INLINE const google_protobuf_EnumValueDescriptorProto* const* google_protobuf_EnumDescriptorProto_value(const google_protobuf_EnumDescriptorProto *msg, size_t *len) { return (const google_protobuf_EnumValueDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(16, 32), len); }
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_has_options(const google_protobuf_EnumDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE const google_protobuf_EnumOptions* google_protobuf_EnumDescriptorProto_options(const google_protobuf_EnumDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), const google_protobuf_EnumOptions*); }
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_has_reserved_range(const google_protobuf_EnumDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(20, 40)); }
+UPB_INLINE const google_protobuf_EnumDescriptorProto_EnumReservedRange* const* google_protobuf_EnumDescriptorProto_reserved_range(const google_protobuf_EnumDescriptorProto *msg, size_t *len) { return (const google_protobuf_EnumDescriptorProto_EnumReservedRange* const*)_upb_array_accessor(msg, UPB_SIZE(20, 40), len); }
+UPB_INLINE upb_strview const* google_protobuf_EnumDescriptorProto_reserved_name(const google_protobuf_EnumDescriptorProto *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(24, 48), len); }
+
+UPB_INLINE void google_protobuf_EnumDescriptorProto_set_name(google_protobuf_EnumDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE google_protobuf_EnumValueDescriptorProto** google_protobuf_EnumDescriptorProto_mutable_value(google_protobuf_EnumDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_EnumValueDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 32), len);
+}
+UPB_INLINE google_protobuf_EnumValueDescriptorProto** google_protobuf_EnumDescriptorProto_resize_value(google_protobuf_EnumDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_EnumValueDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_EnumValueDescriptorProto* google_protobuf_EnumDescriptorProto_add_value(google_protobuf_EnumDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_EnumValueDescriptorProto* sub = (struct google_protobuf_EnumValueDescriptorProto*)_upb_msg_new(&google_protobuf_EnumValueDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void google_protobuf_EnumDescriptorProto_set_options(google_protobuf_EnumDescriptorProto *msg, google_protobuf_EnumOptions* value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), google_protobuf_EnumOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_EnumOptions* google_protobuf_EnumDescriptorProto_mutable_options(google_protobuf_EnumDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_EnumOptions* sub = (struct google_protobuf_EnumOptions*)google_protobuf_EnumDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_EnumOptions*)_upb_msg_new(&google_protobuf_EnumOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_EnumDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto_EnumReservedRange** google_protobuf_EnumDescriptorProto_mutable_reserved_range(google_protobuf_EnumDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_EnumDescriptorProto_EnumReservedRange**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 40), len);
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto_EnumReservedRange** google_protobuf_EnumDescriptorProto_resize_reserved_range(google_protobuf_EnumDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_EnumDescriptorProto_EnumReservedRange**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_EnumDescriptorProto_EnumReservedRange* google_protobuf_EnumDescriptorProto_add_reserved_range(google_protobuf_EnumDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_EnumDescriptorProto_EnumReservedRange* sub = (struct google_protobuf_EnumDescriptorProto_EnumReservedRange*)_upb_msg_new(&google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(20, 40), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE upb_strview* google_protobuf_EnumDescriptorProto_mutable_reserved_name(google_protobuf_EnumDescriptorProto *msg, size_t *len) {
+  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 48), len);
+}
+UPB_INLINE upb_strview* google_protobuf_EnumDescriptorProto_resize_reserved_name(google_protobuf_EnumDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_TYPE_STRING, arena);
+}
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_add_reserved_name(google_protobuf_EnumDescriptorProto *msg, upb_strview val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(24, 48), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+      arena);
+}
+
+/* google.protobuf.EnumDescriptorProto.EnumReservedRange */
+
+UPB_INLINE google_protobuf_EnumDescriptorProto_EnumReservedRange *google_protobuf_EnumDescriptorProto_EnumReservedRange_new(upb_arena *arena) {
+  return (google_protobuf_EnumDescriptorProto_EnumReservedRange *)_upb_msg_new(&google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena);
+}
+UPB_INLINE google_protobuf_EnumDescriptorProto_EnumReservedRange *google_protobuf_EnumDescriptorProto_EnumReservedRange_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_EnumDescriptorProto_EnumReservedRange *ret = google_protobuf_EnumDescriptorProto_EnumReservedRange_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_EnumDescriptorProto_EnumReservedRange_serialize(const google_protobuf_EnumDescriptorProto_EnumReservedRange *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_EnumReservedRange_has_start(const google_protobuf_EnumDescriptorProto_EnumReservedRange *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_EnumDescriptorProto_EnumReservedRange_start(const google_protobuf_EnumDescriptorProto_EnumReservedRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE bool google_protobuf_EnumDescriptorProto_EnumReservedRange_has_end(const google_protobuf_EnumDescriptorProto_EnumReservedRange *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int32_t google_protobuf_EnumDescriptorProto_EnumReservedRange_end(const google_protobuf_EnumDescriptorProto_EnumReservedRange *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+
+UPB_INLINE void google_protobuf_EnumDescriptorProto_EnumReservedRange_set_start(google_protobuf_EnumDescriptorProto_EnumReservedRange *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_EnumDescriptorProto_EnumReservedRange_set_end(google_protobuf_EnumDescriptorProto_EnumReservedRange *msg, int32_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+
+/* google.protobuf.EnumValueDescriptorProto */
+
+UPB_INLINE google_protobuf_EnumValueDescriptorProto *google_protobuf_EnumValueDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_EnumValueDescriptorProto *)_upb_msg_new(&google_protobuf_EnumValueDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_EnumValueDescriptorProto *google_protobuf_EnumValueDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_EnumValueDescriptorProto *ret = google_protobuf_EnumValueDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_EnumValueDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_EnumValueDescriptorProto_serialize(const google_protobuf_EnumValueDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_EnumValueDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_EnumValueDescriptorProto_has_name(const google_protobuf_EnumValueDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE upb_strview google_protobuf_EnumValueDescriptorProto_name(const google_protobuf_EnumValueDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_EnumValueDescriptorProto_has_number(const google_protobuf_EnumValueDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_EnumValueDescriptorProto_number(const google_protobuf_EnumValueDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE bool google_protobuf_EnumValueDescriptorProto_has_options(const google_protobuf_EnumValueDescriptorProto *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE const google_protobuf_EnumValueOptions* google_protobuf_EnumValueDescriptorProto_options(const google_protobuf_EnumValueDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 24), const google_protobuf_EnumValueOptions*); }
+
+UPB_INLINE void google_protobuf_EnumValueDescriptorProto_set_name(google_protobuf_EnumValueDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_EnumValueDescriptorProto_set_number(google_protobuf_EnumValueDescriptorProto *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_EnumValueDescriptorProto_set_options(google_protobuf_EnumValueDescriptorProto *msg, google_protobuf_EnumValueOptions* value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(16, 24), google_protobuf_EnumValueOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_EnumValueOptions* google_protobuf_EnumValueDescriptorProto_mutable_options(google_protobuf_EnumValueDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_EnumValueOptions* sub = (struct google_protobuf_EnumValueOptions*)google_protobuf_EnumValueDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_EnumValueOptions*)_upb_msg_new(&google_protobuf_EnumValueOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_EnumValueDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+
+/* google.protobuf.ServiceDescriptorProto */
+
+UPB_INLINE google_protobuf_ServiceDescriptorProto *google_protobuf_ServiceDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_ServiceDescriptorProto *)_upb_msg_new(&google_protobuf_ServiceDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_ServiceDescriptorProto *google_protobuf_ServiceDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_ServiceDescriptorProto *ret = google_protobuf_ServiceDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_ServiceDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_ServiceDescriptorProto_serialize(const google_protobuf_ServiceDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_ServiceDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_ServiceDescriptorProto_has_name(const google_protobuf_ServiceDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE upb_strview google_protobuf_ServiceDescriptorProto_name(const google_protobuf_ServiceDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_ServiceDescriptorProto_has_method(const google_protobuf_ServiceDescriptorProto *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(16, 32)); }
+UPB_INLINE const google_protobuf_MethodDescriptorProto* const* google_protobuf_ServiceDescriptorProto_method(const google_protobuf_ServiceDescriptorProto *msg, size_t *len) { return (const google_protobuf_MethodDescriptorProto* const*)_upb_array_accessor(msg, UPB_SIZE(16, 32), len); }
+UPB_INLINE bool google_protobuf_ServiceDescriptorProto_has_options(const google_protobuf_ServiceDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE const google_protobuf_ServiceOptions* google_protobuf_ServiceDescriptorProto_options(const google_protobuf_ServiceDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), const google_protobuf_ServiceOptions*); }
+
+UPB_INLINE void google_protobuf_ServiceDescriptorProto_set_name(google_protobuf_ServiceDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE google_protobuf_MethodDescriptorProto** google_protobuf_ServiceDescriptorProto_mutable_method(google_protobuf_ServiceDescriptorProto *msg, size_t *len) {
+  return (google_protobuf_MethodDescriptorProto**)_upb_array_mutable_accessor(msg, UPB_SIZE(16, 32), len);
+}
+UPB_INLINE google_protobuf_MethodDescriptorProto** google_protobuf_ServiceDescriptorProto_resize_method(google_protobuf_ServiceDescriptorProto *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_MethodDescriptorProto**)_upb_array_resize_accessor(msg, UPB_SIZE(16, 32), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_MethodDescriptorProto* google_protobuf_ServiceDescriptorProto_add_method(google_protobuf_ServiceDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_MethodDescriptorProto* sub = (struct google_protobuf_MethodDescriptorProto*)_upb_msg_new(&google_protobuf_MethodDescriptorProto_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(16, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void google_protobuf_ServiceDescriptorProto_set_options(google_protobuf_ServiceDescriptorProto *msg, google_protobuf_ServiceOptions* value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), google_protobuf_ServiceOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_ServiceOptions* google_protobuf_ServiceDescriptorProto_mutable_options(google_protobuf_ServiceDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_ServiceOptions* sub = (struct google_protobuf_ServiceOptions*)google_protobuf_ServiceDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_ServiceOptions*)_upb_msg_new(&google_protobuf_ServiceOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_ServiceDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+
+/* google.protobuf.MethodDescriptorProto */
+
+UPB_INLINE google_protobuf_MethodDescriptorProto *google_protobuf_MethodDescriptorProto_new(upb_arena *arena) {
+  return (google_protobuf_MethodDescriptorProto *)_upb_msg_new(&google_protobuf_MethodDescriptorProto_msginit, arena);
+}
+UPB_INLINE google_protobuf_MethodDescriptorProto *google_protobuf_MethodDescriptorProto_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_MethodDescriptorProto *ret = google_protobuf_MethodDescriptorProto_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_MethodDescriptorProto_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_MethodDescriptorProto_serialize(const google_protobuf_MethodDescriptorProto *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_MethodDescriptorProto_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_has_name(const google_protobuf_MethodDescriptorProto *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE upb_strview google_protobuf_MethodDescriptorProto_name(const google_protobuf_MethodDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_has_input_type(const google_protobuf_MethodDescriptorProto *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE upb_strview google_protobuf_MethodDescriptorProto_input_type(const google_protobuf_MethodDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), upb_strview); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_has_output_type(const google_protobuf_MethodDescriptorProto *msg) { return _upb_hasbit(msg, 5); }
+UPB_INLINE upb_strview google_protobuf_MethodDescriptorProto_output_type(const google_protobuf_MethodDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(20, 40), upb_strview); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_has_options(const google_protobuf_MethodDescriptorProto *msg) { return _upb_hasbit(msg, 6); }
+UPB_INLINE const google_protobuf_MethodOptions* google_protobuf_MethodDescriptorProto_options(const google_protobuf_MethodDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(28, 56), const google_protobuf_MethodOptions*); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_has_client_streaming(const google_protobuf_MethodDescriptorProto *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_client_streaming(const google_protobuf_MethodDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_has_server_streaming(const google_protobuf_MethodDescriptorProto *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE bool google_protobuf_MethodDescriptorProto_server_streaming(const google_protobuf_MethodDescriptorProto *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(2, 2), bool); }
+
+UPB_INLINE void google_protobuf_MethodDescriptorProto_set_name(google_protobuf_MethodDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_MethodDescriptorProto_set_input_type(google_protobuf_MethodDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_MethodDescriptorProto_set_output_type(google_protobuf_MethodDescriptorProto *msg, upb_strview value) {
+  _upb_sethas(msg, 5);
+  *UPB_PTR_AT(msg, UPB_SIZE(20, 40), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_MethodDescriptorProto_set_options(google_protobuf_MethodDescriptorProto *msg, google_protobuf_MethodOptions* value) {
+  _upb_sethas(msg, 6);
+  *UPB_PTR_AT(msg, UPB_SIZE(28, 56), google_protobuf_MethodOptions*) = value;
+}
+UPB_INLINE struct google_protobuf_MethodOptions* google_protobuf_MethodDescriptorProto_mutable_options(google_protobuf_MethodDescriptorProto *msg, upb_arena *arena) {
+  struct google_protobuf_MethodOptions* sub = (struct google_protobuf_MethodOptions*)google_protobuf_MethodDescriptorProto_options(msg);
+  if (sub == NULL) {
+    sub = (struct google_protobuf_MethodOptions*)_upb_msg_new(&google_protobuf_MethodOptions_msginit, arena);
+    if (!sub) return NULL;
+    google_protobuf_MethodDescriptorProto_set_options(msg, sub);
+  }
+  return sub;
+}
+UPB_INLINE void google_protobuf_MethodDescriptorProto_set_client_streaming(google_protobuf_MethodDescriptorProto *msg, bool value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value;
+}
+UPB_INLINE void google_protobuf_MethodDescriptorProto_set_server_streaming(google_protobuf_MethodDescriptorProto *msg, bool value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(2, 2), bool) = value;
+}
+
+/* google.protobuf.FileOptions */
+
+UPB_INLINE google_protobuf_FileOptions *google_protobuf_FileOptions_new(upb_arena *arena) {
+  return (google_protobuf_FileOptions *)_upb_msg_new(&google_protobuf_FileOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_FileOptions *google_protobuf_FileOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_FileOptions *ret = google_protobuf_FileOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_FileOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_FileOptions_serialize(const google_protobuf_FileOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_FileOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_FileOptions_has_java_package(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 11); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_java_package(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(28, 32), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_java_outer_classname(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 12); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_java_outer_classname(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(36, 48), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_optimize_for(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_FileOptions_optimize_for(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+UPB_INLINE bool google_protobuf_FileOptions_has_java_multiple_files(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE bool google_protobuf_FileOptions_java_multiple_files(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 16), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_go_package(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 13); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_go_package(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(44, 64), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_cc_generic_services(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE bool google_protobuf_FileOptions_cc_generic_services(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(17, 17), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_java_generic_services(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE bool google_protobuf_FileOptions_java_generic_services(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(18, 18), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_py_generic_services(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 5); }
+UPB_INLINE bool google_protobuf_FileOptions_py_generic_services(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(19, 19), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_java_generate_equals_and_hash(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 6); }
+UPB_INLINE bool google_protobuf_FileOptions_java_generate_equals_and_hash(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(20, 20), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_deprecated(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 7); }
+UPB_INLINE bool google_protobuf_FileOptions_deprecated(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(21, 21), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_java_string_check_utf8(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 8); }
+UPB_INLINE bool google_protobuf_FileOptions_java_string_check_utf8(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(22, 22), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_cc_enable_arenas(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 9); }
+UPB_INLINE bool google_protobuf_FileOptions_cc_enable_arenas(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(23, 23), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_objc_class_prefix(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 14); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_objc_class_prefix(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(52, 80), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_csharp_namespace(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 15); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_csharp_namespace(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(60, 96), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_swift_prefix(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 16); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_swift_prefix(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(68, 112), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_php_class_prefix(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 17); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_php_class_prefix(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(76, 128), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_php_namespace(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 18); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_php_namespace(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(84, 144), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_php_generic_services(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 10); }
+UPB_INLINE bool google_protobuf_FileOptions_php_generic_services(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(24, 24), bool); }
+UPB_INLINE bool google_protobuf_FileOptions_has_php_metadata_namespace(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 19); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_php_metadata_namespace(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(92, 160), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_ruby_package(const google_protobuf_FileOptions *msg) { return _upb_hasbit(msg, 20); }
+UPB_INLINE upb_strview google_protobuf_FileOptions_ruby_package(const google_protobuf_FileOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(100, 176), upb_strview); }
+UPB_INLINE bool google_protobuf_FileOptions_has_uninterpreted_option(const google_protobuf_FileOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(108, 192)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_FileOptions_uninterpreted_option(const google_protobuf_FileOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(108, 192), len); }
+
+UPB_INLINE void google_protobuf_FileOptions_set_java_package(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 11);
+  *UPB_PTR_AT(msg, UPB_SIZE(28, 32), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_java_outer_classname(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 12);
+  *UPB_PTR_AT(msg, UPB_SIZE(36, 48), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_optimize_for(google_protobuf_FileOptions *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_java_multiple_files(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(16, 16), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_go_package(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 13);
+  *UPB_PTR_AT(msg, UPB_SIZE(44, 64), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_cc_generic_services(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(17, 17), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_java_generic_services(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(18, 18), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_py_generic_services(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 5);
+  *UPB_PTR_AT(msg, UPB_SIZE(19, 19), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_java_generate_equals_and_hash(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 6);
+  *UPB_PTR_AT(msg, UPB_SIZE(20, 20), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_deprecated(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 7);
+  *UPB_PTR_AT(msg, UPB_SIZE(21, 21), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_java_string_check_utf8(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 8);
+  *UPB_PTR_AT(msg, UPB_SIZE(22, 22), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_cc_enable_arenas(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 9);
+  *UPB_PTR_AT(msg, UPB_SIZE(23, 23), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_objc_class_prefix(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 14);
+  *UPB_PTR_AT(msg, UPB_SIZE(52, 80), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_csharp_namespace(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 15);
+  *UPB_PTR_AT(msg, UPB_SIZE(60, 96), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_swift_prefix(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 16);
+  *UPB_PTR_AT(msg, UPB_SIZE(68, 112), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_php_class_prefix(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 17);
+  *UPB_PTR_AT(msg, UPB_SIZE(76, 128), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_php_namespace(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 18);
+  *UPB_PTR_AT(msg, UPB_SIZE(84, 144), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_php_generic_services(google_protobuf_FileOptions *msg, bool value) {
+  _upb_sethas(msg, 10);
+  *UPB_PTR_AT(msg, UPB_SIZE(24, 24), bool) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_php_metadata_namespace(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 19);
+  *UPB_PTR_AT(msg, UPB_SIZE(92, 160), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_FileOptions_set_ruby_package(google_protobuf_FileOptions *msg, upb_strview value) {
+  _upb_sethas(msg, 20);
+  *UPB_PTR_AT(msg, UPB_SIZE(100, 176), upb_strview) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_FileOptions_mutable_uninterpreted_option(google_protobuf_FileOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(108, 192), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_FileOptions_resize_uninterpreted_option(google_protobuf_FileOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(108, 192), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_FileOptions_add_uninterpreted_option(google_protobuf_FileOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(108, 192), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.MessageOptions */
+
+UPB_INLINE google_protobuf_MessageOptions *google_protobuf_MessageOptions_new(upb_arena *arena) {
+  return (google_protobuf_MessageOptions *)_upb_msg_new(&google_protobuf_MessageOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_MessageOptions *google_protobuf_MessageOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_MessageOptions *ret = google_protobuf_MessageOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_MessageOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_MessageOptions_serialize(const google_protobuf_MessageOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_MessageOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_MessageOptions_has_message_set_wire_format(const google_protobuf_MessageOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE bool google_protobuf_MessageOptions_message_set_wire_format(const google_protobuf_MessageOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); }
+UPB_INLINE bool google_protobuf_MessageOptions_has_no_standard_descriptor_accessor(const google_protobuf_MessageOptions *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE bool google_protobuf_MessageOptions_no_standard_descriptor_accessor(const google_protobuf_MessageOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(2, 2), bool); }
+UPB_INLINE bool google_protobuf_MessageOptions_has_deprecated(const google_protobuf_MessageOptions *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE bool google_protobuf_MessageOptions_deprecated(const google_protobuf_MessageOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(3, 3), bool); }
+UPB_INLINE bool google_protobuf_MessageOptions_has_map_entry(const google_protobuf_MessageOptions *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE bool google_protobuf_MessageOptions_map_entry(const google_protobuf_MessageOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), bool); }
+UPB_INLINE bool google_protobuf_MessageOptions_has_uninterpreted_option(const google_protobuf_MessageOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(8, 8)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_MessageOptions_uninterpreted_option(const google_protobuf_MessageOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(8, 8), len); }
+
+UPB_INLINE void google_protobuf_MessageOptions_set_message_set_wire_format(google_protobuf_MessageOptions *msg, bool value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value;
+}
+UPB_INLINE void google_protobuf_MessageOptions_set_no_standard_descriptor_accessor(google_protobuf_MessageOptions *msg, bool value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(2, 2), bool) = value;
+}
+UPB_INLINE void google_protobuf_MessageOptions_set_deprecated(google_protobuf_MessageOptions *msg, bool value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(3, 3), bool) = value;
+}
+UPB_INLINE void google_protobuf_MessageOptions_set_map_entry(google_protobuf_MessageOptions *msg, bool value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 4), bool) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_MessageOptions_mutable_uninterpreted_option(google_protobuf_MessageOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(8, 8), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_MessageOptions_resize_uninterpreted_option(google_protobuf_MessageOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(8, 8), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_MessageOptions_add_uninterpreted_option(google_protobuf_MessageOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(8, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.FieldOptions */
+
+UPB_INLINE google_protobuf_FieldOptions *google_protobuf_FieldOptions_new(upb_arena *arena) {
+  return (google_protobuf_FieldOptions *)_upb_msg_new(&google_protobuf_FieldOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_FieldOptions *google_protobuf_FieldOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_FieldOptions *ret = google_protobuf_FieldOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_FieldOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_FieldOptions_serialize(const google_protobuf_FieldOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_FieldOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_FieldOptions_has_ctype(const google_protobuf_FieldOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_FieldOptions_ctype(const google_protobuf_FieldOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+UPB_INLINE bool google_protobuf_FieldOptions_has_packed(const google_protobuf_FieldOptions *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE bool google_protobuf_FieldOptions_packed(const google_protobuf_FieldOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(24, 24), bool); }
+UPB_INLINE bool google_protobuf_FieldOptions_has_deprecated(const google_protobuf_FieldOptions *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE bool google_protobuf_FieldOptions_deprecated(const google_protobuf_FieldOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(25, 25), bool); }
+UPB_INLINE bool google_protobuf_FieldOptions_has_lazy(const google_protobuf_FieldOptions *msg) { return _upb_hasbit(msg, 5); }
+UPB_INLINE bool google_protobuf_FieldOptions_lazy(const google_protobuf_FieldOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(26, 26), bool); }
+UPB_INLINE bool google_protobuf_FieldOptions_has_jstype(const google_protobuf_FieldOptions *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int32_t google_protobuf_FieldOptions_jstype(const google_protobuf_FieldOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 16), int32_t); }
+UPB_INLINE bool google_protobuf_FieldOptions_has_weak(const google_protobuf_FieldOptions *msg) { return _upb_hasbit(msg, 6); }
+UPB_INLINE bool google_protobuf_FieldOptions_weak(const google_protobuf_FieldOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(27, 27), bool); }
+UPB_INLINE bool google_protobuf_FieldOptions_has_uninterpreted_option(const google_protobuf_FieldOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(28, 32)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_FieldOptions_uninterpreted_option(const google_protobuf_FieldOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(28, 32), len); }
+
+UPB_INLINE void google_protobuf_FieldOptions_set_ctype(google_protobuf_FieldOptions *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FieldOptions_set_packed(google_protobuf_FieldOptions *msg, bool value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(24, 24), bool) = value;
+}
+UPB_INLINE void google_protobuf_FieldOptions_set_deprecated(google_protobuf_FieldOptions *msg, bool value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(25, 25), bool) = value;
+}
+UPB_INLINE void google_protobuf_FieldOptions_set_lazy(google_protobuf_FieldOptions *msg, bool value) {
+  _upb_sethas(msg, 5);
+  *UPB_PTR_AT(msg, UPB_SIZE(26, 26), bool) = value;
+}
+UPB_INLINE void google_protobuf_FieldOptions_set_jstype(google_protobuf_FieldOptions *msg, int32_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(16, 16), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_FieldOptions_set_weak(google_protobuf_FieldOptions *msg, bool value) {
+  _upb_sethas(msg, 6);
+  *UPB_PTR_AT(msg, UPB_SIZE(27, 27), bool) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_FieldOptions_mutable_uninterpreted_option(google_protobuf_FieldOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 32), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_FieldOptions_resize_uninterpreted_option(google_protobuf_FieldOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(28, 32), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_FieldOptions_add_uninterpreted_option(google_protobuf_FieldOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(28, 32), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.OneofOptions */
+
+UPB_INLINE google_protobuf_OneofOptions *google_protobuf_OneofOptions_new(upb_arena *arena) {
+  return (google_protobuf_OneofOptions *)_upb_msg_new(&google_protobuf_OneofOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_OneofOptions *google_protobuf_OneofOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_OneofOptions *ret = google_protobuf_OneofOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_OneofOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_OneofOptions_serialize(const google_protobuf_OneofOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_OneofOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_OneofOptions_has_uninterpreted_option(const google_protobuf_OneofOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(0, 0)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_OneofOptions_uninterpreted_option(const google_protobuf_OneofOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
+
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_OneofOptions_mutable_uninterpreted_option(google_protobuf_OneofOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_OneofOptions_resize_uninterpreted_option(google_protobuf_OneofOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_OneofOptions_add_uninterpreted_option(google_protobuf_OneofOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.EnumOptions */
+
+UPB_INLINE google_protobuf_EnumOptions *google_protobuf_EnumOptions_new(upb_arena *arena) {
+  return (google_protobuf_EnumOptions *)_upb_msg_new(&google_protobuf_EnumOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_EnumOptions *google_protobuf_EnumOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_EnumOptions *ret = google_protobuf_EnumOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_EnumOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_EnumOptions_serialize(const google_protobuf_EnumOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_EnumOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_EnumOptions_has_allow_alias(const google_protobuf_EnumOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE bool google_protobuf_EnumOptions_allow_alias(const google_protobuf_EnumOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); }
+UPB_INLINE bool google_protobuf_EnumOptions_has_deprecated(const google_protobuf_EnumOptions *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE bool google_protobuf_EnumOptions_deprecated(const google_protobuf_EnumOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(2, 2), bool); }
+UPB_INLINE bool google_protobuf_EnumOptions_has_uninterpreted_option(const google_protobuf_EnumOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(4, 8)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_EnumOptions_uninterpreted_option(const google_protobuf_EnumOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); }
+
+UPB_INLINE void google_protobuf_EnumOptions_set_allow_alias(google_protobuf_EnumOptions *msg, bool value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value;
+}
+UPB_INLINE void google_protobuf_EnumOptions_set_deprecated(google_protobuf_EnumOptions *msg, bool value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(2, 2), bool) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_EnumOptions_mutable_uninterpreted_option(google_protobuf_EnumOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_EnumOptions_resize_uninterpreted_option(google_protobuf_EnumOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_EnumOptions_add_uninterpreted_option(google_protobuf_EnumOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.EnumValueOptions */
+
+UPB_INLINE google_protobuf_EnumValueOptions *google_protobuf_EnumValueOptions_new(upb_arena *arena) {
+  return (google_protobuf_EnumValueOptions *)_upb_msg_new(&google_protobuf_EnumValueOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_EnumValueOptions *google_protobuf_EnumValueOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_EnumValueOptions *ret = google_protobuf_EnumValueOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_EnumValueOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_EnumValueOptions_serialize(const google_protobuf_EnumValueOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_EnumValueOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_EnumValueOptions_has_deprecated(const google_protobuf_EnumValueOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE bool google_protobuf_EnumValueOptions_deprecated(const google_protobuf_EnumValueOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); }
+UPB_INLINE bool google_protobuf_EnumValueOptions_has_uninterpreted_option(const google_protobuf_EnumValueOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(4, 8)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_EnumValueOptions_uninterpreted_option(const google_protobuf_EnumValueOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); }
+
+UPB_INLINE void google_protobuf_EnumValueOptions_set_deprecated(google_protobuf_EnumValueOptions *msg, bool value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_EnumValueOptions_mutable_uninterpreted_option(google_protobuf_EnumValueOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_EnumValueOptions_resize_uninterpreted_option(google_protobuf_EnumValueOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_EnumValueOptions_add_uninterpreted_option(google_protobuf_EnumValueOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.ServiceOptions */
+
+UPB_INLINE google_protobuf_ServiceOptions *google_protobuf_ServiceOptions_new(upb_arena *arena) {
+  return (google_protobuf_ServiceOptions *)_upb_msg_new(&google_protobuf_ServiceOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_ServiceOptions *google_protobuf_ServiceOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_ServiceOptions *ret = google_protobuf_ServiceOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_ServiceOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_ServiceOptions_serialize(const google_protobuf_ServiceOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_ServiceOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_ServiceOptions_has_deprecated(const google_protobuf_ServiceOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE bool google_protobuf_ServiceOptions_deprecated(const google_protobuf_ServiceOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); }
+UPB_INLINE bool google_protobuf_ServiceOptions_has_uninterpreted_option(const google_protobuf_ServiceOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(4, 8)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_ServiceOptions_uninterpreted_option(const google_protobuf_ServiceOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(4, 8), len); }
+
+UPB_INLINE void google_protobuf_ServiceOptions_set_deprecated(google_protobuf_ServiceOptions *msg, bool value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_ServiceOptions_mutable_uninterpreted_option(google_protobuf_ServiceOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(4, 8), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_ServiceOptions_resize_uninterpreted_option(google_protobuf_ServiceOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(4, 8), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_ServiceOptions_add_uninterpreted_option(google_protobuf_ServiceOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(4, 8), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.MethodOptions */
+
+UPB_INLINE google_protobuf_MethodOptions *google_protobuf_MethodOptions_new(upb_arena *arena) {
+  return (google_protobuf_MethodOptions *)_upb_msg_new(&google_protobuf_MethodOptions_msginit, arena);
+}
+UPB_INLINE google_protobuf_MethodOptions *google_protobuf_MethodOptions_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_MethodOptions *ret = google_protobuf_MethodOptions_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_MethodOptions_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_MethodOptions_serialize(const google_protobuf_MethodOptions *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_MethodOptions_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_MethodOptions_has_deprecated(const google_protobuf_MethodOptions *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE bool google_protobuf_MethodOptions_deprecated(const google_protobuf_MethodOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 16), bool); }
+UPB_INLINE bool google_protobuf_MethodOptions_has_idempotency_level(const google_protobuf_MethodOptions *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_MethodOptions_idempotency_level(const google_protobuf_MethodOptions *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+UPB_INLINE bool google_protobuf_MethodOptions_has_uninterpreted_option(const google_protobuf_MethodOptions *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(20, 24)); }
+UPB_INLINE const google_protobuf_UninterpretedOption* const* google_protobuf_MethodOptions_uninterpreted_option(const google_protobuf_MethodOptions *msg, size_t *len) { return (const google_protobuf_UninterpretedOption* const*)_upb_array_accessor(msg, UPB_SIZE(20, 24), len); }
+
+UPB_INLINE void google_protobuf_MethodOptions_set_deprecated(google_protobuf_MethodOptions *msg, bool value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(16, 16), bool) = value;
+}
+UPB_INLINE void google_protobuf_MethodOptions_set_idempotency_level(google_protobuf_MethodOptions *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_MethodOptions_mutable_uninterpreted_option(google_protobuf_MethodOptions *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 24), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption** google_protobuf_MethodOptions_resize_uninterpreted_option(google_protobuf_MethodOptions *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption**)_upb_array_resize_accessor(msg, UPB_SIZE(20, 24), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption* google_protobuf_MethodOptions_add_uninterpreted_option(google_protobuf_MethodOptions *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption* sub = (struct google_protobuf_UninterpretedOption*)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(20, 24), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.UninterpretedOption */
+
+UPB_INLINE google_protobuf_UninterpretedOption *google_protobuf_UninterpretedOption_new(upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption *)_upb_msg_new(&google_protobuf_UninterpretedOption_msginit, arena);
+}
+UPB_INLINE google_protobuf_UninterpretedOption *google_protobuf_UninterpretedOption_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_UninterpretedOption *ret = google_protobuf_UninterpretedOption_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_UninterpretedOption_serialize(const google_protobuf_UninterpretedOption *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_UninterpretedOption_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_name(const google_protobuf_UninterpretedOption *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(56, 80)); }
+UPB_INLINE const google_protobuf_UninterpretedOption_NamePart* const* google_protobuf_UninterpretedOption_name(const google_protobuf_UninterpretedOption *msg, size_t *len) { return (const google_protobuf_UninterpretedOption_NamePart* const*)_upb_array_accessor(msg, UPB_SIZE(56, 80), len); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_identifier_value(const google_protobuf_UninterpretedOption *msg) { return _upb_hasbit(msg, 4); }
+UPB_INLINE upb_strview google_protobuf_UninterpretedOption_identifier_value(const google_protobuf_UninterpretedOption *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(32, 32), upb_strview); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_positive_int_value(const google_protobuf_UninterpretedOption *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE uint64_t google_protobuf_UninterpretedOption_positive_int_value(const google_protobuf_UninterpretedOption *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), uint64_t); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_negative_int_value(const google_protobuf_UninterpretedOption *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int64_t google_protobuf_UninterpretedOption_negative_int_value(const google_protobuf_UninterpretedOption *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(16, 16), int64_t); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_double_value(const google_protobuf_UninterpretedOption *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE double google_protobuf_UninterpretedOption_double_value(const google_protobuf_UninterpretedOption *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(24, 24), double); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_string_value(const google_protobuf_UninterpretedOption *msg) { return _upb_hasbit(msg, 5); }
+UPB_INLINE upb_strview google_protobuf_UninterpretedOption_string_value(const google_protobuf_UninterpretedOption *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(40, 48), upb_strview); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_has_aggregate_value(const google_protobuf_UninterpretedOption *msg) { return _upb_hasbit(msg, 6); }
+UPB_INLINE upb_strview google_protobuf_UninterpretedOption_aggregate_value(const google_protobuf_UninterpretedOption *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(48, 64), upb_strview); }
+
+UPB_INLINE google_protobuf_UninterpretedOption_NamePart** google_protobuf_UninterpretedOption_mutable_name(google_protobuf_UninterpretedOption *msg, size_t *len) {
+  return (google_protobuf_UninterpretedOption_NamePart**)_upb_array_mutable_accessor(msg, UPB_SIZE(56, 80), len);
+}
+UPB_INLINE google_protobuf_UninterpretedOption_NamePart** google_protobuf_UninterpretedOption_resize_name(google_protobuf_UninterpretedOption *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption_NamePart**)_upb_array_resize_accessor(msg, UPB_SIZE(56, 80), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_UninterpretedOption_NamePart* google_protobuf_UninterpretedOption_add_name(google_protobuf_UninterpretedOption *msg, upb_arena *arena) {
+  struct google_protobuf_UninterpretedOption_NamePart* sub = (struct google_protobuf_UninterpretedOption_NamePart*)_upb_msg_new(&google_protobuf_UninterpretedOption_NamePart_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(56, 80), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_set_identifier_value(google_protobuf_UninterpretedOption *msg, upb_strview value) {
+  _upb_sethas(msg, 4);
+  *UPB_PTR_AT(msg, UPB_SIZE(32, 32), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_set_positive_int_value(google_protobuf_UninterpretedOption *msg, uint64_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), uint64_t) = value;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_set_negative_int_value(google_protobuf_UninterpretedOption *msg, int64_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(16, 16), int64_t) = value;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_set_double_value(google_protobuf_UninterpretedOption *msg, double value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(24, 24), double) = value;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_set_string_value(google_protobuf_UninterpretedOption *msg, upb_strview value) {
+  _upb_sethas(msg, 5);
+  *UPB_PTR_AT(msg, UPB_SIZE(40, 48), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_set_aggregate_value(google_protobuf_UninterpretedOption *msg, upb_strview value) {
+  _upb_sethas(msg, 6);
+  *UPB_PTR_AT(msg, UPB_SIZE(48, 64), upb_strview) = value;
+}
+
+/* google.protobuf.UninterpretedOption.NamePart */
+
+UPB_INLINE google_protobuf_UninterpretedOption_NamePart *google_protobuf_UninterpretedOption_NamePart_new(upb_arena *arena) {
+  return (google_protobuf_UninterpretedOption_NamePart *)_upb_msg_new(&google_protobuf_UninterpretedOption_NamePart_msginit, arena);
+}
+UPB_INLINE google_protobuf_UninterpretedOption_NamePart *google_protobuf_UninterpretedOption_NamePart_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_UninterpretedOption_NamePart *ret = google_protobuf_UninterpretedOption_NamePart_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_UninterpretedOption_NamePart_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_UninterpretedOption_NamePart_serialize(const google_protobuf_UninterpretedOption_NamePart *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_UninterpretedOption_NamePart_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_UninterpretedOption_NamePart_has_name_part(const google_protobuf_UninterpretedOption_NamePart *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE upb_strview google_protobuf_UninterpretedOption_NamePart_name_part(const google_protobuf_UninterpretedOption_NamePart *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_NamePart_has_is_extension(const google_protobuf_UninterpretedOption_NamePart *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE bool google_protobuf_UninterpretedOption_NamePart_is_extension(const google_protobuf_UninterpretedOption_NamePart *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool); }
+
+UPB_INLINE void google_protobuf_UninterpretedOption_NamePart_set_name_part(google_protobuf_UninterpretedOption_NamePart *msg, upb_strview value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_UninterpretedOption_NamePart_set_is_extension(google_protobuf_UninterpretedOption_NamePart *msg, bool value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(1, 1), bool) = value;
+}
+
+/* google.protobuf.SourceCodeInfo */
+
+UPB_INLINE google_protobuf_SourceCodeInfo *google_protobuf_SourceCodeInfo_new(upb_arena *arena) {
+  return (google_protobuf_SourceCodeInfo *)_upb_msg_new(&google_protobuf_SourceCodeInfo_msginit, arena);
+}
+UPB_INLINE google_protobuf_SourceCodeInfo *google_protobuf_SourceCodeInfo_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_SourceCodeInfo *ret = google_protobuf_SourceCodeInfo_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_SourceCodeInfo_serialize(const google_protobuf_SourceCodeInfo *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_SourceCodeInfo_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_SourceCodeInfo_has_location(const google_protobuf_SourceCodeInfo *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(0, 0)); }
+UPB_INLINE const google_protobuf_SourceCodeInfo_Location* const* google_protobuf_SourceCodeInfo_location(const google_protobuf_SourceCodeInfo *msg, size_t *len) { return (const google_protobuf_SourceCodeInfo_Location* const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
+
+UPB_INLINE google_protobuf_SourceCodeInfo_Location** google_protobuf_SourceCodeInfo_mutable_location(google_protobuf_SourceCodeInfo *msg, size_t *len) {
+  return (google_protobuf_SourceCodeInfo_Location**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
+}
+UPB_INLINE google_protobuf_SourceCodeInfo_Location** google_protobuf_SourceCodeInfo_resize_location(google_protobuf_SourceCodeInfo *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_SourceCodeInfo_Location**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_SourceCodeInfo_Location* google_protobuf_SourceCodeInfo_add_location(google_protobuf_SourceCodeInfo *msg, upb_arena *arena) {
+  struct google_protobuf_SourceCodeInfo_Location* sub = (struct google_protobuf_SourceCodeInfo_Location*)_upb_msg_new(&google_protobuf_SourceCodeInfo_Location_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.SourceCodeInfo.Location */
+
+UPB_INLINE google_protobuf_SourceCodeInfo_Location *google_protobuf_SourceCodeInfo_Location_new(upb_arena *arena) {
+  return (google_protobuf_SourceCodeInfo_Location *)_upb_msg_new(&google_protobuf_SourceCodeInfo_Location_msginit, arena);
+}
+UPB_INLINE google_protobuf_SourceCodeInfo_Location *google_protobuf_SourceCodeInfo_Location_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_SourceCodeInfo_Location *ret = google_protobuf_SourceCodeInfo_Location_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_SourceCodeInfo_Location_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_SourceCodeInfo_Location_serialize(const google_protobuf_SourceCodeInfo_Location *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_SourceCodeInfo_Location_msginit, arena, len);
+}
+
+UPB_INLINE int32_t const* google_protobuf_SourceCodeInfo_Location_path(const google_protobuf_SourceCodeInfo_Location *msg, size_t *len) { return (int32_t const*)_upb_array_accessor(msg, UPB_SIZE(20, 40), len); }
+UPB_INLINE int32_t const* google_protobuf_SourceCodeInfo_Location_span(const google_protobuf_SourceCodeInfo_Location *msg, size_t *len) { return (int32_t const*)_upb_array_accessor(msg, UPB_SIZE(24, 48), len); }
+UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_has_leading_comments(const google_protobuf_SourceCodeInfo_Location *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE upb_strview google_protobuf_SourceCodeInfo_Location_leading_comments(const google_protobuf_SourceCodeInfo_Location *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview); }
+UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_has_trailing_comments(const google_protobuf_SourceCodeInfo_Location *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE upb_strview google_protobuf_SourceCodeInfo_Location_trailing_comments(const google_protobuf_SourceCodeInfo_Location *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 24), upb_strview); }
+UPB_INLINE upb_strview const* google_protobuf_SourceCodeInfo_Location_leading_detached_comments(const google_protobuf_SourceCodeInfo_Location *msg, size_t *len) { return (upb_strview const*)_upb_array_accessor(msg, UPB_SIZE(28, 56), len); }
+
+UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_mutable_path(google_protobuf_SourceCodeInfo_Location *msg, size_t *len) {
+  return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 40), len);
+}
+UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_resize_path(google_protobuf_SourceCodeInfo_Location *msg, size_t len, upb_arena *arena) {
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(20, 40), len, UPB_TYPE_INT32, arena);
+}
+UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_add_path(google_protobuf_SourceCodeInfo_Location *msg, int32_t val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(20, 40), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+      arena);
+}
+UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_mutable_span(google_protobuf_SourceCodeInfo_Location *msg, size_t *len) {
+  return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(24, 48), len);
+}
+UPB_INLINE int32_t* google_protobuf_SourceCodeInfo_Location_resize_span(google_protobuf_SourceCodeInfo_Location *msg, size_t len, upb_arena *arena) {
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(24, 48), len, UPB_TYPE_INT32, arena);
+}
+UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_add_span(google_protobuf_SourceCodeInfo_Location *msg, int32_t val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(24, 48), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+      arena);
+}
+UPB_INLINE void google_protobuf_SourceCodeInfo_Location_set_leading_comments(google_protobuf_SourceCodeInfo_Location *msg, upb_strview value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 8), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_SourceCodeInfo_Location_set_trailing_comments(google_protobuf_SourceCodeInfo_Location *msg, upb_strview value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 24), upb_strview) = value;
+}
+UPB_INLINE upb_strview* google_protobuf_SourceCodeInfo_Location_mutable_leading_detached_comments(google_protobuf_SourceCodeInfo_Location *msg, size_t *len) {
+  return (upb_strview*)_upb_array_mutable_accessor(msg, UPB_SIZE(28, 56), len);
+}
+UPB_INLINE upb_strview* google_protobuf_SourceCodeInfo_Location_resize_leading_detached_comments(google_protobuf_SourceCodeInfo_Location *msg, size_t len, upb_arena *arena) {
+  return (upb_strview*)_upb_array_resize_accessor(msg, UPB_SIZE(28, 56), len, UPB_TYPE_STRING, arena);
+}
+UPB_INLINE bool google_protobuf_SourceCodeInfo_Location_add_leading_detached_comments(google_protobuf_SourceCodeInfo_Location *msg, upb_strview val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(28, 56), UPB_SIZE(8, 16), UPB_TYPE_STRING, &val,
+      arena);
+}
+
+/* google.protobuf.GeneratedCodeInfo */
+
+UPB_INLINE google_protobuf_GeneratedCodeInfo *google_protobuf_GeneratedCodeInfo_new(upb_arena *arena) {
+  return (google_protobuf_GeneratedCodeInfo *)_upb_msg_new(&google_protobuf_GeneratedCodeInfo_msginit, arena);
+}
+UPB_INLINE google_protobuf_GeneratedCodeInfo *google_protobuf_GeneratedCodeInfo_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_GeneratedCodeInfo *ret = google_protobuf_GeneratedCodeInfo_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_GeneratedCodeInfo_serialize(const google_protobuf_GeneratedCodeInfo *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_GeneratedCodeInfo_msginit, arena, len);
+}
+
+UPB_INLINE bool google_protobuf_GeneratedCodeInfo_has_annotation(const google_protobuf_GeneratedCodeInfo *msg) { return _upb_has_submsg_nohasbit(msg, UPB_SIZE(0, 0)); }
+UPB_INLINE const google_protobuf_GeneratedCodeInfo_Annotation* const* google_protobuf_GeneratedCodeInfo_annotation(const google_protobuf_GeneratedCodeInfo *msg, size_t *len) { return (const google_protobuf_GeneratedCodeInfo_Annotation* const*)_upb_array_accessor(msg, UPB_SIZE(0, 0), len); }
+
+UPB_INLINE google_protobuf_GeneratedCodeInfo_Annotation** google_protobuf_GeneratedCodeInfo_mutable_annotation(google_protobuf_GeneratedCodeInfo *msg, size_t *len) {
+  return (google_protobuf_GeneratedCodeInfo_Annotation**)_upb_array_mutable_accessor(msg, UPB_SIZE(0, 0), len);
+}
+UPB_INLINE google_protobuf_GeneratedCodeInfo_Annotation** google_protobuf_GeneratedCodeInfo_resize_annotation(google_protobuf_GeneratedCodeInfo *msg, size_t len, upb_arena *arena) {
+  return (google_protobuf_GeneratedCodeInfo_Annotation**)_upb_array_resize_accessor(msg, UPB_SIZE(0, 0), len, UPB_TYPE_MESSAGE, arena);
+}
+UPB_INLINE struct google_protobuf_GeneratedCodeInfo_Annotation* google_protobuf_GeneratedCodeInfo_add_annotation(google_protobuf_GeneratedCodeInfo *msg, upb_arena *arena) {
+  struct google_protobuf_GeneratedCodeInfo_Annotation* sub = (struct google_protobuf_GeneratedCodeInfo_Annotation*)_upb_msg_new(&google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena);
+  bool ok = _upb_array_append_accessor(
+      msg, UPB_SIZE(0, 0), UPB_SIZE(4, 8), UPB_TYPE_MESSAGE, &sub, arena);
+  if (!ok) return NULL;
+  return sub;
+}
+
+/* google.protobuf.GeneratedCodeInfo.Annotation */
+
+UPB_INLINE google_protobuf_GeneratedCodeInfo_Annotation *google_protobuf_GeneratedCodeInfo_Annotation_new(upb_arena *arena) {
+  return (google_protobuf_GeneratedCodeInfo_Annotation *)_upb_msg_new(&google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena);
+}
+UPB_INLINE google_protobuf_GeneratedCodeInfo_Annotation *google_protobuf_GeneratedCodeInfo_Annotation_parse(const char *buf, size_t size,
+                        upb_arena *arena) {
+  google_protobuf_GeneratedCodeInfo_Annotation *ret = google_protobuf_GeneratedCodeInfo_Annotation_new(arena);
+  return (ret && upb_decode(buf, size, ret, &google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena)) ? ret : NULL;
+}
+UPB_INLINE char *google_protobuf_GeneratedCodeInfo_Annotation_serialize(const google_protobuf_GeneratedCodeInfo_Annotation *msg, upb_arena *arena, size_t *len) {
+  return upb_encode(msg, &google_protobuf_GeneratedCodeInfo_Annotation_msginit, arena, len);
+}
+
+UPB_INLINE int32_t const* google_protobuf_GeneratedCodeInfo_Annotation_path(const google_protobuf_GeneratedCodeInfo_Annotation *msg, size_t *len) { return (int32_t const*)_upb_array_accessor(msg, UPB_SIZE(20, 32), len); }
+UPB_INLINE bool google_protobuf_GeneratedCodeInfo_Annotation_has_source_file(const google_protobuf_GeneratedCodeInfo_Annotation *msg) { return _upb_hasbit(msg, 3); }
+UPB_INLINE upb_strview google_protobuf_GeneratedCodeInfo_Annotation_source_file(const google_protobuf_GeneratedCodeInfo_Annotation *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(12, 16), upb_strview); }
+UPB_INLINE bool google_protobuf_GeneratedCodeInfo_Annotation_has_begin(const google_protobuf_GeneratedCodeInfo_Annotation *msg) { return _upb_hasbit(msg, 1); }
+UPB_INLINE int32_t google_protobuf_GeneratedCodeInfo_Annotation_begin(const google_protobuf_GeneratedCodeInfo_Annotation *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t); }
+UPB_INLINE bool google_protobuf_GeneratedCodeInfo_Annotation_has_end(const google_protobuf_GeneratedCodeInfo_Annotation *msg) { return _upb_hasbit(msg, 2); }
+UPB_INLINE int32_t google_protobuf_GeneratedCodeInfo_Annotation_end(const google_protobuf_GeneratedCodeInfo_Annotation *msg) { return *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t); }
+
+UPB_INLINE int32_t* google_protobuf_GeneratedCodeInfo_Annotation_mutable_path(google_protobuf_GeneratedCodeInfo_Annotation *msg, size_t *len) {
+  return (int32_t*)_upb_array_mutable_accessor(msg, UPB_SIZE(20, 32), len);
+}
+UPB_INLINE int32_t* google_protobuf_GeneratedCodeInfo_Annotation_resize_path(google_protobuf_GeneratedCodeInfo_Annotation *msg, size_t len, upb_arena *arena) {
+  return (int32_t*)_upb_array_resize_accessor(msg, UPB_SIZE(20, 32), len, UPB_TYPE_INT32, arena);
+}
+UPB_INLINE bool google_protobuf_GeneratedCodeInfo_Annotation_add_path(google_protobuf_GeneratedCodeInfo_Annotation *msg, int32_t val, upb_arena *arena) {
+  return _upb_array_append_accessor(msg, UPB_SIZE(20, 32), UPB_SIZE(4, 4), UPB_TYPE_INT32, &val,
+      arena);
+}
+UPB_INLINE void google_protobuf_GeneratedCodeInfo_Annotation_set_source_file(google_protobuf_GeneratedCodeInfo_Annotation *msg, upb_strview value) {
+  _upb_sethas(msg, 3);
+  *UPB_PTR_AT(msg, UPB_SIZE(12, 16), upb_strview) = value;
+}
+UPB_INLINE void google_protobuf_GeneratedCodeInfo_Annotation_set_begin(google_protobuf_GeneratedCodeInfo_Annotation *msg, int32_t value) {
+  _upb_sethas(msg, 1);
+  *UPB_PTR_AT(msg, UPB_SIZE(4, 4), int32_t) = value;
+}
+UPB_INLINE void google_protobuf_GeneratedCodeInfo_Annotation_set_end(google_protobuf_GeneratedCodeInfo_Annotation *msg, int32_t value) {
+  _upb_sethas(msg, 2);
+  *UPB_PTR_AT(msg, UPB_SIZE(8, 8), int32_t) = value;
+}
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+
+#endif  /* GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H_ */
+/*
+** Defs are upb's internal representation of the constructs that can appear
+** in a .proto file:
+**
+** - upb_msgdef: describes a "message" construct.
+** - upb_fielddef: describes a message field.
+** - upb_filedef: describes a .proto file and its defs.
+** - upb_enumdef: describes an enum.
+** - upb_oneofdef: describes a oneof.
+**
+** TODO: definitions of services.
+*/
+
+#ifndef UPB_DEF_H_
+#define UPB_DEF_H_
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif  /* __cplusplus */
+
+struct upb_enumdef;
+typedef struct upb_enumdef upb_enumdef;
+struct upb_fielddef;
+typedef struct upb_fielddef upb_fielddef;
+struct upb_filedef;
+typedef struct upb_filedef upb_filedef;
+struct upb_msgdef;
+typedef struct upb_msgdef upb_msgdef;
+struct upb_oneofdef;
+typedef struct upb_oneofdef upb_oneofdef;
+struct upb_symtab;
+typedef struct upb_symtab upb_symtab;
+
+typedef enum {
+  UPB_SYNTAX_PROTO2 = 2,
+  UPB_SYNTAX_PROTO3 = 3
+} upb_syntax_t;
+
+/* All the different kind of well known type messages. For simplicity of check,
+ * number wrappers and string wrappers are grouped together. Make sure the
+ * order and merber of these groups are not changed.
+ */
+typedef enum {
+  UPB_WELLKNOWN_UNSPECIFIED,
+  UPB_WELLKNOWN_ANY,
+  UPB_WELLKNOWN_FIELDMASK,
+  UPB_WELLKNOWN_DURATION,
+  UPB_WELLKNOWN_TIMESTAMP,
+  /* number wrappers */
+  UPB_WELLKNOWN_DOUBLEVALUE,
+  UPB_WELLKNOWN_FLOATVALUE,
+  UPB_WELLKNOWN_INT64VALUE,
+  UPB_WELLKNOWN_UINT64VALUE,
+  UPB_WELLKNOWN_INT32VALUE,
+  UPB_WELLKNOWN_UINT32VALUE,
+  /* string wrappers */
+  UPB_WELLKNOWN_STRINGVALUE,
+  UPB_WELLKNOWN_BYTESVALUE,
+  UPB_WELLKNOWN_BOOLVALUE,
+  UPB_WELLKNOWN_VALUE,
+  UPB_WELLKNOWN_LISTVALUE,
+  UPB_WELLKNOWN_STRUCT
+} upb_wellknowntype_t;
+
+/* upb_fielddef ***************************************************************/
+
+/* Maximum field number allowed for FieldDefs.  This is an inherent limit of the
+ * protobuf wire format. */
+#define UPB_MAX_FIELDNUMBER ((1 << 29) - 1)
+
+const char *upb_fielddef_fullname(const upb_fielddef *f);
+upb_fieldtype_t upb_fielddef_type(const upb_fielddef *f);
+upb_descriptortype_t upb_fielddef_descriptortype(const upb_fielddef *f);
+upb_label_t upb_fielddef_label(const upb_fielddef *f);
+uint32_t upb_fielddef_number(const upb_fielddef *f);
+const char *upb_fielddef_name(const upb_fielddef *f);
+const char *upb_fielddef_jsonname(const upb_fielddef *f);
+bool upb_fielddef_isextension(const upb_fielddef *f);
+bool upb_fielddef_lazy(const upb_fielddef *f);
+bool upb_fielddef_packed(const upb_fielddef *f);
+const upb_filedef *upb_fielddef_file(const upb_fielddef *f);
+const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f);
+const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f);
+const upb_oneofdef *upb_fielddef_realcontainingoneof(const upb_fielddef *f);
+uint32_t upb_fielddef_index(const upb_fielddef *f);
+bool upb_fielddef_issubmsg(const upb_fielddef *f);
+bool upb_fielddef_isstring(const upb_fielddef *f);
+bool upb_fielddef_isseq(const upb_fielddef *f);
+bool upb_fielddef_isprimitive(const upb_fielddef *f);
+bool upb_fielddef_ismap(const upb_fielddef *f);
+int64_t upb_fielddef_defaultint64(const upb_fielddef *f);
+int32_t upb_fielddef_defaultint32(const upb_fielddef *f);
+uint64_t upb_fielddef_defaultuint64(const upb_fielddef *f);
+uint32_t upb_fielddef_defaultuint32(const upb_fielddef *f);
+bool upb_fielddef_defaultbool(const upb_fielddef *f);
+float upb_fielddef_defaultfloat(const upb_fielddef *f);
+double upb_fielddef_defaultdouble(const upb_fielddef *f);
+const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len);
+bool upb_fielddef_hassubdef(const upb_fielddef *f);
+bool upb_fielddef_haspresence(const upb_fielddef *f);
+const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f);
+const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f);
+const upb_msglayout_field *upb_fielddef_layout(const upb_fielddef *f);
+
+/* Internal only. */
+uint32_t upb_fielddef_selectorbase(const upb_fielddef *f);
+
+/* upb_oneofdef ***************************************************************/
+
+typedef upb_inttable_iter upb_oneof_iter;
+
+const char *upb_oneofdef_name(const upb_oneofdef *o);
+const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o);
+int upb_oneofdef_numfields(const upb_oneofdef *o);
+uint32_t upb_oneofdef_index(const upb_oneofdef *o);
+bool upb_oneofdef_issynthetic(const upb_oneofdef *o);
+
+/* Oneof lookups:
+ * - ntof:  look up a field by name.
+ * - ntofz: look up a field by name (as a null-terminated string).
+ * - itof:  look up a field by number. */
+const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o,
+                                      const char *name, size_t length);
+UPB_INLINE const upb_fielddef *upb_oneofdef_ntofz(const upb_oneofdef *o,
+                                                  const char *name) {
+  return upb_oneofdef_ntof(o, name, strlen(name));
+}
+const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num);
+
+/*  upb_oneof_iter i;
+ *  for(upb_oneof_begin(&i, e); !upb_oneof_done(&i); upb_oneof_next(&i)) {
+ *    // ...
+ *  }
+ */
+void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o);
+void upb_oneof_next(upb_oneof_iter *iter);
+bool upb_oneof_done(upb_oneof_iter *iter);
+upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter);
+void upb_oneof_iter_setdone(upb_oneof_iter *iter);
+bool upb_oneof_iter_isequal(const upb_oneof_iter *iter1,
+                            const upb_oneof_iter *iter2);
+
+/* upb_msgdef *****************************************************************/
+
+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
+
+/* Well-known field tag numbers for Any messages. */
+#define UPB_ANY_TYPE 1
+#define UPB_ANY_VALUE 2
+
+/* Well-known field tag numbers for timestamp messages. */
+#define UPB_DURATION_SECONDS 1
+#define UPB_DURATION_NANOS 2
+
+/* Well-known field tag numbers for duration messages. */
+#define UPB_TIMESTAMP_SECONDS 1
+#define UPB_TIMESTAMP_NANOS 2
+
+const char *upb_msgdef_fullname(const upb_msgdef *m);
+const upb_filedef *upb_msgdef_file(const upb_msgdef *m);
+const char *upb_msgdef_name(const upb_msgdef *m);
+int upb_msgdef_numfields(const upb_msgdef *m);
+int upb_msgdef_numoneofs(const upb_msgdef *m);
+int upb_msgdef_numrealoneofs(const upb_msgdef *m);
+upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m);
+bool upb_msgdef_mapentry(const upb_msgdef *m);
+upb_wellknowntype_t upb_msgdef_wellknowntype(const upb_msgdef *m);
+bool upb_msgdef_iswrapper(const upb_msgdef *m);
+bool upb_msgdef_isnumberwrapper(const upb_msgdef *m);
+const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i);
+const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name,
+                                    size_t len);
+const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name,
+                                    size_t len);
+const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m);
+const upb_fielddef *_upb_msgdef_field(const upb_msgdef *m, int i);
+
+UPB_INLINE const upb_oneofdef *upb_msgdef_ntooz(const upb_msgdef *m,
+                                               const char *name) {
+  return upb_msgdef_ntoo(m, name, strlen(name));
+}
+
+UPB_INLINE const upb_fielddef *upb_msgdef_ntofz(const upb_msgdef *m,
+                                                const char *name) {
+  return upb_msgdef_ntof(m, name, strlen(name));
+}
+
+/* Internal-only. */
+size_t upb_msgdef_selectorcount(const upb_msgdef *m);
+uint32_t upb_msgdef_submsgfieldcount(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);
+
+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);
+}
+
+/* Returns a field by either JSON name or regular proto name. */
+const upb_fielddef *upb_msgdef_lookupjsonname(const upb_msgdef *m,
+                                              const char *name, size_t len);
+
+/* Iteration over fields and oneofs.  For example:
+ *
+ * upb_msg_field_iter i;
+ * for(upb_msg_field_begin(&i, m);
+ *     !upb_msg_field_done(&i);
+ *     upb_msg_field_next(&i)) {
+ *   upb_fielddef *f = upb_msg_iter_field(&i);
+ *   // ...
+ * }
+ *
+ * For C we don't have separate iterators for const and non-const.
+ * It is the caller's responsibility to cast the upb_fielddef* to
+ * const if the upb_msgdef* is const. */
+void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m);
+void upb_msg_field_next(upb_msg_field_iter *iter);
+bool upb_msg_field_done(const upb_msg_field_iter *iter);
+upb_fielddef *upb_msg_iter_field(const upb_msg_field_iter *iter);
+void upb_msg_field_iter_setdone(upb_msg_field_iter *iter);
+bool upb_msg_field_iter_isequal(const upb_msg_field_iter * iter1,
+                                const upb_msg_field_iter * iter2);
+
+/* Similar to above, we also support iterating through the oneofs in a
+ * msgdef. */
+void upb_msg_oneof_begin(upb_msg_oneof_iter * iter, const upb_msgdef *m);
+void upb_msg_oneof_next(upb_msg_oneof_iter * iter);
+bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter);
+const upb_oneofdef *upb_msg_iter_oneof(const upb_msg_oneof_iter *iter);
+void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter * iter);
+bool upb_msg_oneof_iter_isequal(const upb_msg_oneof_iter *iter1,
+                                const upb_msg_oneof_iter *iter2);
+
+/* upb_enumdef ****************************************************************/
+
+typedef upb_strtable_iter upb_enum_iter;
+
+const char *upb_enumdef_fullname(const upb_enumdef *e);
+const char *upb_enumdef_name(const upb_enumdef *e);
+const upb_filedef *upb_enumdef_file(const upb_enumdef *e);
+int32_t upb_enumdef_default(const upb_enumdef *e);
+int upb_enumdef_numvals(const upb_enumdef *e);
+
+/* Enum lookups:
+ * - ntoi:  look up a name with specified length.
+ * - ntoiz: look up a name provided as a null-terminated string.
+ * - iton:  look up an integer, returning the name as a null-terminated
+ *          string. */
+bool upb_enumdef_ntoi(const upb_enumdef *e, const char *name, size_t len,
+                      int32_t *num);
+UPB_INLINE bool upb_enumdef_ntoiz(const upb_enumdef *e,
+                                  const char *name, int32_t *num) {
+  return upb_enumdef_ntoi(e, name, strlen(name), num);
+}
+const char *upb_enumdef_iton(const upb_enumdef *e, int32_t num);
+
+/*  upb_enum_iter i;
+ *  for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) {
+ *    // ...
+ *  }
+ */
+void upb_enum_begin(upb_enum_iter *iter, const upb_enumdef *e);
+void upb_enum_next(upb_enum_iter *iter);
+bool upb_enum_done(upb_enum_iter *iter);
+const char *upb_enum_iter_name(upb_enum_iter *iter);
+int32_t upb_enum_iter_number(upb_enum_iter *iter);
+
+/* upb_filedef ****************************************************************/
+
+const char *upb_filedef_name(const upb_filedef *f);
+const char *upb_filedef_package(const upb_filedef *f);
+const char *upb_filedef_phpprefix(const upb_filedef *f);
+const char *upb_filedef_phpnamespace(const upb_filedef *f);
+upb_syntax_t upb_filedef_syntax(const upb_filedef *f);
+int upb_filedef_depcount(const upb_filedef *f);
+int upb_filedef_msgcount(const upb_filedef *f);
+int upb_filedef_enumcount(const upb_filedef *f);
+const upb_filedef *upb_filedef_dep(const upb_filedef *f, int i);
+const upb_msgdef *upb_filedef_msg(const upb_filedef *f, int i);
+const upb_enumdef *upb_filedef_enum(const upb_filedef *f, int i);
+
+/* upb_symtab *****************************************************************/
+
+upb_symtab *upb_symtab_new(void);
+void upb_symtab_free(upb_symtab* s);
+const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym);
+const upb_msgdef *upb_symtab_lookupmsg2(
+    const upb_symtab *s, const char *sym, size_t len);
+const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym);
+const upb_filedef *upb_symtab_lookupfile(const upb_symtab *s, const char *name);
+const upb_filedef *upb_symtab_lookupfile2(
+    const upb_symtab *s, const char *name, size_t len);
+int upb_symtab_filecount(const upb_symtab *s);
+const upb_filedef *upb_symtab_addfile(
+    upb_symtab *s, const google_protobuf_FileDescriptorProto *file,
+    upb_status *status);
+
+/* For generated code only: loads a generated descriptor. */
+typedef struct upb_def_init {
+  struct upb_def_init **deps;     /* Dependencies of this file. */
+  const upb_msglayout **layouts;  /* Pre-order layouts of all messages. */
+  const char *filename;
+  upb_strview descriptor;         /* Serialized descriptor. */
+} upb_def_init;
+
+bool _upb_symtab_loaddefinit(upb_symtab *s, const upb_def_init *init);
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif  /* __cplusplus */
+
+#endif /* UPB_DEF_H_ */
+/* This file was generated by upbc (the upb compiler) from the input
+ * file:
+ *
+ *     google/protobuf/descriptor.proto
+ *
+ * Do not edit -- your changes will be discarded when the file is
+ * regenerated. */
+
+#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPBDEFS_H_
+#define GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPBDEFS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+extern upb_def_init google_protobuf_descriptor_proto_upbdefinit;
+
+UPB_INLINE const upb_msgdef *google_protobuf_FileDescriptorSet_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorSet");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_FileDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_DescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_DescriptorProto_ExtensionRange_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ExtensionRange");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_DescriptorProto_ReservedRange_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ReservedRange");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_ExtensionRangeOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.ExtensionRangeOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_FieldDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.FieldDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_OneofDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.OneofDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_EnumDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_EnumDescriptorProto_EnumReservedRange_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto.EnumReservedRange");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_EnumValueDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.EnumValueDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_ServiceDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.ServiceDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_MethodDescriptorProto_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.MethodDescriptorProto");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_FileOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.FileOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_MessageOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.MessageOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_FieldOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.FieldOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_OneofOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.OneofOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_EnumOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.EnumOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_EnumValueOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.EnumValueOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_ServiceOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.ServiceOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_MethodOptions_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.MethodOptions");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_UninterpretedOption_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_UninterpretedOption_NamePart_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption.NamePart");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_SourceCodeInfo_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_SourceCodeInfo_Location_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo.Location");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_GeneratedCodeInfo_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.GeneratedCodeInfo");
+}
+
+UPB_INLINE const upb_msgdef *google_protobuf_GeneratedCodeInfo_Annotation_getmsgdef(upb_symtab *s) {
+  _upb_symtab_loaddefinit(s, &google_protobuf_descriptor_proto_upbdefinit);
+  return upb_symtab_lookupmsg(s, "google.protobuf.GeneratedCodeInfo.Annotation");
+}
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+
+#endif  /* GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPBDEFS_H_ */
+
+#ifndef UPB_REFLECTION_H_
+#define UPB_REFLECTION_H_
+
+
+
+typedef union {
+  bool bool_val;
+  float float_val;
+  double double_val;
+  int32_t int32_val;
+  int64_t int64_val;
+  uint32_t uint32_val;
+  uint64_t uint64_val;
+  const upb_map* map_val;
+  const upb_msg* msg_val;
+  const upb_array* array_val;
+  upb_strview str_val;
+} upb_msgval;
+
+typedef union {
+  upb_map* map;
+  upb_msg* msg;
+  upb_array* array;
+} upb_mutmsgval;
+
+/** upb_msg *******************************************************************/
+
+/* Creates a new message of the given type in the given arena. */
+upb_msg *upb_msg_new(const upb_msgdef *m, upb_arena *a);
+
+/* Returns the value associated with this field. */
+upb_msgval upb_msg_get(const upb_msg *msg, const upb_fielddef *f);
+
+/* Returns a mutable pointer to a map, array, or submessage value.  If the given
+ * arena is non-NULL this will construct a new object if it was not previously
+ * present.  May not be called for primitive fields. */
+upb_mutmsgval upb_msg_mutable(upb_msg *msg, const upb_fielddef *f, upb_arena *a);
+
+/* May only be called for fields where upb_fielddef_haspresence(f) == true. */
+bool upb_msg_has(const upb_msg *msg, const upb_fielddef *f);
+
+/* Returns the field that is set in the oneof, or NULL if none are set. */
+const upb_fielddef *upb_msg_whichoneof(const upb_msg *msg,
+                                       const upb_oneofdef *o);
+
+/* Sets the given field to the given value.  For a msg/array/map/string, the
+ * value must be in the same arena.  */
+void upb_msg_set(upb_msg *msg, const upb_fielddef *f, upb_msgval val,
+                 upb_arena *a);
+
+/* Clears any field presence and sets the value back to its default. */
+void upb_msg_clearfield(upb_msg *msg, const upb_fielddef *f);
+
+/* Clear all data and unknown fields. */
+void upb_msg_clear(upb_msg *msg, const upb_msgdef *m);
+
+/* Iterate over present fields.
+ *
+ * size_t iter = UPB_MSG_BEGIN;
+ * const upb_fielddef *f;
+ * upb_msgval val;
+ * while (upb_msg_next(msg, m, ext_pool, &f, &val, &iter)) {
+ *   process_field(f, val);
+ * }
+ *
+ * If ext_pool is NULL, no extensions will be returned.  If the given symtab
+ * returns extensions that don't match what is in this message, those extensions
+ * will be skipped.
+ */
+
+#define UPB_MSG_BEGIN -1
+bool upb_msg_next(const upb_msg *msg, const upb_msgdef *m,
+                  const upb_symtab *ext_pool, const upb_fielddef **f,
+                  upb_msgval *val, size_t *iter);
+
+/* Adds unknown data (serialized protobuf data) to the given message.  The data
+ * is copied into the message instance. */
+void upb_msg_addunknown(upb_msg *msg, const char *data, size_t len,
+                        upb_arena *arena);
+
+/* Clears all unknown field data from this message and all submessages. */
+bool upb_msg_discardunknown(upb_msg *msg, const upb_msgdef *m, int maxdepth);
+
+/* Returns a reference to the message's unknown data. */
+const char *upb_msg_getunknown(const upb_msg *msg, size_t *len);
+
+/** upb_array *****************************************************************/
+
+/* Creates a new array on the given arena that holds elements of this type. */
+upb_array *upb_array_new(upb_arena *a, upb_fieldtype_t type);
+
+/* Returns the size of the array. */
+size_t upb_array_size(const upb_array *arr);
+
+/* Returns the given element, which must be within the array's current size. */
+upb_msgval upb_array_get(const upb_array *arr, size_t i);
+
+/* Sets the given element, which must be within the array's current size. */
+void upb_array_set(upb_array *arr, size_t i, upb_msgval val);
+
+/* Appends an element to the array.  Returns false on allocation failure. */
+bool upb_array_append(upb_array *array, upb_msgval val, upb_arena *arena);
+
+/* Changes the size of a vector.  New elements are initialized to empty/0.
+ * Returns false on allocation failure. */
+bool upb_array_resize(upb_array *array, size_t size, upb_arena *arena);
+
+/** upb_map *******************************************************************/
+
+/* Creates a new map on the given arena with the given key/value size. */
+upb_map *upb_map_new(upb_arena *a, upb_fieldtype_t key_type,
+                     upb_fieldtype_t value_type);
+
+/* Returns the number of entries in the map. */
+size_t upb_map_size(const upb_map *map);
+
+/* Stores a value for the given key into |*val| (or the zero value if the key is
+ * not present).  Returns whether the key was present.  The |val| pointer may be
+ * NULL, in which case the function tests whether the given key is present.  */
+bool upb_map_get(const upb_map *map, upb_msgval key, upb_msgval *val);
+
+/* Removes all entries in the map. */
+void upb_map_clear(upb_map *map);
+
+/* Sets the given key to the given value.  Returns true if this was a new key in
+ * the map, or false if an existing key was replaced. */
+bool upb_map_set(upb_map *map, upb_msgval key, upb_msgval val,
+                 upb_arena *arena);
+
+/* Deletes this key from the table.  Returns true if the key was present. */
+bool upb_map_delete(upb_map *map, upb_msgval key);
+
+/* Map iteration:
+ *
+ * size_t iter = UPB_MAP_BEGIN;
+ * while (upb_mapiter_next(map, &iter)) {
+ *   upb_msgval key = upb_mapiter_key(map, iter);
+ *   upb_msgval val = upb_mapiter_value(map, iter);
+ *
+ *   // If mutating is desired.
+ *   upb_mapiter_setvalue(map, iter, value2);
+ * }
+ */
+
+/* Advances to the next entry.  Returns false if no more entries are present. */
+bool upb_mapiter_next(const upb_map *map, size_t *iter);
+
+/* Returns true if the iterator still points to a valid entry, or false if the
+ * iterator is past the last element. It is an error to call this function with
+ * UPB_MAP_BEGIN (you must call next() at least once first). */
+bool upb_mapiter_done(const upb_map *map, size_t iter);
+
+/* Returns the key and value for this entry of the map. */
+upb_msgval upb_mapiter_key(const upb_map *map, size_t iter);
+upb_msgval upb_mapiter_value(const upb_map *map, size_t iter);
+
+/* Sets the value for this entry.  The iterator must not be done, and the
+ * iterator must not have been initialized const. */
+void upb_mapiter_setvalue(upb_map *map, size_t iter, upb_msgval value);
+
+
+#endif /* UPB_REFLECTION_H_ */
+
+#ifndef UPB_JSONDECODE_H_
+#define UPB_JSONDECODE_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+  UPB_JSONDEC_IGNOREUNKNOWN = 1
+};
+
+bool upb_json_decode(const char *buf, size_t size, upb_msg *msg,
+                     const upb_msgdef *m, const upb_symtab *any_pool,
+                     int options, upb_arena *arena, upb_status *status);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* UPB_JSONDECODE_H_ */
+
+#ifndef UPB_JSONENCODE_H_
+#define UPB_JSONENCODE_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+  /* When set, emits 0/default values.  TOOD(haberman): proto3 only? */
+  UPB_JSONENC_EMITDEFAULTS = 1,
+
+  /* When set, use normal (snake_caes) field names instead of JSON (camelCase)
+     names. */
+  UPB_JSONENC_PROTONAMES = 2
+};
+
+/* Encodes the given |msg| to JSON format.  The message's reflection is given in
+ * |m|.  The symtab in |symtab| is used to find extensions (if NULL, extensions
+ * will not be printed).
+ *
+ * Output is placed in the given buffer, and always NULL-terminated.  The output
+ * size (excluding NULL) is returned.  This means that a return value >= |size|
+ * implies that the output was truncated.  (These are the same semantics as
+ * snprintf()). */
+size_t upb_json_encode(const upb_msg *msg, const upb_msgdef *m,
+                       const upb_symtab *ext_pool, int options, char *buf,
+                       size_t size, upb_status *status);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* UPB_JSONENCODE_H_ */
+/* See port_def.inc.  This should #undef all macros #defined there. */
+
+#undef UPB_MAPTYPE_STRING
+#undef UPB_SIZE
+#undef UPB_PTR_AT
+#undef UPB_READ_ONEOF
+#undef UPB_WRITE_ONEOF
+#undef UPB_INLINE
+#undef UPB_ALIGN_UP
+#undef UPB_ALIGN_DOWN
+#undef UPB_ALIGN_MALLOC
+#undef UPB_ALIGN_OF
+#undef UPB_FORCEINLINE
+#undef UPB_NOINLINE
+#undef UPB_NORETURN
+#undef UPB_MAX
+#undef UPB_MIN
+#undef UPB_UNUSED
+#undef UPB_ASSUME
+#undef UPB_ASSERT
+#undef UPB_ASSERT_DEBUGVAR
+#undef UPB_UNREACHABLE
+#undef UPB_INFINITY
+#undef UPB_MSVC_VSNPRINTF
+#undef _upb_snprintf
+#undef _upb_vsnprintf
+#undef _upb_va_copy
diff --git a/php/ext/google/protobuf2/protobuf.c b/php/ext/google/protobuf2/protobuf.c
new file mode 100644
index 0000000..15c8f9b
--- /dev/null
+++ b/php/ext/google/protobuf2/protobuf.c
@@ -0,0 +1,349 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "protobuf.h"
+
+#include <php.h>
+#include <Zend/zend_interfaces.h>
+
+#include "arena.h"
+#include "array.h"
+#include "bundled_php.h"
+#include "convert.h"
+#include "def.h"
+#include "map.h"
+#include "message.h"
+#include "names.h"
+
+// -----------------------------------------------------------------------------
+// Module "globals"
+// -----------------------------------------------------------------------------
+
+// Despite the name, module "globals" are really thread-locals:
+//  * PROTOBUF_G(var) accesses the thread-local variable for 'var'. Either:
+//    * PROTOBUF_G(var) -> protobuf_globals.var (Non-ZTS / non-thread-safe)
+//    * PROTOBUF_G(var) -> <Zend magic>         (ZTS / thread-safe builds)
+
+#define PROTOBUF_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(protobuf, v)
+
+ZEND_BEGIN_MODULE_GLOBALS(protobuf)
+  // Set by the user to make the descriptor pool persist between requests.
+  zend_bool keep_descriptor_pool_after_request;
+
+  // Currently we make the generated pool a "global", which means that if a user
+  // does explicitly create threads within their request, the other threads will
+  // get different results from DescriptorPool::getGeneratedPool(). We require
+  // that all descriptors are loaded from the main thread.
+  zval generated_pool;
+
+  // A upb_symtab that we are saving for the next request so that we don't have
+  // to rebuild it from scratch. When keep_descriptor_pool_after_request==true,
+  // we steal the upb_symtab from the global DescriptorPool object just before
+  // destroying it.
+  upb_symtab *saved_symtab;
+
+  // Object cache (see interface in protobuf.h).
+  HashTable object_cache;
+
+  // Name cache (see interface in protobuf.h).
+  HashTable name_msg_cache;
+  HashTable name_enum_cache;
+ZEND_END_MODULE_GLOBALS(protobuf)
+
+ZEND_DECLARE_MODULE_GLOBALS(protobuf)
+
+const zval *get_generated_pool() {
+  return &PROTOBUF_G(generated_pool);
+}
+
+// This is a PHP extension (not a Zend extension). What follows is a summary of
+// a PHP extension's lifetime and when various handlers are called.
+//
+//  * PHP_GINIT_FUNCTION(protobuf) / PHP_GSHUTDOWN_FUNCTION(protobuf)
+//    are the constructor/destructor for the globals. The sequence over the
+//    course of a process lifetime is:
+//
+//    # Process startup
+//    GINIT(<Main Thread Globals>)
+//    MINIT
+//
+//    foreach request:
+//      RINIT
+//        # Request is processed here.
+//      RSHUTDOWN
+//
+//    foreach thread:
+//      GINIT(<This Thread Globals>)
+//        # Code for the thread runs here.
+//      GSHUTDOWN(<This Thread Globals>)
+//
+//    # Process Shutdown
+//    #
+//    # These should be running per the docs, but I have not been able to
+//    # actually get the process-wide shutdown functions to run.
+//    #
+//    # MSHUTDOWN
+//    # GSHUTDOWN(<Main Thread Globals>)
+//
+//  * Threads can be created either explicitly by the user, inside a request,
+//    or implicitly by the runtime, to process multiple requests concurrently.
+//    If the latter is being used, then the "foreach thread" block above
+//    actually looks like this:
+//
+//    foreach thread:
+//      GINIT(<This Thread Globals>)
+//      # A non-main thread will only receive requests when using a threaded
+//      # MPM with Apache
+//      foreach request:
+//        RINIT
+//          # Request is processed here.
+//        RSHUTDOWN
+//      GSHUTDOWN(<This Thread Globals>)
+//
+// That said, it appears that few people use threads with PHP:
+//   * The pthread package documented at
+//     https://www.php.net/manual/en/class.thread.php nas not been released
+//     since 2016, and the current release fails to compile against any PHP
+//     newer than 7.0.33.
+//     * The GitHub master branch supports 7.2+, but this has not been released
+//       to PECL.
+//     * Its owner has disavowed it as "broken by design" and "in an untenable
+//       position for the future": https://github.com/krakjoe/pthreads/issues/929
+//   * The only way to use PHP with requests in different threads is to use the
+//     Apache 2 mod_php with the "worker" MPM. But this is explicitly
+//     discouraged by the documentation: https://serverfault.com/a/231660
+
+static PHP_GSHUTDOWN_FUNCTION(protobuf) {
+  if (protobuf_globals->saved_symtab) {
+    upb_symtab_free(protobuf_globals->saved_symtab);
+  }
+}
+
+static PHP_GINIT_FUNCTION(protobuf) {
+  ZVAL_NULL(&protobuf_globals->generated_pool);
+  protobuf_globals->saved_symtab = NULL;
+}
+
+/**
+ * PHP_RINIT_FUNCTION(protobuf)
+ *
+ * This function is run at the beginning of processing each request.
+ */
+static PHP_RINIT_FUNCTION(protobuf) {
+  // Create the global generated pool.
+  // Reuse the symtab (if any) left to us by the last request.
+  upb_symtab *symtab = PROTOBUF_G(saved_symtab);
+  DescriptorPool_CreateWithSymbolTable(&PROTOBUF_G(generated_pool), symtab);
+
+  // Set up autoloader for bundled sources.
+  zend_eval_string("spl_autoload_register('protobuf_internal_loadbundled');",
+                   NULL, "autoload_register.php");
+
+  zend_hash_init(&PROTOBUF_G(object_cache), 64, NULL, NULL, 0);
+  zend_hash_init(&PROTOBUF_G(name_msg_cache), 64, NULL, NULL, 0);
+  zend_hash_init(&PROTOBUF_G(name_enum_cache), 64, NULL, NULL, 0);
+
+  return SUCCESS;
+}
+
+/**
+ * PHP_RSHUTDOWN_FUNCTION(protobuf)
+ *
+ * This function is run at the end of processing each request.
+ */
+static PHP_RSHUTDOWN_FUNCTION(protobuf) {
+  // Preserve the symtab if requested.
+  if (PROTOBUF_G(keep_descriptor_pool_after_request)) {
+    zval *zv = &PROTOBUF_G(generated_pool);
+    PROTOBUF_G(saved_symtab) = DescriptorPool_Steal(zv);
+  }
+
+  zval_dtor(&PROTOBUF_G(generated_pool));
+  zend_hash_destroy(&PROTOBUF_G(object_cache));
+  zend_hash_destroy(&PROTOBUF_G(name_msg_cache));
+  zend_hash_destroy(&PROTOBUF_G(name_enum_cache));
+
+  return SUCCESS;
+}
+
+// -----------------------------------------------------------------------------
+// Bundled PHP sources
+// -----------------------------------------------------------------------------
+
+// We bundle PHP sources for well-known types into the C extension. There is no
+// need to implement these in C.
+
+static PHP_FUNCTION(protobuf_internal_loadbundled) {
+  char *name = NULL;
+  zend_long size;
+  BundledPhp_File *file;
+
+  if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &size) != SUCCESS) {
+    return;
+  }
+
+  for (file = bundled_files; file->filename; file++) {
+    if (strcmp(file->filename, name) == 0) {
+      zend_eval_string((char*)file->contents, NULL, (char*)file->filename);
+      return;
+    }
+  }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_load_bundled_source, 0, 0, 1)
+  ZEND_ARG_INFO(0, class_name)
+ZEND_END_ARG_INFO()
+
+// -----------------------------------------------------------------------------
+// Object Cache.
+// -----------------------------------------------------------------------------
+
+void ObjCache_Add(const void *upb_obj, zend_object *php_obj) {
+  zend_ulong k = (zend_ulong)upb_obj;
+  zend_hash_index_add_ptr(&PROTOBUF_G(object_cache), k, php_obj);
+}
+
+void ObjCache_Delete(const void *upb_obj) {
+  if (upb_obj) {
+    zend_ulong k = (zend_ulong)upb_obj;
+    int ret = zend_hash_index_del(&PROTOBUF_G(object_cache), k);
+    PBPHP_ASSERT(ret == SUCCESS);
+  }
+}
+
+bool ObjCache_Get(const void *upb_obj, zval *val) {
+  zend_ulong k = (zend_ulong)upb_obj;
+  zend_object *obj = zend_hash_index_find_ptr(&PROTOBUF_G(object_cache), k);
+
+  if (obj) {
+    GC_ADDREF(obj);
+    ZVAL_OBJ(val, obj);
+    return true;
+  } else {
+    ZVAL_NULL(val);
+    return false;
+  }
+}
+
+// -----------------------------------------------------------------------------
+// Name Cache.
+// -----------------------------------------------------------------------------
+
+void NameMap_AddMessage(const upb_msgdef *m) {
+  char *k = GetPhpClassname(upb_msgdef_file(m), upb_msgdef_fullname(m));
+  zend_hash_str_add_ptr(&PROTOBUF_G(name_msg_cache), k, strlen(k), (void*)m);
+  free(k);
+}
+
+void NameMap_AddEnum(const upb_enumdef *e) {
+  char *k = GetPhpClassname(upb_enumdef_file(e), upb_enumdef_fullname(e));
+  zend_hash_str_add_ptr(&PROTOBUF_G(name_enum_cache), k, strlen(k), (void*)e);
+  free(k);
+}
+
+const upb_msgdef *NameMap_GetMessage(zend_class_entry *ce) {
+  const upb_msgdef *ret =
+      zend_hash_find_ptr(&PROTOBUF_G(name_msg_cache), ce->name);
+
+  if (!ret && ce->create_object) {
+    zval tmp;
+    zval zv;
+    ZVAL_OBJ(&tmp, ce->create_object(ce));
+    zend_call_method_with_0_params(&tmp, ce, NULL, "__construct", &zv);
+    zval_ptr_dtor(&tmp);
+    zval_ptr_dtor(&zv);
+    ret = zend_hash_find_ptr(&PROTOBUF_G(name_msg_cache), ce->name);
+  }
+
+  return ret;
+}
+
+const upb_enumdef *NameMap_GetEnum(zend_class_entry *ce) {
+  const upb_enumdef *ret =
+      zend_hash_find_ptr(&PROTOBUF_G(name_enum_cache), ce->name);
+  return ret;
+}
+
+// -----------------------------------------------------------------------------
+// Module init.
+// -----------------------------------------------------------------------------
+
+zend_function_entry protobuf_functions[] = {
+  PHP_FE(protobuf_internal_loadbundled, arginfo_load_bundled_source)
+  ZEND_FE_END
+};
+
+static const zend_module_dep protobuf_deps[] = {
+  ZEND_MOD_OPTIONAL("date")
+  ZEND_MOD_END
+};
+
+PHP_INI_BEGIN()
+STD_PHP_INI_ENTRY("protobuf.keep_descriptor_pool_after_request", "0",
+                  PHP_INI_SYSTEM, OnUpdateBool,
+                  keep_descriptor_pool_after_request, zend_protobuf_globals,
+                  protobuf_globals)
+PHP_INI_END()
+
+static PHP_MINIT_FUNCTION(protobuf) {
+  REGISTER_INI_ENTRIES();
+  Arena_ModuleInit();
+  Array_ModuleInit();
+  Convert_ModuleInit();
+  Def_ModuleInit();
+  Map_ModuleInit();
+  Message_ModuleInit();
+  return SUCCESS;
+}
+
+static PHP_MSHUTDOWN_FUNCTION(protobuf) {
+  return SUCCESS;
+}
+
+zend_module_entry protobuf_module_entry = {
+  STANDARD_MODULE_HEADER_EX,
+  NULL,
+  protobuf_deps,
+  "protobuf",               // extension name
+  protobuf_functions,       // function list
+  PHP_MINIT(protobuf),      // process startup
+  PHP_MSHUTDOWN(protobuf),  // process shutdown
+  PHP_RINIT(protobuf),      // request shutdown
+  PHP_RSHUTDOWN(protobuf),  // request shutdown
+  NULL,                     // extension info
+  "3.13.0",                 // extension version
+  PHP_MODULE_GLOBALS(protobuf),  // globals descriptor
+  PHP_GINIT(protobuf),      // globals ctor
+  PHP_GSHUTDOWN(protobuf),  // globals dtor
+  NULL,                     // post deactivate
+  STANDARD_MODULE_PROPERTIES_EX
+};
+
+ZEND_GET_MODULE(protobuf)
diff --git a/php/ext/google/protobuf2/protobuf.h b/php/ext/google/protobuf2/protobuf.h
new file mode 100644
index 0000000..6a7afae
--- /dev/null
+++ b/php/ext/google/protobuf2/protobuf.h
@@ -0,0 +1,89 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PHP_PROTOBUF_H_
+#define PHP_PROTOBUF_H_
+
+#include <php.h>
+#include <stdbool.h>
+
+#include "php-upb.h"
+
+const zval *get_generated_pool();
+
+#if PHP_VERSION_ID < 70300
+#define GC_ADDREF(h) ++GC_REFCOUNT(h)
+#define GC_DELREF(h) --GC_REFCOUNT(h)
+#endif
+
+// ptr -> PHP object cache. This is a weak map that caches lazily-created
+// wrapper objects around upb types:
+//  * upb_msg* -> Message
+//  * upb_array* -> RepeatedField
+//  * upb_map*, -> MapField
+//  * upb_msgdef* -> Descriptor
+//  * upb_enumdef* -> EnumDescriptor
+//  * zend_class_entry* -> Descriptor
+//
+// Each wrapped object should add itself to the map when it is constructed, and
+// remove itself from the map when it is destroyed. This is how we ensure that
+// the map only contains live objects. The map is weak so it does not actually
+// take references to the cached objects.
+void ObjCache_Add(const void *key, zend_object *php_obj);
+void ObjCache_Delete(const void *key);
+bool ObjCache_Get(const void *key, zval *val);
+
+// PHP class name map. This is necessary because the pb_name->php_class_name
+// transformation is non-reversible, so when we need to look up a msgdef or
+// enumdef by PHP class, we can't turn the class name into a pb_name.
+//  * php_class_name -> upb_msgdef*
+//  * php_class_name -> upb_enumdef*
+void NameMap_AddMessage(const upb_msgdef *m);
+void NameMap_AddEnum(const upb_enumdef *m);
+const upb_msgdef *NameMap_GetMessage(zend_class_entry *ce);
+const upb_enumdef *NameMap_GetEnum(zend_class_entry *ce);
+
+// We need our own assert() because PHP takes control of NDEBUG in its headers.
+#ifdef PBPHP_ENABLE_ASSERTS
+#define PBPHP_ASSERT(x)                                                    \
+  do {                                                                     \
+    if (!(x)) {                                                            \
+      fprintf(stderr, "Assertion failure at %s:%d %s", __FILE__, __LINE__, \
+              #x);                                                         \
+      abort();                                                             \
+    }                                                                      \
+  } while (false)
+#else
+#define PBPHP_ASSERT(x) \
+  do {                  \
+  } while (false && (x))
+#endif
+
+#endif  // PHP_PROTOBUF_H_
diff --git a/php/src/Google/Protobuf/Any.php b/php/src/Google/Protobuf/Any.php
index 166c66a..2c7bc75 100644
--- a/php/src/Google/Protobuf/Any.php
+++ b/php/src/Google/Protobuf/Any.php
@@ -270,7 +270,7 @@
 
         // Create message according to fully qualified name.
         $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
-        $desc = $pool->getDescriptorByProtoName( ".".$fully_qualifed_name);
+        $desc = $pool->getDescriptorByProtoName($fully_qualifed_name);
         if (is_null($desc)) {
             throw new \Exception("Class ".$fully_qualifed_name
                                      ." hasn't been added to descriptor pool");
@@ -303,8 +303,7 @@
         $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
         $desc = $pool->getDescriptorByClassName(get_class($msg));
         $fully_qualifed_name = $desc->getFullName();
-        $this->type_url = GPBUtil::TYPE_URL_PREFIX.substr(
-            $fully_qualifed_name, 1, strlen($fully_qualifed_name));
+        $this->type_url = GPBUtil::TYPE_URL_PREFIX . $fully_qualifed_name;
     }
 
     /**
@@ -317,8 +316,7 @@
         $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
         $desc = $pool->getDescriptorByClassName($klass);
         $fully_qualifed_name = $desc->getFullName();
-        $type_url = GPBUtil::TYPE_URL_PREFIX.substr(
-            $fully_qualifed_name, 1, strlen($fully_qualifed_name));
+        $type_url = GPBUtil::TYPE_URL_PREFIX . $fully_qualifed_name;
         return $this->type_url === $type_url;
     }
 }
diff --git a/php/src/Google/Protobuf/Internal/DescriptorPool.php b/php/src/Google/Protobuf/Internal/DescriptorPool.php
index f96b0fb..2880cee 100644
--- a/php/src/Google/Protobuf/Internal/DescriptorPool.php
+++ b/php/src/Google/Protobuf/Internal/DescriptorPool.php
@@ -152,6 +152,9 @@
             switch ($field->getType()) {
                 case GPBType::MESSAGE:
                     $proto = $field->getMessageType();
+                    if ($proto[0] == '.') {
+                      $proto = substr($proto, 1);
+                    }
                     $subdesc = $this->getDescriptorByProtoName($proto);
                     if (is_null($subdesc)) {
                         trigger_error(
@@ -162,6 +165,9 @@
                     break;
                 case GPBType::ENUM:
                     $proto = $field->getEnumType();
+                    if ($proto[0] == '.') {
+                      $proto = substr($proto, 1);
+                    }
                     $field->setEnumType(
                         $this->getEnumDescriptorByProtoName($proto));
                     break;
diff --git a/php/src/Google/Protobuf/Internal/GPBUtil.php b/php/src/Google/Protobuf/Internal/GPBUtil.php
index c091fc6..1900e71 100644
--- a/php/src/Google/Protobuf/Internal/GPBUtil.php
+++ b/php/src/Google/Protobuf/Internal/GPBUtil.php
@@ -338,9 +338,9 @@
 
         $package = $file_proto->getPackage();
         if ($package === "") {
-            $fullname = "." . $message_name_without_package;
+            $fullname = $message_name_without_package;
         } else {
-            $fullname = "." . $package . "." . $message_name_without_package;
+            $fullname = $package . "." . $message_name_without_package;
         }
 
         $class_name_without_package =
diff --git a/php/tests/compile_extension.sh b/php/tests/compile_extension.sh
index ae0a6a3..37c6810 100755
--- a/php/tests/compile_extension.sh
+++ b/php/tests/compile_extension.sh
@@ -12,7 +12,18 @@
   CFLAGS="-g -O0 -Wall"
 fi
 
+
 pushd  ../ext/google/protobuf
-make clean || true
+phpize --clean
+rm -f configure.in configure.ac
 phpize && ./configure --with-php-config=$(which php-config) CFLAGS="$CFLAGS" && make
 popd
+
+if [ $(php -r "echo PHP_VERSION;" | cut -c1) != "5" ]; then
+  pushd  ../ext/google/protobuf2
+  phpize --clean
+  rm -f configure.in configure.ac
+  php make-preload.php
+  phpize && ./configure --with-php-config=$(which php-config) CFLAGS="$CFLAGS" && make
+  popd
+fi
diff --git a/php/tests/test.sh b/php/tests/test.sh
index bb2ada3..b1d8d5f 100755
--- a/php/tests/test.sh
+++ b/php/tests/test.sh
@@ -8,12 +8,25 @@
 ./compile_extension.sh
 
 PHP_VERSION=$(php -r "echo PHP_VERSION;")
+TEST_NEW_EXTENSION=true
 
 # Each version of PHPUnit supports a fairly narrow range of PHP versions.
 case "$PHP_VERSION" in
-  5.6.*) PHPUNIT=phpunit-5.6.8.phar;;
-  7.0.*) PHPUNIT=phpunit-5.6.0.phar;;  # Oddly older than for 5.6. Not sure the reason.
-  7.3.*) PHPUNIT=phpunit-8.phar;;
+  5.*.*)
+    PHPUNIT=phpunit-5.6.8.phar
+    TEST_NEW_EXTENSION=false
+    ;;
+  7.0.*|7.1.*|7.2.*)
+    # Oddly older than for 5.6. Not sure the reason.
+    PHPUNIT=phpunit-5.6.0.phar
+    ;;
+  7.3.*|7.4.*)
+    PHPUNIT=phpunit-8.phar
+    ;;
+  *)
+    echo "ERROR: Unknown PHP version $PHP_VERSION"
+    exit 1
+    ;;
 esac
 
 [ -f $PHPUNIT ] || wget https://phar.phpunit.de/$PHPUNIT
@@ -26,6 +39,9 @@
   echo "* $t"
   echo "****************************"
   php -dextension=../ext/google/protobuf/modules/protobuf.so $PHPUNIT --bootstrap autoload.php $t
+  if [ "$TEST_NEW_EXTENSION" = "true" ]; then
+    php -dextension=../ext/google/protobuf2/modules/protobuf.so $PHPUNIT --bootstrap autoload.php $t
+  fi
   echo ""
 done
 
@@ -35,6 +51,9 @@
   echo "* $t persistent"
   echo "****************************"
   php -d protobuf.keep_descriptor_pool_after_request=1 -dextension=../ext/google/protobuf/modules/protobuf.so $PHPUNIT --bootstrap autoload.php $t
+  if [ "$TEST_NEW_EXTENSION" = "true" ]; then
+    php -d protobuf.keep_descriptor_pool_after_request=1 -dextension=../ext/google/protobuf2/modules/protobuf.so $PHPUNIT --bootstrap autoload.php $t
+  fi
   echo ""
 done
 
@@ -45,6 +64,10 @@
 export USE_ZEND_ALLOC=0
 valgrind --leak-check=yes php -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php
 valgrind --leak-check=yes php -d protobuf.keep_descriptor_pool_after_request=1 -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php
+if [ "$TEST_NEW_EXTENSION" = "true" ]; then
+  valgrind --leak-check=yes php -dextension=../ext/google/protobuf2/modules/protobuf.so memory_leak_test.php
+  valgrind --leak-check=yes php -d protobuf.keep_descriptor_pool_after_request=1 -dextension=../ext/google/protobuf2/modules/protobuf.so memory_leak_test.php
+fi
 
 # TODO(teboring): Only for debug (phpunit has memory leak which blocks this beging used by
 # regresssion test.)
diff --git a/tests.sh b/tests.sh
index ca998e1..52b9cf1 100755
--- a/tests.sh
+++ b/tests.sh
@@ -478,45 +478,6 @@
   (cd conformance && make test_php)
 }
 
-build_php5.5_c() {
-  IS_64BIT=$1
-  use_php 5.5
-  php/tests/test.sh
-  pushd conformance
-  if [ "$IS_64BIT" = "true" ]
-  then
-    make test_php_c
-  else
-    make test_php_c_32
-  fi
-  popd
-}
-
-build_php5.5_mixed() {
-  use_php 5.5
-  pushd php
-  rm -rf vendor
-  composer update
-  tests/compile_extension.sh
-  tests/generate_protos.sh
-  php -dextension=./ext/google/protobuf/modules/protobuf.so ./vendor/bin/phpunit
-  popd
-}
-
-build_php5.5_zts_c() {
-  IS_64BIT=$1
-  use_php_zts 5.5
-  php/tests/test.sh
-  pushd conformance
-  if [ "$IS_64BIT" = "true" ]
-  then
-    make test_php_c
-  else
-    make test_php_c_32
-  fi
-  popd
-}
-
 build_php5.6() {
   use_php 5.6
   pushd php
@@ -527,61 +488,22 @@
   (cd conformance && make test_php)
 }
 
-build_php5.6_c() {
-  IS_64BIT=$1
-  use_php 5.6
-  php/tests/test.sh
-  pushd conformance
-  if [ "$IS_64BIT" = "true" ]
-  then
-    make test_php_c
-  else
-    make test_php_c_32
-  fi
-  popd
-}
-
-build_php5.6_mixed() {
-  use_php 5.6
-  pushd php
-  rm -rf vendor
-  composer update
-  tests/compile_extension.sh
-  tests/generate_protos.sh
-  php -dextension=./ext/google/protobuf/modules/protobuf.so ./vendor/bin/phpunit
-  popd
-}
-
-build_php5.6_zts_c() {
-  IS_64BIT=$1
-  use_php_zts 5.6
-  php/tests/test.sh
-  pushd conformance
-  if [ "$IS_64BIT" = "true" ]
-  then
-    make test_php_c
-  else
-    make test_php_c_32
-  fi
-  popd
-}
-
 build_php5.6_mac() {
-  internal_build_cpp
   # Install PHP
   curl -s https://php-osx.liip.ch/install.sh | bash -s 5.6
   PHP_FOLDER=`find /usr/local -type d -name "php5-5.6*"`  # The folder name may change upon time
   test ! -z "$PHP_FOLDER"
   export PATH="$PHP_FOLDER/bin:$PATH"
 
-  # Install valgrind
-  echo "#! /bin/bash" > valgrind
-  chmod ug+x valgrind
-  sudo mv valgrind /usr/local/bin/valgrind
+  internal_build_cpp
 
-  # Test
-  php/tests/test.sh
-  (cd conformance && make test_php_c)
+  # Run pure-PHP tests only.
+  pushd php
+  rm -rf vendor
+  composer update
+  composer test
+  popd
+  (cd conformance && make test_php)
 }
 
 build_php7.0() {
@@ -752,7 +674,6 @@
     make test_php_c_32
   fi
   popd
-  (cd php/ext/google/protobuf && phpize --clean)
 }
 
 build_php7.4_mixed() {
@@ -764,7 +685,6 @@
   tests/generate_protos.sh
   php -dextension=./ext/google/protobuf/modules/protobuf.so ./vendor/bin/phpunit
   popd
-  (cd php/ext/google/protobuf && phpize --clean)
 }
 
 build_php7.4_zts_c() {
@@ -779,7 +699,6 @@
     make test_php_c_32
   fi
   popd
-  (cd php/ext/google/protobuf && phpize --clean)
 }
 
 build_php_all_32() {
@@ -788,18 +707,12 @@
   build_php7.0
   build_php7.1
   build_php7.4
-  build_php5.5_c $1
-  build_php5.6_c $1
   build_php7.0_c $1
   build_php7.1_c $1
   build_php7.4_c $1
-  build_php5.5_mixed
-  build_php5.6_mixed
   build_php7.0_mixed
   build_php7.1_mixed
   build_php7.4_mixed
-  build_php5.5_zts_c $1
-  build_php5.6_zts_c $1
   build_php7.0_zts_c $1
   build_php7.1_zts_c $1
   build_php7.4_zts_c $1
@@ -846,9 +759,7 @@
             jruby |
             ruby_all |
             php5.5   |
-            php5.5_c |
             php5.6   |
-            php5.6_c |
             php7.0   |
             php7.0_c |
             php_compatibility |