summaryrefslogtreecommitdiff
path: root/upb
diff options
context:
space:
mode:
Diffstat (limited to 'upb')
-rw-r--r--upb/bindings/lua/def.c1346
-rw-r--r--upb/bindings/lua/msg.c1073
-rw-r--r--upb/bindings/lua/upb.c1975
-rw-r--r--upb/bindings/lua/upb.h64
-rw-r--r--upb/bindings/lua/upb/msg.c892
-rw-r--r--upb/bindings/lua/upb/pb.c94
-rw-r--r--upb/def.c15
-rw-r--r--upb/def.h40
-rw-r--r--upb/descriptor/descriptor.pbbin6341 -> 0 bytes
-rw-r--r--upb/descriptor/reader.c22
-rw-r--r--upb/json/parser.rl1
-rw-r--r--upb/msg.c1205
-rw-r--r--upb/msg.h340
-rw-r--r--upb/shim/shim.c84
-rw-r--r--upb/shim/shim.h69
-rw-r--r--upb/structdefs.int.h3
-rw-r--r--upb/table.int.h34
-rw-r--r--upb/upb.c4
-rw-r--r--upb/upb.h16
19 files changed, 5055 insertions, 2222 deletions
diff --git a/upb/bindings/lua/def.c b/upb/bindings/lua/def.c
new file mode 100644
index 0000000..7e41782
--- /dev/null
+++ b/upb/bindings/lua/def.c
@@ -0,0 +1,1346 @@
+
+#include <float.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lauxlib.h"
+#include "upb/bindings/lua/upb.h"
+#include "upb/def.h"
+#include "upb/pb/glue.h"
+
+#define LUPB_ENUMDEF "lupb.enumdef"
+#define LUPB_FIELDDEF "lupb.fielddef"
+#define LUPB_FILEDEF "lupb.filedef"
+#define LUPB_MSGDEF "lupb.msgdef"
+#define LUPB_ONEOFDEF "lupb.oneof"
+#define LUPB_SYMTAB "lupb.symtab"
+#define LUPB_OBJCACHE "lupb.objcache"
+
+#define CHK(pred) do { \
+ upb_status status = UPB_STATUS_INIT; \
+ pred; \
+ lupb_checkstatus(L, &status); \
+ } while (0)
+
+
+const char *lupb_checkname(lua_State *L, int narg) {
+ size_t len;
+ const char *name = luaL_checklstring(L, narg, &len);
+ if (strlen(name) != len)
+ luaL_error(L, "names cannot have embedded NULLs");
+ return name;
+}
+
+upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg) {
+ int type = luaL_checkint(L, narg);
+ if (!upb_fielddef_checktype(type))
+ luaL_argerror(L, narg, "invalid field type");
+ return type;
+}
+
+
+/* lupb_refcounted ************************************************************/
+
+/* All upb objects that use upb_refcounted have a userdata that begins with a
+ * pointer to that object. Each type has its own metatable. Objects are cached
+ * in a weak table indexed by the C pointer of the object they are caching.
+ *
+ * Note that we consistently use memcpy() to read to/from the object. This
+ * allows the userdata to use its own struct without violating aliasing, as
+ * long as it begins with a pointer. */
+
+/* Checks type; if it matches, pulls the pointer out of the wrapper. */
+void *lupb_refcounted_check(lua_State *L, int narg, const char *type) {
+ void *ud = lua_touserdata(L, narg);
+ void *ret;
+
+ if (ud) {
+ memcpy(&ret, ud, sizeof(ret));
+ if (!ret) {
+ luaL_error(L, "called into dead object");
+ }
+ }
+
+ luaL_checkudata(L, narg, type);
+ return ret;
+}
+
+bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor,
+ size_t size) {
+ bool create;
+ void *ud;
+
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return false;
+ }
+
+ /* Lookup our cache in the registry (we don't put our objects in the registry
+ * directly because we need our cache to be a weak table). */
+ lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
+ UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
+ lua_pushlightuserdata(L, (void*)obj);
+ lua_rawget(L, -2);
+ /* Stack is now: objcache, cached value. */
+
+ create = false;
+
+ if (lua_isnil(L, -1)) {
+ create = true;
+ } else {
+ void *ud = lua_touserdata(L, -1);
+ void *ud_obj;
+ lupb_assert(L, ud);
+ memcpy(&ud_obj, ud, sizeof(void*));
+
+ /* A corner case: it is possible for the value to be GC'd
+ * already, in which case we should evict this entry and create
+ * a new one. */
+ if (ud_obj == NULL) {
+ create = true;
+ }
+ }
+
+ ud = NULL;
+
+ if (create) {
+ /* Remove bad cached value and push new value. */
+ lua_pop(L, 1);
+
+ /* All of our userdata begin with a pointer to the obj. */
+ ud = lua_newuserdata(L, size);
+ memcpy(ud, &obj, sizeof(void*));
+ upb_refcounted_donateref(obj, ref_donor, ud);
+
+ luaL_getmetatable(L, type);
+ /* Should have been created by luaopen_upb. */
+ lupb_assert(L, !lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ /* Set it in the cache. */
+ lua_pushlightuserdata(L, (void*)obj);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ } else {
+ /* Existing wrapper obj already has a ref. */
+ ud = lua_touserdata(L, -1);
+ upb_refcounted_checkref(obj, ud);
+ if (ref_donor)
+ upb_refcounted_unref(obj, ref_donor);
+ }
+
+ lua_insert(L, -2);
+ lua_pop(L, 1);
+ return create;
+}
+
+void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor) {
+ bool created =
+ lupb_refcounted_pushwrapper(L, obj, type, ref_donor, sizeof(void *));
+ UPB_ASSERT(created == true);
+}
+
+int lupb_refcounted_gc(lua_State *L) {
+ void *ud = lua_touserdata(L, 1);
+ void *nullp;
+ upb_refcounted *obj;
+ memcpy(&obj, ud, sizeof(obj));
+ upb_refcounted_unref(obj, ud);
+
+ /* Zero out pointer so we can detect a call into a GC'd object. */
+ nullp = NULL;
+ memcpy(ud, &nullp, sizeof(nullp));
+
+ return 0;
+}
+
+
+/* lupb_def *******************************************************************/
+
+static const upb_def *lupb_def_check(lua_State *L, int narg) {
+ upb_def *ret;
+ void *ud, *ud2;
+
+ ud = lua_touserdata(L, narg);
+ if (ud) {
+ memcpy(&ret, ud, sizeof(ret));
+ if (!ret) {
+ luaL_error(L, "called into dead object");
+ }
+ }
+
+ ud2 = luaL_testudata(L, narg, LUPB_MSGDEF);
+ if (!ud2) ud2 = luaL_testudata(L, narg, LUPB_ENUMDEF);
+ if (!ud2) ud2 = luaL_testudata(L, narg, LUPB_FIELDDEF);
+ if (!ud2) luaL_typerror(L, narg, "upb def");
+
+ return ret;
+}
+
+static upb_def *lupb_def_checkmutable(lua_State *L, int narg) {
+ const upb_def *def = lupb_def_check(L, narg);
+ if (upb_def_isfrozen(def))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_def*)def;
+}
+
+bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor) {
+ const char *type = NULL;
+ bool created;
+
+ if (def == NULL) {
+ lua_pushnil(L);
+ return false;
+ }
+
+ switch (upb_def_type(def)) {
+ case UPB_DEF_MSG:
+ type = LUPB_MSGDEF;
+ break;
+ case UPB_DEF_FIELD:
+ type = LUPB_FIELDDEF;
+ break;
+ case UPB_DEF_ENUM:
+ type = LUPB_ENUMDEF;
+ break;
+ default:
+ UPB_UNREACHABLE();
+ }
+
+ created = lupb_refcounted_pushwrapper(L, upb_def_upcast(def), type, ref_donor,
+ sizeof(void *));
+ return created;
+}
+
+void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor) {
+ bool created = lupb_def_pushwrapper(L, def, ref_donor);
+ UPB_ASSERT(created == true);
+}
+
+static int lupb_def_type(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushinteger(L, upb_def_type(def));
+ return 1;
+}
+
+static int lupb_def_freeze(lua_State *L) {
+ upb_def *def = lupb_def_checkmutable(L, 1);
+ CHK(upb_def_freeze(&def, 1, &status));
+ return 0;
+}
+
+static int lupb_def_isfrozen(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushboolean(L, upb_def_isfrozen(def));
+ return 1;
+}
+
+static int lupb_def_fullname(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushstring(L, upb_def_fullname(def));
+ return 1;
+}
+
+static int lupb_def_name(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushstring(L, upb_def_name(def));
+ return 1;
+}
+
+static int lupb_def_setfullname(lua_State *L) {
+ const char *name = lupb_checkname(L, 2);
+ CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), name, &status));
+ return 0;
+}
+
+#define LUPB_COMMON_DEF_METHODS \
+ {"def_type", lupb_def_type}, \
+ {"full_name", lupb_def_fullname}, \
+ {"freeze", lupb_def_freeze}, \
+ {"is_frozen", lupb_def_isfrozen}, \
+ {"name", lupb_def_name}, \
+ {"set_full_name", lupb_def_setfullname}, \
+
+
+/* lupb_fielddef **************************************************************/
+
+void lupb_fielddef_pushwrapper(lua_State *L, const upb_fielddef *f,
+ const void *ref_donor) {
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(f), ref_donor);
+}
+
+const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_FIELDDEF);
+}
+
+static upb_fielddef *lupb_fielddef_checkmutable(lua_State *L, int narg) {
+ const upb_fielddef *f = lupb_fielddef_check(L, narg);
+ if (upb_fielddef_isfrozen(f))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_fielddef*)f;
+}
+
+static int lupb_fielddef_new(lua_State *L) {
+ upb_fielddef *f = upb_fielddef_new(&f);
+ lupb_def_pushnewrapper(L, upb_fielddef_upcast(f), &f);
+ return 1;
+}
+
+/* Getters */
+
+static int lupb_fielddef_containingtype(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lupb_msgdef_pushwrapper(L, upb_fielddef_containingtype(f), NULL);
+ return 1;
+}
+
+static int lupb_fielddef_containingtypename(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ lua_pushstring(L, upb_fielddef_containingtypename(f));
+ return 1;
+}
+
+static int lupb_fielddef_default(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_INT32:
+ int32:
+ lupb_pushint32(L, upb_fielddef_defaultint32(f)); break;
+ case UPB_TYPE_INT64:
+ lupb_pushint64(L, upb_fielddef_defaultint64(f)); break;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, upb_fielddef_defaultuint32(f)); break;
+ case UPB_TYPE_UINT64:
+ lupb_pushuint64(L, upb_fielddef_defaultuint64(f)); break;
+ case UPB_TYPE_DOUBLE:
+ lua_pushnumber(L, upb_fielddef_defaultdouble(f)); break;
+ case UPB_TYPE_FLOAT:
+ lua_pushnumber(L, upb_fielddef_defaultfloat(f)); break;
+ case UPB_TYPE_BOOL:
+ lua_pushboolean(L, upb_fielddef_defaultbool(f)); break;
+ case UPB_TYPE_ENUM:
+ if (upb_fielddef_enumhasdefaultstr(f)) {
+ goto str;
+ } else if (upb_fielddef_enumhasdefaultint32(f)) {
+ goto int32;
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ str: {
+ size_t len;
+ const char *data = upb_fielddef_defaultstr(f, &len);
+ lua_pushlstring(L, data, len);
+ break;
+ }
+ case UPB_TYPE_MESSAGE:
+ return luaL_error(L, "Message fields do not have explicit defaults.");
+ }
+ return 1;
+}
+
+static int lupb_fielddef_getsel(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ upb_selector_t sel;
+ if (upb_handlers_getselector(f, luaL_checknumber(L, 2), &sel)) {
+ lua_pushinteger(L, sel);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int lupb_fielddef_hassubdef(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_hassubdef(f));
+ return 1;
+}
+
+static int lupb_fielddef_index(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushinteger(L, upb_fielddef_index(f));
+ return 1;
+}
+
+static int lupb_fielddef_intfmt(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushinteger(L, upb_fielddef_intfmt(f));
+ return 1;
+}
+
+static int lupb_fielddef_isextension(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_isextension(f));
+ return 1;
+}
+
+static int lupb_fielddef_istagdelim(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_istagdelim(f));
+ return 1;
+}
+
+static int lupb_fielddef_label(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushinteger(L, upb_fielddef_label(f));
+ return 1;
+}
+
+static int lupb_fielddef_lazy(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_lazy(f));
+ return 1;
+}
+
+static int lupb_fielddef_name(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushstring(L, upb_fielddef_name(f));
+ return 1;
+}
+
+static int lupb_fielddef_number(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ int32_t num = upb_fielddef_number(f);
+ if (num)
+ lua_pushinteger(L, num);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int lupb_fielddef_packed(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_packed(f));
+ return 1;
+}
+
+static int lupb_fielddef_subdef(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ const upb_def *def;
+
+ if (!upb_fielddef_hassubdef(f))
+ luaL_error(L, "Tried to get subdef of non-message field");
+ def = upb_fielddef_subdef(f);
+ lupb_def_pushwrapper(L, def, NULL);
+ return 1;
+}
+
+static int lupb_fielddef_subdefname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ if (!upb_fielddef_hassubdef(f))
+ luaL_error(L, "Tried to get subdef name of non-message field");
+ lua_pushstring(L, upb_fielddef_subdefname(f));
+ return 1;
+}
+
+static int lupb_fielddef_type(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ if (upb_fielddef_typeisset(f))
+ lua_pushinteger(L, upb_fielddef_type(f));
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+/* Setters */
+
+static int lupb_fielddef_setcontainingtypename(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const char *name = NULL;
+ if (!lua_isnil(L, 2))
+ name = lupb_checkname(L, 2);
+ CHK(upb_fielddef_setcontainingtypename(f, name, &status));
+ return 0;
+}
+
+static int lupb_fielddef_setdefault(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_INT32:
+ upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
+ break;
+ case UPB_TYPE_INT64:
+ upb_fielddef_setdefaultint64(f, lupb_checkint64(L, 2));
+ break;
+ case UPB_TYPE_UINT32:
+ upb_fielddef_setdefaultuint32(f, lupb_checkuint32(L, 2));
+ break;
+ case UPB_TYPE_UINT64:
+ upb_fielddef_setdefaultuint64(f, lupb_checkuint64(L, 2));
+ break;
+ case UPB_TYPE_DOUBLE:
+ upb_fielddef_setdefaultdouble(f, lupb_checkdouble(L, 2));
+ break;
+ case UPB_TYPE_FLOAT:
+ upb_fielddef_setdefaultfloat(f, lupb_checkfloat(L, 2));
+ break;
+ case UPB_TYPE_BOOL:
+ upb_fielddef_setdefaultbool(f, lupb_checkbool(L, 2));
+ break;
+ case UPB_TYPE_MESSAGE:
+ return luaL_error(L, "Message types cannot have defaults.");
+ case UPB_TYPE_ENUM:
+ if (lua_type(L, 2) != LUA_TSTRING) {
+ upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
+ break;
+ }
+ /* Else fall through and set string default. */
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_STRING: {
+ size_t len;
+ const char *str = lua_tolstring(L, 2, &len);
+ CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
+ }
+ }
+ return 0;
+}
+
+static int lupb_fielddef_setisextension(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setisextension(f, lupb_checkbool(L, 2)));
+ return 0;
+}
+
+static int lupb_fielddef_setlabel(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ int label = luaL_checkint(L, 2);
+ if (!upb_fielddef_checklabel(label))
+ luaL_argerror(L, 2, "invalid field label");
+ upb_fielddef_setlabel(f, label);
+ return 0;
+}
+
+static int lupb_fielddef_setlazy(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_setlazy(f, lupb_checkbool(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setname(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_fielddef_setnumber(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setnumber(f, luaL_checkint(L, 2), &status));
+ return 0;
+}
+
+static int lupb_fielddef_setpacked(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_setpacked(f, lupb_checkbool(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setsubdef(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const upb_def *def = NULL;
+ if (!lua_isnil(L, 2))
+ def = lupb_def_check(L, 2);
+ CHK(upb_fielddef_setsubdef(f, def, &status));
+ return 0;
+}
+
+static int lupb_fielddef_setsubdefname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const char *name = NULL;
+ if (!lua_isnil(L, 2))
+ name = lupb_checkname(L, 2);
+ CHK(upb_fielddef_setsubdefname(f, name, &status));
+ return 0;
+}
+
+static int lupb_fielddef_settype(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_settype(f, lupb_checkfieldtype(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setintfmt(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ int32_t intfmt = luaL_checknumber(L, 2);
+ if (!upb_fielddef_checkintfmt(intfmt))
+ luaL_argerror(L, 2, "invalid intfmt");
+ upb_fielddef_setintfmt(f, intfmt);
+ return 0;
+}
+
+static int lupb_fielddef_settagdelim(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ bool is_tag_delim = lupb_checkbool(L, 2);
+ CHK(upb_fielddef_settagdelim(f, is_tag_delim));
+ return 0;
+}
+
+static const struct luaL_Reg lupb_fielddef_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_fielddef_m[] = {
+ LUPB_COMMON_DEF_METHODS
+
+ {"containing_type", lupb_fielddef_containingtype},
+ {"containing_type_name", lupb_fielddef_containingtypename},
+ {"default", lupb_fielddef_default},
+ {"getsel", lupb_fielddef_getsel},
+ {"has_subdef", lupb_fielddef_hassubdef},
+ {"index", lupb_fielddef_index},
+ {"intfmt", lupb_fielddef_intfmt},
+ {"is_extension", lupb_fielddef_isextension},
+ {"istagdelim", lupb_fielddef_istagdelim},
+ {"label", lupb_fielddef_label},
+ {"lazy", lupb_fielddef_lazy},
+ {"name", lupb_fielddef_name},
+ {"number", lupb_fielddef_number},
+ {"packed", lupb_fielddef_packed},
+ {"subdef", lupb_fielddef_subdef},
+ {"subdef_name", lupb_fielddef_subdefname},
+ {"type", lupb_fielddef_type},
+
+ {"set_containing_type_name", lupb_fielddef_setcontainingtypename},
+ {"set_default", lupb_fielddef_setdefault},
+ {"set_is_extension", lupb_fielddef_setisextension},
+ {"set_label", lupb_fielddef_setlabel},
+ {"set_lazy", lupb_fielddef_setlazy},
+ {"set_name", lupb_fielddef_setname},
+ {"set_number", lupb_fielddef_setnumber},
+ {"set_packed", lupb_fielddef_setpacked},
+ {"set_subdef", lupb_fielddef_setsubdef},
+ {"set_subdef_name", lupb_fielddef_setsubdefname},
+ {"set_type", lupb_fielddef_settype},
+ {"set_intfmt", lupb_fielddef_setintfmt},
+ {"set_tagdelim", lupb_fielddef_settagdelim},
+
+ {NULL, NULL}
+};
+
+
+/* lupb_oneofdef **************************************************************/
+
+void lupb_oneofdef_pushwrapper(lua_State *L, const upb_oneofdef *o,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF,
+ ref_donor, sizeof(void *));
+}
+
+const upb_oneofdef *lupb_oneofdef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_ONEOFDEF);
+}
+
+static upb_oneofdef *lupb_oneofdef_checkmutable(lua_State *L, int narg) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, narg);
+ if (upb_oneofdef_isfrozen(o))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_oneofdef*)o;
+}
+
+static int lupb_oneofdef_new(lua_State *L) {
+ upb_oneofdef *o = upb_oneofdef_new(&o);
+ lupb_refcounted_pushnewrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF, &o);
+ return 1;
+}
+
+/* Getters */
+
+static int lupb_oneofdef_containingtype(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ lupb_def_pushwrapper(L, upb_msgdef_upcast(upb_oneofdef_containingtype(o)),
+ NULL);
+ return 1;
+}
+
+static int lupb_oneofdef_field(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ int type = lua_type(L, 2);
+ const upb_fielddef *f;
+ if (type == LUA_TNUMBER) {
+ f = upb_oneofdef_itof(o, lua_tointeger(L, 2));
+ } else if (type == LUA_TSTRING) {
+ f = upb_oneofdef_ntofz(o, lua_tostring(L, 2));
+ } else {
+ const char *msg = lua_pushfstring(L, "number or string expected, got %s",
+ luaL_typename(L, 2));
+ return luaL_argerror(L, 2, msg);
+ }
+
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
+ return 1;
+}
+
+static int lupb_oneofdef_len(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ lua_pushinteger(L, upb_oneofdef_numfields(o));
+ return 1;
+}
+
+static int lupb_oneofdef_name(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ lua_pushstring(L, upb_oneofdef_name(o));
+ return 1;
+}
+
+/* Setters */
+
+static int lupb_oneofdef_add(lua_State *L) {
+ upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
+ CHK(upb_oneofdef_addfield(o, f, NULL, &status));
+ return 0;
+}
+
+static int lupb_oneofdef_setname(lua_State *L) {
+ upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
+ CHK(upb_oneofdef_setname(o, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static const struct luaL_Reg lupb_oneofdef_m[] = {
+ {"containing_type", lupb_oneofdef_containingtype},
+ {"field", lupb_oneofdef_field},
+ {"name", lupb_oneofdef_name},
+
+ {"add", lupb_oneofdef_add},
+ {"set_name", lupb_oneofdef_setname},
+
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_oneofdef_mm[] = {
+ {"__len", lupb_oneofdef_len},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgdef ****************************************************************/
+
+typedef struct {
+ const upb_msgdef *md;
+} lupb_msgdef;
+
+void lupb_msgdef_pushwrapper(lua_State *L, const upb_msgdef *m,
+ const void *ref_donor) {
+ lupb_def_pushwrapper(L, upb_msgdef_upcast(m), ref_donor);
+}
+
+const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_MSGDEF);
+}
+
+static upb_msgdef *lupb_msgdef_checkmutable(lua_State *L, int narg) {
+ const upb_msgdef *m = lupb_msgdef_check(L, narg);
+ if (upb_msgdef_isfrozen(m))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_msgdef*)m;
+}
+
+static int lupb_msgdef_new(lua_State *L) {
+ upb_msgdef *md = upb_msgdef_new(&md);
+ lupb_def_pushnewrapper(L, upb_msgdef_upcast(md), &md);
+ return 1;
+}
+
+static int lupb_msgdef_add(lua_State *L) {
+ upb_msgdef *m = lupb_msgdef_checkmutable(L, 1);
+
+ /* Both oneofs and fields can be added. */
+ if (luaL_testudata(L, 2, LUPB_FIELDDEF)) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
+ CHK(upb_msgdef_addfield(m, f, NULL, &status));
+ } else if (luaL_testudata(L, 2, LUPB_ONEOFDEF)) {
+ upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 2);
+ CHK(upb_msgdef_addoneof(m, o, NULL, &status));
+ }
+ return 0;
+}
+
+static int lupb_msgdef_len(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ lua_pushinteger(L, upb_msgdef_numfields(m));
+ return 1;
+}
+
+static int lupb_msgdef_field(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ int type = lua_type(L, 2);
+ const upb_fielddef *f;
+ if (type == LUA_TNUMBER) {
+ f = upb_msgdef_itof(m, lua_tointeger(L, 2));
+ } else if (type == LUA_TSTRING) {
+ f = upb_msgdef_ntofz(m, lua_tostring(L, 2));
+ } else {
+ const char *msg = lua_pushfstring(L, "number or string expected, got %s",
+ luaL_typename(L, 2));
+ return luaL_argerror(L, 2, msg);
+ }
+
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
+ return 1;
+}
+
+static int lupb_msgdef_lookupname(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ const upb_fielddef *f;
+ const upb_oneofdef *o;
+ if (!upb_msgdef_lookupnamez(m, lua_tostring(L, 2), &f, &o)) {
+ lua_pushnil(L);
+ } else if (o) {
+ lupb_oneofdef_pushwrapper(L, o, NULL);
+ } else {
+ lupb_fielddef_pushwrapper(L, f, NULL);
+ }
+ return 1;
+}
+
+static int lupb_msgfielditer_next(lua_State *L) {
+ upb_msg_field_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_msg_field_done(i)) return 0;
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(upb_msg_iter_field(i)), NULL);
+ upb_msg_field_next(i);
+ return 1;
+}
+
+static int lupb_msgdef_fields(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ upb_msg_field_iter *i = lua_newuserdata(L, sizeof(upb_msg_field_iter));
+ upb_msg_field_begin(i, m);
+ /* Need to guarantee that the msgdef outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_msgfielditer_next, 2);
+ return 1;
+}
+
+static int lupb_msgoneofiter_next(lua_State *L) {
+ upb_msg_oneof_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_msg_oneof_done(i)) return 0;
+ lupb_oneofdef_pushwrapper(L, upb_msg_iter_oneof(i), NULL);
+ upb_msg_oneof_next(i);
+ return 1;
+}
+
+static int lupb_msgdef_oneofs(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ upb_msg_oneof_iter *i = lua_newuserdata(L, sizeof(upb_msg_oneof_iter));
+ upb_msg_oneof_begin(i, m);
+ /* Need to guarantee that the msgdef outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_msgoneofiter_next, 2);
+ return 1;
+}
+
+static int lupb_msgdef_mapentry(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ lua_pushboolean(L, upb_msgdef_mapentry(m));
+ return 1;
+}
+
+static int lupb_msgdef_syntax(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ lua_pushinteger(L, upb_msgdef_syntax(m));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgdef_mm[] = {
+ {"__len", lupb_msgdef_len},
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_msgdef_m[] = {
+ LUPB_COMMON_DEF_METHODS
+ {"add", lupb_msgdef_add},
+ {"field", lupb_msgdef_field},
+ {"fields", lupb_msgdef_fields},
+ {"lookup_name", lupb_msgdef_lookupname},
+ {"oneofs", lupb_msgdef_oneofs},
+ {"syntax", lupb_msgdef_syntax},
+ {"_map_entry", lupb_msgdef_mapentry},
+
+ {NULL, NULL}
+};
+
+
+/* lupb_enumdef ***************************************************************/
+
+const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_ENUMDEF);
+}
+
+static upb_enumdef *lupb_enumdef_checkmutable(lua_State *L, int narg) {
+ const upb_enumdef *f = lupb_enumdef_check(L, narg);
+ if (upb_enumdef_isfrozen(f))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_enumdef*)f;
+}
+
+static int lupb_enumdef_new(lua_State *L) {
+ upb_enumdef *e = upb_enumdef_new(&e);
+ lupb_def_pushnewrapper(L, upb_enumdef_upcast(e), &e);
+ return 1;
+}
+
+static int lupb_enumdef_add(lua_State *L) {
+ upb_enumdef *e = lupb_enumdef_checkmutable(L, 1);
+ const char *name = lupb_checkname(L, 2);
+ int32_t val = lupb_checkint32(L, 3);
+ CHK(upb_enumdef_addval(e, name, val, &status));
+ return 0;
+}
+
+static int lupb_enumdef_len(lua_State *L) {
+ const upb_enumdef *e = lupb_enumdef_check(L, 1);
+ lua_pushinteger(L, upb_enumdef_numvals(e));
+ return 1;
+}
+
+static int lupb_enumdef_value(lua_State *L) {
+ const upb_enumdef *e = lupb_enumdef_check(L, 1);
+ int type = lua_type(L, 2);
+ if (type == LUA_TNUMBER) {
+ /* Pushes "nil" for a NULL pointer. */
+ int32_t key = lupb_checkint32(L, 2);
+ lua_pushstring(L, upb_enumdef_iton(e, key));
+ } else if (type == LUA_TSTRING) {
+ const char *key = lua_tostring(L, 2);
+ int32_t num;
+ if (upb_enumdef_ntoiz(e, key, &num)) {
+ lua_pushinteger(L, num);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ const char *msg = lua_pushfstring(L, "number or string expected, got %s",
+ luaL_typename(L, 2));
+ return luaL_argerror(L, 2, msg);
+ }
+ return 1;
+}
+
+static int lupb_enumiter_next(lua_State *L) {
+ upb_enum_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_enum_done(i)) return 0;
+ lua_pushstring(L, upb_enum_iter_name(i));
+ lua_pushinteger(L, upb_enum_iter_number(i));
+ upb_enum_next(i);
+ return 2;
+}
+
+static int lupb_enumdef_values(lua_State *L) {
+ const upb_enumdef *e = lupb_enumdef_check(L, 1);
+ upb_enum_iter *i = lua_newuserdata(L, sizeof(upb_enum_iter));
+ upb_enum_begin(i, e);
+ /* Need to guarantee that the enumdef outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_enumiter_next, 2);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_enumdef_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {"__len", lupb_enumdef_len},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_enumdef_m[] = {
+ LUPB_COMMON_DEF_METHODS
+ {"add", lupb_enumdef_add},
+ {"value", lupb_enumdef_value},
+ {"values", lupb_enumdef_values},
+ {NULL, NULL}
+};
+
+
+/* lupb_filedef ***************************************************************/
+
+void lupb_filedef_pushwrapper(lua_State *L, const upb_filedef *f,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF, ref_donor,
+ sizeof(void *));
+}
+
+void lupb_filedef_pushnewrapper(lua_State *L, const upb_filedef *f,
+ const void *ref_donor) {
+ lupb_refcounted_pushnewrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF,
+ ref_donor);
+}
+
+const upb_filedef *lupb_filedef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_FILEDEF);
+}
+
+static upb_filedef *lupb_filedef_checkmutable(lua_State *L, int narg) {
+ const upb_filedef *f = lupb_filedef_check(L, narg);
+ if (upb_filedef_isfrozen(f))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_filedef*)f;
+}
+
+static int lupb_filedef_new(lua_State *L) {
+ upb_filedef *f = upb_filedef_new(&f);
+ lupb_filedef_pushnewrapper(L, f, &f);
+ return 1;
+}
+
+static int lupb_filedef_def(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ int index = luaL_checkint(L, 2);
+ const upb_def *def = upb_filedef_def(f, index);
+
+ if (!def) {
+ return luaL_error(L, "index out of range");
+ }
+
+ lupb_def_pushwrapper(L, def, NULL);
+ return 1;
+}
+
+static int lupb_filedef_name(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ lua_pushstring(L, upb_filedef_name(f));
+ return 1;
+}
+
+static int lupb_filedef_package(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ lua_pushstring(L, upb_filedef_package(f));
+ return 1;
+}
+
+static int lupb_filedef_len(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ lua_pushinteger(L, upb_filedef_defcount(f));
+ return 1;
+}
+
+static int lupb_filedef_setname(lua_State *L) {
+ upb_filedef *f = lupb_filedef_checkmutable(L, 1);
+ CHK(upb_filedef_setname(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_filedef_setpackage(lua_State *L) {
+ upb_filedef *f = lupb_filedef_checkmutable(L, 1);
+ CHK(upb_filedef_setpackage(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_filedefiter_next(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, lua_upvalueindex(1));
+ int type = lua_tointeger(L, lua_upvalueindex(2));
+ size_t i = lua_tointeger(L, lua_upvalueindex(3));
+ size_t n = upb_filedef_defcount(f);
+
+ for (; i < n; i++) {
+ const upb_def *def;
+
+ def = upb_filedef_def(f, i);
+ UPB_ASSERT(def);
+
+ if (type == UPB_DEF_ANY || upb_def_type(def) == type) {
+ lua_pushinteger(L, i + 1);
+ lua_replace(L, lua_upvalueindex(3));
+ lupb_def_pushwrapper(L, def, NULL);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int lupb_filedef_defs(lua_State *L) {
+ lupb_filedef_check(L, 1);
+ luaL_checkinteger(L, 2); /* Def type. Could make this optional. */
+ lua_pushnumber(L, 0); /* Index, starts at zero. */
+ lua_pushcclosure(L, &lupb_filedefiter_next, 3);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_filedef_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {"__len", lupb_filedef_len},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_filedef_m[] = {
+ {"def", lupb_filedef_def},
+ {"defs", lupb_filedef_defs},
+ {"name", lupb_filedef_name},
+ {"package", lupb_filedef_package},
+
+ {"set_name", lupb_filedef_setname},
+ {"set_package", lupb_filedef_setpackage},
+
+ {NULL, NULL}
+};
+
+
+/* lupb_symtab ****************************************************************/
+
+/* Inherits a ref on the symtab.
+ * Checks that narg is a proper lupb_symtab object. If it is, leaves its
+ * metatable on the stack for cache lookups/updates. */
+const upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_SYMTAB);
+}
+
+static upb_symtab *lupb_symtab_checkmutable(lua_State *L, int narg) {
+ const upb_symtab *s = lupb_symtab_check(L, narg);
+ if (upb_symtab_isfrozen(s))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_symtab*)s;
+}
+
+void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB, ref_donor,
+ sizeof(void *));
+}
+
+void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushnewrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB,
+ ref_donor);
+}
+
+static int lupb_symtab_new(lua_State *L) {
+ upb_symtab *s = upb_symtab_new(&s);
+ lupb_symtab_pushnewrapper(L, s, &s);
+ return 1;
+}
+
+static int lupb_symtab_freeze(lua_State *L) {
+ upb_symtab_freeze(lupb_symtab_checkmutable(L, 1));
+ return 0;
+}
+
+static int lupb_symtab_isfrozen(lua_State *L) {
+ lua_pushboolean(L, upb_symtab_isfrozen(lupb_symtab_check(L, 1)));
+ return 1;
+}
+
+static int lupb_symtab_add(lua_State *L) {
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
+ int n;
+ upb_def **defs;
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+ /* Iterate over table twice. First iteration to count entries and
+ * check constraints. */
+ n = 0;
+ for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
+ lupb_def_checkmutable(L, -1);
+ ++n;
+ }
+
+ /* Second iteration to build deflist.
+ * Allocate list with lua_newuserdata() so it is anchored as a GC root in
+ * case any Lua functions longjmp(). */
+ defs = lua_newuserdata(L, n * sizeof(*defs));
+ n = 0;
+ for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
+ upb_def *def = lupb_def_checkmutable(L, -1);
+ defs[n++] = def;
+ }
+
+ CHK(upb_symtab_add(s, defs, n, NULL, &status));
+ return 0;
+}
+
+static int lupb_symtab_addfile(lua_State *L) {
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
+ upb_filedef *f = lupb_filedef_checkmutable(L, 2);
+ CHK(upb_symtab_addfile(s, f, &status));
+ return 0;
+}
+
+static int lupb_symtab_lookup(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ int i;
+ for (i = 2; i <= lua_gettop(L); i++) {
+ const upb_def *def = upb_symtab_lookup(s, luaL_checkstring(L, i));
+ lupb_def_pushwrapper(L, def, NULL);
+ lua_replace(L, i);
+ }
+ return lua_gettop(L) - 1;
+}
+
+static int lupb_symtabiter_next(lua_State *L) {
+ upb_symtab_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_symtab_done(i)) return 0;
+ lupb_def_pushwrapper(L, upb_symtab_iter_def(i), NULL);
+ upb_symtab_next(i);
+ return 1;
+}
+
+static int lupb_symtab_defs(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ upb_deftype_t type = lua_gettop(L) > 1 ? luaL_checkint(L, 2) : UPB_DEF_ANY;
+ upb_symtab_iter *i = lua_newuserdata(L, sizeof(upb_symtab_iter));
+ upb_symtab_begin(i, s, type);
+ /* Need to guarantee that the symtab outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_symtabiter_next, 2);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_symtab_m[] = {
+ {"add", lupb_symtab_add},
+ {"add_file", lupb_symtab_addfile},
+ {"defs", lupb_symtab_defs},
+ {"freeze", lupb_symtab_freeze},
+ {"is_frozen", lupb_symtab_isfrozen},
+ {"lookup", lupb_symtab_lookup},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_symtab_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
+/* lupb toplevel **************************************************************/
+
+static int lupb_freeze(lua_State *L) {
+ int n = lua_gettop(L);
+ int i;
+ /* Scratch memory; lua_newuserdata() anchors it as a GC root in case any Lua
+ * functions fail. */
+ upb_def **defs = lua_newuserdata(L, n * sizeof(upb_def*));
+
+ for (i = 0; i < n; i++) {
+ /* Could allow an array of defs here also. */
+ defs[i] = lupb_def_checkmutable(L, i + 1);
+ }
+ CHK(upb_def_freeze(defs, n, &status));
+ return 0;
+}
+
+/* This is a *temporary* API that will be removed once pending refactorings are
+ * complete (it does not belong here in core because it depends on both
+ * the descriptor.proto schema and the protobuf binary format. */
+static int lupb_loaddescriptor(lua_State *L) {
+ size_t len;
+ const char *str = luaL_checklstring(L, 1, &len);
+ size_t i;
+ upb_filedef **files = NULL;
+ CHK(files = upb_loaddescriptor(str, len, &files, &status));
+
+ lua_newtable(L);
+ for (i = 1; *files; i++, files++) {
+ lupb_filedef_pushnewrapper(L, *files, &files);
+ lua_rawseti(L, -2, i);
+ }
+
+ return 1;
+}
+
+static void lupb_setfieldi(lua_State *L, const char *field, int i) {
+ lua_pushinteger(L, i);
+ lua_setfield(L, -2, field);
+}
+
+static const struct luaL_Reg lupbdef_toplevel_m[] = {
+ {"EnumDef", lupb_enumdef_new},
+ {"FieldDef", lupb_fielddef_new},
+ {"FileDef", lupb_filedef_new},
+ {"MessageDef", lupb_msgdef_new},
+ {"OneofDef", lupb_oneofdef_new},
+ {"SymbolTable", lupb_symtab_new},
+ {"freeze", lupb_freeze},
+ {"load_descriptor", lupb_loaddescriptor},
+
+ {NULL, NULL}
+};
+
+void lupb_def_registertypes(lua_State *L) {
+ lupb_setfuncs(L, lupbdef_toplevel_m);
+
+ /* Refcounted types. */
+ lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm);
+ lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, lupb_fielddef_mm);
+ lupb_register_type(L, LUPB_FILEDEF, lupb_filedef_m, lupb_filedef_mm);
+ lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm);
+ lupb_register_type(L, LUPB_ONEOFDEF, lupb_oneofdef_m, lupb_oneofdef_mm);
+ lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, lupb_symtab_mm);
+
+ /* Create our object cache. */
+ lua_newtable(L);
+ lua_createtable(L, 0, 1); /* Cache metatable. */
+ lua_pushstring(L, "v"); /* Values are weak. */
+ lua_setfield(L, -2, "__mode");
+ lua_setmetatable(L, -2);
+ lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
+
+ /* Register constants. */
+ lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL_OPTIONAL);
+ lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL_REQUIRED);
+ lupb_setfieldi(L, "LABEL_REPEATED", UPB_LABEL_REPEATED);
+
+ lupb_setfieldi(L, "TYPE_DOUBLE", UPB_TYPE_DOUBLE);
+ lupb_setfieldi(L, "TYPE_FLOAT", UPB_TYPE_FLOAT);
+ lupb_setfieldi(L, "TYPE_INT64", UPB_TYPE_INT64);
+ lupb_setfieldi(L, "TYPE_UINT64", UPB_TYPE_UINT64);
+ lupb_setfieldi(L, "TYPE_INT32", UPB_TYPE_INT32);
+ lupb_setfieldi(L, "TYPE_BOOL", UPB_TYPE_BOOL);
+ lupb_setfieldi(L, "TYPE_STRING", UPB_TYPE_STRING);
+ lupb_setfieldi(L, "TYPE_MESSAGE", UPB_TYPE_MESSAGE);
+ lupb_setfieldi(L, "TYPE_BYTES", UPB_TYPE_BYTES);
+ lupb_setfieldi(L, "TYPE_UINT32", UPB_TYPE_UINT32);
+ lupb_setfieldi(L, "TYPE_ENUM", UPB_TYPE_ENUM);
+
+ lupb_setfieldi(L, "INTFMT_VARIABLE", UPB_INTFMT_VARIABLE);
+ lupb_setfieldi(L, "INTFMT_FIXED", UPB_INTFMT_FIXED);
+ lupb_setfieldi(L, "INTFMT_ZIGZAG", UPB_INTFMT_ZIGZAG);
+
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_DOUBLE", UPB_DESCRIPTOR_TYPE_DOUBLE);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_FLOAT", UPB_DESCRIPTOR_TYPE_FLOAT);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT64", UPB_DESCRIPTOR_TYPE_INT64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT64", UPB_DESCRIPTOR_TYPE_UINT64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT32", UPB_DESCRIPTOR_TYPE_INT32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED64", UPB_DESCRIPTOR_TYPE_FIXED64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED32", UPB_DESCRIPTOR_TYPE_FIXED32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_BOOL", UPB_DESCRIPTOR_TYPE_BOOL);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_STRING", UPB_DESCRIPTOR_TYPE_STRING);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_GROUP", UPB_DESCRIPTOR_TYPE_GROUP);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_MESSAGE", UPB_DESCRIPTOR_TYPE_MESSAGE);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_BYTES", UPB_DESCRIPTOR_TYPE_BYTES);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT32", UPB_DESCRIPTOR_TYPE_UINT32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_ENUM", UPB_DESCRIPTOR_TYPE_ENUM);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED32", UPB_DESCRIPTOR_TYPE_SFIXED32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED64", UPB_DESCRIPTOR_TYPE_SFIXED64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT32", UPB_DESCRIPTOR_TYPE_SINT32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT64", UPB_DESCRIPTOR_TYPE_SINT64);
+
+ lupb_setfieldi(L, "DEF_MSG", UPB_DEF_MSG);
+ lupb_setfieldi(L, "DEF_FIELD", UPB_DEF_FIELD);
+ lupb_setfieldi(L, "DEF_ENUM", UPB_DEF_ENUM);
+ lupb_setfieldi(L, "DEF_SERVICE", UPB_DEF_SERVICE);
+ lupb_setfieldi(L, "DEF_ANY", UPB_DEF_ANY);
+
+ lupb_setfieldi(L, "HANDLER_INT32", UPB_HANDLER_INT32);
+ lupb_setfieldi(L, "HANDLER_INT64", UPB_HANDLER_INT64);
+ lupb_setfieldi(L, "HANDLER_UINT32", UPB_HANDLER_UINT32);
+ lupb_setfieldi(L, "HANDLER_UINT64", UPB_HANDLER_UINT64);
+ lupb_setfieldi(L, "HANDLER_FLOAT", UPB_HANDLER_FLOAT);
+ lupb_setfieldi(L, "HANDLER_DOUBLE", UPB_HANDLER_DOUBLE);
+ lupb_setfieldi(L, "HANDLER_BOOL", UPB_HANDLER_BOOL);
+ lupb_setfieldi(L, "HANDLER_STARTSTR", UPB_HANDLER_STARTSTR);
+ lupb_setfieldi(L, "HANDLER_STRING", UPB_HANDLER_STRING);
+ lupb_setfieldi(L, "HANDLER_ENDSTR", UPB_HANDLER_ENDSTR);
+ lupb_setfieldi(L, "HANDLER_STARTSUBMSG", UPB_HANDLER_STARTSUBMSG);
+ lupb_setfieldi(L, "HANDLER_ENDSUBMSG", UPB_HANDLER_ENDSUBMSG);
+ lupb_setfieldi(L, "HANDLER_STARTSEQ", UPB_HANDLER_STARTSEQ);
+ lupb_setfieldi(L, "HANDLER_ENDSEQ", UPB_HANDLER_ENDSEQ);
+
+ lupb_setfieldi(L, "SYNTAX_PROTO2", UPB_SYNTAX_PROTO2);
+ lupb_setfieldi(L, "SYNTAX_PROTO3", UPB_SYNTAX_PROTO3);
+}
diff --git a/upb/bindings/lua/msg.c b/upb/bindings/lua/msg.c
new file mode 100644
index 0000000..6783b23
--- /dev/null
+++ b/upb/bindings/lua/msg.c
@@ -0,0 +1,1073 @@
+/*
+** lupb_msg -- Message/Array/Map objects in Lua/C that wrap upb/msg.h
+*/
+
+#include <float.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lauxlib.h"
+#include "upb/bindings/lua/upb.h"
+#include "upb/handlers.h"
+#include "upb/msg.h"
+
+/*
+ * Message/Array/Map objects can be constructed in one of two ways:
+ *
+ * 1. To point to existing msg/array/map data inside an arena.
+ * 2. To create and uniquely own some brand new data.
+ *
+ * Case (1) is for when we've parsed some data into an arena (which is faster
+ * than parsing directly into Lua objects) or when we're pointing at some
+ * read-only data (like custom options in a def).
+ *
+ * Case (2) is for when a user creates the object directly in Lua.
+ *
+ * We use the userval of container objects (Message/Array/Map) to store
+ * references to sub-objects (Strings/Messages/Arrays/Maps). But we need to
+ * keep the userval in sync with the underlying upb_msg/upb_array/upb_map.
+ * We populate the userval lazily from the underlying data.
+ *
+ * This means that no one may remove/replace any String/Message/Array/Map
+ * field/entry in the underlying upb_{msg,array,map} behind our back. It's ok
+ * for entries to be added or for primitives to be modified, but *replacing*
+ * sub-containers is not.
+ *
+ * Luckily parse/merge follow this rule. However clear does not, so it's not
+ * safe to clear behind our back.
+ */
+
+#define LUPB_ARENA "lupb.arena"
+
+#define LUPB_MSGCLASS "lupb.msgclass"
+#define LUPB_MSGFACTORY "lupb.msgfactory"
+
+#define LUPB_ARRAY "lupb.array"
+#define LUPB_MAP "lupb.map"
+#define LUPB_MSG "lupb.msg"
+#define LUPB_STRING "lupb.string"
+
+static int lupb_msg_pushnew(lua_State *L, int narg);
+
+/* Lazily creates the uservalue if it doesn't exist. */
+static void lupb_getuservalue(lua_State *L, int index) {
+ lua_getuservalue(L, index);
+ if (lua_isnil(L, -1)) {
+ /* Lazily create and set userval. */
+ lua_pop(L, 1); /* nil. */
+ lua_pushvalue(L, index); /* userdata copy. */
+ lua_newtable(L);
+ lua_setuservalue(L, -2);
+ lua_pop(L, 1); /* userdata copy. */
+ lua_getuservalue(L, index);
+ }
+ assert(!lua_isnil(L, -1));
+}
+
+static void lupb_uservalseti(lua_State *L, int userdata, int index, int val) {
+ lupb_getuservalue(L, userdata);
+ lua_pushvalue(L, val);
+ lua_rawseti(L, -2, index);
+ lua_pop(L, 1); /* Uservalue. */
+}
+
+static void lupb_uservalgeti(lua_State *L, int userdata, int index) {
+ lupb_getuservalue(L, userdata);
+ lua_rawgeti(L, -1, index);
+ lua_insert(L, -2);
+ lua_pop(L, 1); /* Uservalue. */
+}
+
+/* Pushes a new userdata with the given metatable. */
+static void *lupb_newuserdata(lua_State *L, size_t size, const char *type) {
+ void *ret = lua_newuserdata(L, size);
+
+ /* Set metatable. */
+ luaL_getmetatable(L, type);
+ UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
+ lua_setmetatable(L, -2);
+
+ /* We don't set a uservalue here -- we lazily create it later if necessary. */
+
+ return ret;
+}
+
+
+/* lupb_alloc *****************************************************************/
+
+typedef struct {
+ upb_alloc alloc;
+ lua_State *L;
+} lupb_alloc;
+
+char lupb_alloc_cache_key;
+
+static void *lupb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize,
+ size_t size) {
+ lupb_alloc *lalloc = (lupb_alloc*)alloc;
+ void *ud;
+
+ /* We read this every time in case the user changes the Lua alloc function. */
+ lua_Alloc func = lua_getallocf(lalloc->L, &ud);
+
+ return func(ud, ptr, oldsize, size);
+}
+
+static void lupb_alloc_pushnew(lua_State *L) {
+ lupb_alloc *lalloc = lua_newuserdata(L, sizeof(lupb_alloc));
+
+ lalloc->alloc.func = &lupb_alloc_func;
+ lalloc->L = L;
+}
+
+/* Returns the global lupb_alloc func that was created in our luaopen().
+ * Callers can be guaranteed that it will be alive as long as |L| is. */
+static upb_alloc *lupb_alloc_get(lua_State *L) {
+ lupb_alloc *lalloc;
+
+ lua_pushlightuserdata(L, &lupb_alloc_cache_key);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ lalloc = lua_touserdata(L, -1);
+ UPB_ASSERT(lalloc);
+ lua_pop(L, 1);
+
+ return &lalloc->alloc;
+}
+
+static void lupb_alloc_initsingleton(lua_State *L) {
+ lua_pushlightuserdata(L, &lupb_alloc_cache_key);
+ lupb_alloc_pushnew(L);
+ lua_settable(L, LUA_REGISTRYINDEX);
+}
+
+#define ADD_BYTES(ptr, bytes) ((void*)((char*)ptr + bytes))
+
+
+/* lupb_arena *****************************************************************/
+
+/* lupb_arena only exists to wrap a upb_arena. It is never exposed to users;
+ * it is an internal memory management detail. Other objects refer to this
+ * object from their userdata to keep the arena-owned data alive. */
+
+upb_arena *lupb_arena_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARENA);
+}
+
+int lupb_arena_new(lua_State *L) {
+ upb_arena *a = lupb_newuserdata(L, sizeof(upb_arena), LUPB_ARENA);
+
+ /* TODO(haberman): use Lua alloc func as block allocator? Would need to
+ * verify that all cases of upb_malloc in msg/table are longjmp-safe. */
+ upb_arena_init(a);
+
+ return 1;
+}
+
+static int lupb_arena_gc(lua_State *L) {
+ upb_arena *a = lupb_arena_check(L, 1);
+ upb_arena_uninit(a);
+ return 0;
+}
+
+static const struct luaL_Reg lupb_arena_mm[] = {
+ {"__gc", lupb_arena_gc},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgfactory ************************************************************/
+
+/* Userval contains a map of:
+ * [1] -> SymbolTable (to keep GC-reachable)
+ * [const upb_msgdef*] -> [lupb_msgclass userdata]
+ */
+
+#define LUPB_MSGFACTORY_SYMTAB 1
+
+typedef struct lupb_msgfactory {
+ upb_msgfactory *factory;
+} lupb_msgfactory;
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l);
+
+/* lupb_msgfactory helpers. */
+
+static lupb_msgfactory *lupb_msgfactory_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGFACTORY);
+}
+
+static void lupb_msgfactory_pushmsgclass(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, narg);
+
+ lupb_getuservalue(L, narg);
+ lua_pushlightuserdata(L, (void*)md);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ /* TODO: verify md is in symtab? */
+ lupb_msgclass_pushnew(L, narg,
+ upb_msgfactory_getlayout(lfactory->factory, md));
+
+ /* Set in userval. */
+ lua_pushlightuserdata(L, (void*)md);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ }
+}
+
+static int lupb_msgfactory_gc(lua_State *L) {
+ lupb_msgfactory *lfactory = lupb_msgfactory_check(L, 1);
+
+ if (lfactory->factory) {
+ upb_msgfactory_free(lfactory->factory);
+ lfactory->factory = NULL;
+ }
+
+ return 0;
+}
+
+/* lupb_msgfactory Public API. */
+
+/**
+ * lupb_msgfactory_new()
+ *
+ * Handles:
+ * msgfactory = upb.MessageFactory(symtab)
+ *
+ * Creates a new, empty MessageFactory for the given SymbolTable.
+ * Message classes will be created on demand when the user calls
+ * msgfactory.get_message_class().
+ */
+static int lupb_msgfactory_new(lua_State *L) {
+ const upb_symtab *symtab = lupb_symtab_check(L, 1);
+
+ lupb_msgfactory *lmsgfactory =
+ lupb_newuserdata(L, sizeof(lupb_msgfactory), LUPB_MSGFACTORY);
+ lmsgfactory->factory = upb_msgfactory_new(symtab);
+ lupb_uservalseti(L, -1, LUPB_MSGFACTORY_SYMTAB, 1);
+
+ return 1;
+}
+
+/**
+ * lupb_msgfactory_getmsgclass()
+ *
+ * Handles:
+ * MessageClass = factory.get_message_class(msgdef)
+ */
+static int lupb_msgfactory_getmsgclass(lua_State *L) {
+ lupb_msgfactory_pushmsgclass(L, 1, lupb_msgdef_check(L, 2));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgfactory_m[] = {
+ {"get_message_class", lupb_msgfactory_getmsgclass},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_msgfactory_mm[] = {
+ {"__gc", lupb_msgfactory_gc},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgclass **************************************************************/
+
+#define LUPB_MSGCLASS_FACTORY 1
+#define LUPB_MSGCLASS_MSGDEF 2
+
+struct lupb_msgclass {
+ const upb_msglayout *layout;
+ const lupb_msgfactory *lfactory;
+};
+
+/* Checks that the given object is a lupb_msg with the given lmsgclass. */
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass);
+
+/* Type-checks for assigning to a message field. */
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md);
+
+lupb_msgclass *lupb_msgclass_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGCLASS);
+}
+
+const upb_msglayout *lupb_msgclass_getlayout(lua_State *L, int narg) {
+ return lupb_msgclass_check(L, narg)->layout;
+}
+
+const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg);
+ return upb_msgfactory_getmergehandlers(
+ lmsgclass->lfactory->factory, upb_msglayout_msgdef(lmsgclass->layout));
+}
+
+/**
+ * lupb_msgclass_typecheck()
+ *
+ * Verifies that the expected msgclass matches the actual. If not, raises a Lua
+ * error.
+ */
+static void lupb_msgclass_typecheck(lua_State *L, const lupb_msgclass *expected,
+ const lupb_msgclass *actual) {
+ if (expected != actual) {
+ luaL_error(L, "Message had incorrect type, expected '%s', got '%s'",
+ upb_msgdef_fullname(upb_msglayout_msgdef(expected->layout)),
+ upb_msgdef_fullname(upb_msglayout_msgdef(actual->layout)));
+ }
+}
+
+static const lupb_msgclass *lupb_msgclass_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSGCLASS_FACTORY);
+ lupb_msgfactory_pushmsgclass(L, -1, md);
+ return lupb_msgclass_check(L, -1);
+}
+
+/**
+ * lupb_msgclass_getsubmsgclass()
+ *
+ * Given a MessageClass at index |narg| and the submessage field |f|, returns
+ * the message class for this field.
+ *
+ * Currently we do a hash table lookup for this. If we wanted we could try to
+ * optimize this by caching these pointers in our msgclass, in an array indexed
+ * by field index. We would still need to fall back to calling msgclassfor(),
+ * unless we wanted to eagerly create message classes for all submessages. But
+ * for big schemas that might be a lot of things to build, and we might end up
+ * not using most of them. */
+static const lupb_msgclass *lupb_msgclass_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ if (upb_fielddef_type(f) != UPB_TYPE_MESSAGE) {
+ return NULL;
+ }
+
+ return lupb_msgclass_msgclassfor(L, narg, upb_fielddef_msgsubdef(f));
+}
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, factory);
+ lupb_msgclass *lmc = lupb_newuserdata(L, sizeof(*lmc), LUPB_MSGCLASS);
+
+ UPB_ASSERT(l);
+ lupb_uservalseti(L, -1, LUPB_MSGCLASS_FACTORY, factory);
+ lmc->layout = l;
+ lmc->lfactory = lfactory;
+
+ return 1;
+}
+
+/* MessageClass Public API. */
+
+/**
+ * lupb_msgclass_call()
+ *
+ * Handles:
+ * msg = MessageClass()
+ *
+ * Creates a new message from the given MessageClass.
+ */
+static int lupb_msgclass_call(lua_State *L) {
+ lupb_msg_pushnew(L, 1);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgclass_mm[] = {
+ {"__call", lupb_msgclass_call},
+ {NULL, NULL}
+};
+
+
+/* upb <-> Lua type conversion ************************************************/
+
+static bool lupb_istypewrapped(upb_fieldtype_t type) {
+ return type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES ||
+ type == UPB_TYPE_MESSAGE;
+}
+
+static upb_msgval lupb_tomsgval(lua_State *L, upb_fieldtype_t type, int narg,
+ const lupb_msgclass *lmsgclass) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ return upb_msgval_int32(lupb_checkint32(L, narg));
+ case UPB_TYPE_INT64:
+ return upb_msgval_int64(lupb_checkint64(L, narg));
+ case UPB_TYPE_UINT32:
+ return upb_msgval_uint32(lupb_checkuint32(L, narg));
+ case UPB_TYPE_UINT64:
+ return upb_msgval_uint64(lupb_checkuint64(L, narg));
+ case UPB_TYPE_DOUBLE:
+ return upb_msgval_double(lupb_checkdouble(L, narg));
+ case UPB_TYPE_FLOAT:
+ return upb_msgval_float(lupb_checkfloat(L, narg));
+ case UPB_TYPE_BOOL:
+ return upb_msgval_bool(lupb_checkbool(L, narg));
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ size_t len;
+ const char *ptr = lupb_checkstring(L, narg, &len);
+ return upb_msgval_str(ptr, len);
+ }
+ case UPB_TYPE_MESSAGE:
+ UPB_ASSERT(lmsgclass);
+ return lupb_msg_typecheck(L, narg, lmsgclass);
+ }
+ UPB_UNREACHABLE();
+}
+
+static void lupb_pushmsgval(lua_State *L, upb_fieldtype_t type,
+ upb_msgval val) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ lupb_pushint32(L, upb_msgval_getint32(val));
+ return;
+ case UPB_TYPE_INT64:
+ lupb_pushint64(L, upb_msgval_getint64(val));
+ return;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, upb_msgval_getuint32(val));
+ return;
+ case UPB_TYPE_UINT64:
+ lupb_pushuint64(L, upb_msgval_getuint64(val));
+ return;
+ case UPB_TYPE_DOUBLE:
+ lupb_pushdouble(L, upb_msgval_getdouble(val));
+ return;
+ case UPB_TYPE_FLOAT:
+ lupb_pushfloat(L, upb_msgval_getfloat(val));
+ return;
+ case UPB_TYPE_BOOL:
+ lua_pushboolean(L, upb_msgval_getbool(val));
+ return;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ break; /* Shouldn't call this function. */
+ }
+ UPB_UNREACHABLE();
+}
+
+
+/* lupb_array *****************************************************************/
+
+/* A strongly typed array. Implemented by wrapping upb_array.
+ *
+ * - we only allow integer indices.
+ * - all entries must have the correct type.
+ * - we do not allow "holes" in the array; you can only assign to an existing
+ * index or one past the end (which will grow the array by one).
+ *
+ * For string/submessage entries we keep in the userval:
+ *
+ * [number index] -> [lupb_string/lupb_msg userdata]
+ */
+
+typedef struct {
+ /* Only needed for array of message. This wastes space in the non-message
+ * case but simplifies the code. Could optimize away if desired. */
+ lupb_msgclass *lmsgclass;
+ upb_array *arr;
+} lupb_array;
+
+#define ARRAY_MSGCLASS_INDEX 0
+
+static lupb_array *lupb_array_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+/**
+ * lupb_array_typecheck()
+ *
+ * Verifies that the lupb_array object at index |narg| can be safely assigned
+ * to the field |f| of the lupb_msg object at index |msg|. If this is safe,
+ * returns a upb_msgval representing the array. Otherwise, throws a Lua error.
+ */
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_array *larray = lupb_array_check(L, narg);
+
+ if (upb_array_type(larray->arr) != upb_fielddef_type(f) ||
+ lupb_msg_getsubmsgclass(L, msg, f) != larray->lmsgclass) {
+ luaL_error(L, "Array had incorrect type (expected: %d, got: %d)",
+ (int)upb_fielddef_type(f), (int)upb_array_type(larray->arr));
+ }
+
+ if (upb_array_type(larray->arr) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(L, lupb_msg_getsubmsgclass(L, msg, f),
+ larray->lmsgclass);
+ }
+
+ return upb_msgval_arr(larray->arr);
+}
+
+/**
+ * lupb_array_checkindex()
+ *
+ * Checks the array index at Lua stack index |narg| to verify that it is an
+ * integer between 1 and |max|, inclusively. Also corrects it to be zero-based
+ * for C.
+ *
+ * We use "int" because of lua_rawseti/lua_rawgeti -- can re-evaluate if we want
+ * arrays bigger than 2^31.
+ */
+static int lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
+ uint32_t n = lupb_checkuint32(L, narg);
+ if (n == 0 || n > max || n > INT_MAX) {
+ luaL_error(L, "Invalid array index: expected between 1 and %d", (int)max);
+ }
+ return n - 1; /* Lua uses 1-based indexing. :( */
+}
+
+static int lupb_array_gc(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array_uninit(larray->arr);
+ return 0;
+}
+
+/* lupb_array Public API */
+
+static int lupb_array_new(lua_State *L) {
+ lupb_array *larray;
+ upb_fieldtype_t type;
+ lupb_msgclass *lmsgclass = NULL;
+
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ type = lupb_checkfieldtype(L, 1);
+ } else {
+ type = UPB_TYPE_MESSAGE;
+ lmsgclass = lupb_msgclass_check(L, 1);
+ lupb_uservalseti(L, -1, ARRAY_MSGCLASS_INDEX, 1); /* GC-root lmsgclass. */
+ }
+
+ larray =
+ lupb_newuserdata(L, sizeof(*larray) + upb_array_sizeof(type), LUPB_ARRAY);
+ larray->lmsgclass = lmsgclass;
+ larray->arr = ADD_BYTES(larray, sizeof(*larray));
+ upb_array_init(larray->arr, type, lupb_alloc_get(L));
+
+ return 1;
+}
+
+static int lupb_array_newindex(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_fieldtype_t type = upb_array_type(larray->arr);
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(larray->arr) + 1);
+ upb_msgval msgval = lupb_tomsgval(L, type, 3, larray->lmsgclass);
+
+ upb_array_set(larray->arr, n, msgval);
+
+ if (lupb_istypewrapped(type)) {
+ lupb_uservalseti(L, 1, n, 3);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static int lupb_array_index(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array *array = larray->arr;
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(array));
+ upb_fieldtype_t type = upb_array_type(array);
+
+ if (lupb_istypewrapped(type)) {
+ lupb_uservalgeti(L, 1, n);
+ } else {
+ lupb_pushmsgval(L, upb_array_type(array), upb_array_get(array, n));
+ }
+
+ return 1;
+}
+
+static int lupb_array_len(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ lua_pushnumber(L, upb_array_size(larray->arr));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_array_mm[] = {
+ {"__gc", lupb_array_gc},
+ {"__index", lupb_array_index},
+ {"__len", lupb_array_len},
+ {"__newindex", lupb_array_newindex},
+ {NULL, NULL}
+};
+
+
+/* lupb_map *******************************************************************/
+
+/* A map object. Implemented by wrapping upb_map.
+ *
+ * When the value type is string/bytes/message, the userval consists of:
+ *
+ * [Lua number/string] -> [lupb_string/lupb_msg userdata]
+ *
+ * For other value types we don't use the userdata.
+ */
+
+typedef struct {
+ const lupb_msgclass *value_lmsgclass;
+ upb_map *map;
+} lupb_map;
+
+#define MAP_MSGCLASS_INDEX 0
+
+/* lupb_map internal functions */
+
+static lupb_map *lupb_map_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+/**
+ * lupb_map_typecheck()
+ *
+ * Checks that the lupb_map at index |narg| can be safely assigned to the
+ * field |f| of the message at index |msg|. If so, returns a upb_msgval for
+ * this map. Otherwise, raises a Lua error.
+ */
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_map *lmap = lupb_map_check(L, narg);
+ upb_map *map = lmap->map;
+ const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+ const upb_fielddef *key_field = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
+ const upb_fielddef *value_field = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
+
+ UPB_ASSERT(entry && key_field && value_field);
+
+ if (upb_map_keytype(map) != upb_fielddef_type(key_field)) {
+ luaL_error(L, "Map key type invalid");
+ }
+
+ if (upb_map_valuetype(map) != upb_fielddef_type(value_field)) {
+ luaL_error(L, "Map had incorrect value type (expected: %s, got: %s)",
+ upb_fielddef_type(value_field), upb_map_valuetype(map));
+ }
+
+ if (upb_map_valuetype(map) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(
+ L, lupb_msg_msgclassfor(L, msg, upb_fielddef_msgsubdef(value_field)),
+ lmap->value_lmsgclass);
+ }
+
+ return upb_msgval_map(map);
+}
+
+static int lupb_map_gc(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map_uninit(lmap->map);
+ return 0;
+}
+
+/* lupb_map Public API */
+
+/**
+ * lupb_map_new
+ *
+ * Handles:
+ * new_map = upb.Map(key_type, value_type)
+ */
+static int lupb_map_new(lua_State *L) {
+ lupb_map *lmap;
+ upb_fieldtype_t key_type = lupb_checkfieldtype(L, 1);
+ upb_fieldtype_t value_type;
+ lupb_msgclass *value_lmsgclass = NULL;
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ value_type = lupb_checkfieldtype(L, 2);
+ } else {
+ value_type = UPB_TYPE_MESSAGE;
+ }
+
+ lmap = lupb_newuserdata(
+ L, sizeof(*lmap) + upb_map_sizeof(key_type, value_type), LUPB_MAP);
+ lmap->map = ADD_BYTES(lmap, sizeof(*lmap));
+
+ if (value_type == UPB_TYPE_MESSAGE) {
+ value_lmsgclass = lupb_msgclass_check(L, 2);
+ lupb_uservalseti(L, -1, MAP_MSGCLASS_INDEX, 2); /* GC-root lmsgclass. */
+ }
+
+ lmap->value_lmsgclass = value_lmsgclass;
+ upb_map_init(lmap->map, key_type, value_type, lupb_alloc_get(L));
+
+ return 1;
+}
+
+/**
+ * lupb_map_index
+ *
+ * Handles:
+ * map[key]
+ */
+static int lupb_map_index(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lmap->map;
+ upb_fieldtype_t valtype = upb_map_valuetype(map);
+ /* We don't always use "key", but this call checks the key type. */
+ upb_msgval key = lupb_tomsgval(L, upb_map_keytype(map), 2, NULL);
+
+ if (lupb_istypewrapped(valtype)) {
+ /* Userval contains the full map, lookup there by key. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ /* TODO: lazy read from upb_map */
+ }
+ } else {
+ /* Lookup in upb_map. */
+ upb_msgval val;
+ if (upb_map_get(map, key, &val)) {
+ lupb_pushmsgval(L, upb_map_valuetype(map), val);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * lupb_map_len
+ *
+ * Handles:
+ * map_len = #map
+ */
+static int lupb_map_len(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ lua_pushnumber(L, upb_map_size(lmap->map));
+ return 1;
+}
+
+/**
+ * lupb_map_newindex
+ *
+ * Handles:
+ * map[key] = val
+ * map[key] = nil # to remove from map
+ */
+static int lupb_map_newindex(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lmap->map;
+ upb_msgval key = lupb_tomsgval(L, upb_map_keytype(map), 2, NULL);
+
+ if (lua_isnil(L, 3)) {
+ /* Delete from map. */
+ upb_map_del(map, key);
+
+ if (lupb_istypewrapped(upb_map_valuetype(map))) {
+ /* Delete in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ } else {
+ /* Set in map. */
+ upb_msgval val =
+ lupb_tomsgval(L, upb_map_valuetype(map), 3, lmap->value_lmsgclass);
+
+ upb_map_set(map, key, val, NULL);
+
+ if (lupb_istypewrapped(upb_map_valuetype(map))) {
+ /* Set in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, 3);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ }
+
+ return 0;
+}
+
+/* upb_mapiter [[[ */
+
+static int lupb_mapiter_next(lua_State *L) {
+ upb_mapiter *i = lua_touserdata(L, lua_upvalueindex(1));
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lmap->map;
+
+ if (upb_mapiter_done(i)) {
+ return 0;
+ }
+
+ lupb_pushmsgval(L, upb_map_keytype(map), upb_mapiter_key(i));
+ lupb_pushmsgval(L, upb_map_valuetype(map), upb_mapiter_value(i));
+ upb_mapiter_next(i);
+
+ return 2;
+}
+
+static int lupb_map_pairs(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+
+ if (lupb_istypewrapped(upb_map_keytype(lmap->map)) ||
+ lupb_istypewrapped(upb_map_valuetype(lmap->map))) {
+ /* Complex key or value type.
+ * Sync upb_map to userval if necessary, then iterate over userval. */
+
+ /* TODO: Lua tables don't know how many entries they have, gah!. */
+ return 1;
+ } else {
+ /* Simple key and value type, iterate over the upb_map directly. */
+ upb_mapiter *i = lua_newuserdata(L, upb_mapiter_sizeof());
+
+ upb_mapiter_begin(i, lmap->map);
+ lua_pushvalue(L, 1);
+
+ /* Upvalues are [upb_mapiter, lupb_map]. */
+ lua_pushcclosure(L, &lupb_mapiter_next, 2);
+
+ return 1;
+ }
+}
+
+/* upb_mapiter ]]] */
+
+static const struct luaL_Reg lupb_map_mm[] = {
+ {"__gc", lupb_map_gc},
+ {"__index", lupb_map_index},
+ {"__len", lupb_map_len},
+ {"__newindex", lupb_map_newindex},
+ {"__pairs", lupb_map_pairs},
+ {NULL, NULL}
+};
+
+
+/* lupb_msg *******************************************************************/
+
+/* A message object. Implemented by wrapping upb_msg.
+ *
+ * Our userval contains:
+ *
+ * - [0] -> our message class
+ * - [lupb_fieldindex(f)] -> [lupb_{string,array,map,msg} userdata]
+ *
+ * Fields with scalar number/bool types don't go in the userval.
+ */
+
+#define LUPB_MSG_MSGCLASSINDEX 0
+#define LUPB_MSG_ARENA -1
+
+int lupb_fieldindex(const upb_fielddef *f) {
+ return upb_fielddef_index(f) + 1; /* 1-based Lua arrays. */
+}
+
+
+typedef struct {
+ const lupb_msgclass *lmsgclass;
+ upb_msg *msg;
+} lupb_msg;
+
+/* lupb_msg helpers */
+
+static bool in_userval(const upb_fielddef *f) {
+ return lupb_istypewrapped(upb_fielddef_type(f)) || upb_fielddef_isseq(f) ||
+ upb_fielddef_ismap(f);
+}
+
+lupb_msg *lupb_msg_check(lua_State *L, int narg) {
+ lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
+ if (!msg->lmsgclass) luaL_error(L, "called into dead msg");
+ return msg;
+}
+
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass) {
+ lupb_msg *msg = lupb_msg_check(L, narg);
+ lupb_msgclass_typecheck(L, lmsgclass, msg->lmsgclass);
+ return upb_msgval_msg(msg);
+}
+
+const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
+ return upb_msglayout_msgdef(lupb_msg_check(L, narg)->lmsgclass->layout);
+}
+
+static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
+ const lupb_msg *msg,
+ int fieldarg) {
+ size_t len;
+ const char *fieldname = luaL_checklstring(L, fieldarg, &len);
+ const upb_msgdef *msgdef = upb_msglayout_msgdef(msg->lmsgclass->layout);
+ const upb_fielddef *f = upb_msgdef_ntof(msgdef, fieldname, len);
+
+ if (!f) {
+ const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
+ luaL_argerror(L, fieldarg, msg);
+ return NULL; /* Never reached. */
+ }
+
+ return f;
+}
+
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_msgclassfor(L, -1, md);
+}
+
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_getsubmsgclass(L, -1, f);
+}
+
+int lupb_msg_pushref(lua_State *L, int msgclass, void *msg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, msgclass);
+ lupb_msg *lmsg = lupb_newuserdata(L, sizeof(lupb_msg), LUPB_MSG);
+
+ lmsg->lmsgclass = lmsgclass;
+ lmsg->msg = msg;
+
+ lupb_uservalseti(L, -1, LUPB_MSG_MSGCLASSINDEX, msgclass);
+ lupb_uservalseti(L, -1, LUPB_MSG_ARENA, -2);
+
+ return 1;
+}
+
+/* lupb_msg Public API */
+
+/**
+ * lupb_msg_pushnew
+ *
+ * Handles:
+ * new_msg = MessageClass()
+ */
+static int lupb_msg_pushnew(lua_State *L, int narg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg);
+ size_t size = sizeof(lupb_msg) + upb_msg_sizeof(lmsgclass->layout);
+ lupb_msg *lmsg = lupb_newuserdata(L, size, LUPB_MSG);
+
+ lmsg->lmsgclass = lmsgclass;
+ lmsg->msg = ADD_BYTES(lmsg, sizeof(*lmsg));
+ upb_msg_init(lmsg->msg, lmsgclass->layout, lupb_alloc_get(L));
+
+ lupb_uservalseti(L, -1, LUPB_MSG_MSGCLASSINDEX, narg);
+
+ return 1;
+}
+
+/**
+ * lupb_msg_index
+ *
+ * Handles:
+ * msg.foo
+ * msg["foo"]
+ * msg[field_descriptor] # (for extensions) (TODO)
+ */
+static int lupb_msg_index(lua_State *L) {
+ lupb_msg *lmsg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, lmsg, 2);
+ const upb_msglayout *l = lmsg->lmsgclass->layout;
+
+ if (in_userval(f)) {
+ lupb_uservalgeti(L, 1, lupb_fieldindex(f));
+
+ if (lua_isnil(L, -1)) {
+ /* Check if we need to lazily create wrapper. */
+ if (upb_fielddef_isseq(f)) {
+ /* TODO(haberman) */
+ } else if (upb_fielddef_issubmsg(f)) {
+ /* TODO(haberman) */
+ } else {
+ UPB_ASSERT(upb_fielddef_isstring(f));
+ if (upb_msg_has(lmsg->msg, f, l)) {
+ upb_msgval val = upb_msg_get(lmsg->msg, f, l);
+ lua_pop(L, 1);
+ lua_pushlstring(L, val.str.ptr, val.str.len);
+ lupb_uservalseti(L, 1, lupb_fieldindex(f), -1);
+ }
+ }
+ }
+ } else {
+ lupb_pushmsgval(L, upb_fielddef_type(f), upb_msg_get(lmsg->msg, f, l));
+ }
+
+ return 1;
+}
+
+/**
+ * lupb_msg_newindex()
+ *
+ * Handles:
+ * msg.foo = bar
+ * msg["foo"] = bar
+ * msg[field_descriptor] = bar # (for extensions) (TODO)
+ */
+static int lupb_msg_newindex(lua_State *L) {
+ lupb_msg *lmsg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, lmsg, 2);
+ upb_fieldtype_t type = upb_fielddef_type(f);
+ upb_msgval msgval;
+
+ /* Typecheck and get msgval. */
+
+ if (upb_fielddef_isseq(f)) {
+ msgval = lupb_array_typecheck(L, 3, 1, f);
+ } else if (upb_fielddef_ismap(f)) {
+ msgval = lupb_map_typecheck(L, 3, 1, f);
+ } else {
+ const lupb_msgclass *lmsgclass = NULL;
+
+ if (type == UPB_TYPE_MESSAGE) {
+ lmsgclass = lupb_msg_getsubmsgclass(L, 1, f);
+ }
+
+ msgval = lupb_tomsgval(L, type, 3, lmsgclass);
+ }
+
+ /* Set in upb_msg and userval (if necessary). */
+
+ upb_msg_set(lmsg->msg, f, msgval, lmsg->lmsgclass->layout);
+
+ if (in_userval(f)) {
+ lupb_uservalseti(L, 1, lupb_fieldindex(f), 3);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static const struct luaL_Reg lupb_msg_mm[] = {
+ {"__index", lupb_msg_index},
+ {"__newindex", lupb_msg_newindex},
+ {NULL, NULL}
+};
+
+
+/* lupb_msg toplevel **********************************************************/
+
+static const struct luaL_Reg lupb_msg_toplevel_m[] = {
+ {"Array", lupb_array_new},
+ {"Map", lupb_map_new},
+ {"MessageFactory", lupb_msgfactory_new},
+ {NULL, NULL}
+};
+
+void lupb_msg_registertypes(lua_State *L) {
+ lupb_setfuncs(L, lupb_msg_toplevel_m);
+
+ lupb_register_type(L, LUPB_ARENA, NULL, lupb_arena_mm);
+ lupb_register_type(L, LUPB_MSGCLASS, NULL, lupb_msgclass_mm);
+ lupb_register_type(L, LUPB_MSGFACTORY, lupb_msgfactory_m, lupb_msgfactory_mm);
+ lupb_register_type(L, LUPB_ARRAY, NULL, lupb_array_mm);
+ lupb_register_type(L, LUPB_MAP, NULL, lupb_map_mm);
+ lupb_register_type(L, LUPB_MSG, NULL, lupb_msg_mm);
+
+ lupb_alloc_initsingleton(L);
+}
diff --git a/upb/bindings/lua/upb.c b/upb/bindings/lua/upb.c
index 1483676..38fd24a 100644
--- a/upb/bindings/lua/upb.c
+++ b/upb/bindings/lua/upb.c
@@ -27,25 +27,8 @@
#include "lauxlib.h"
#include "upb/bindings/lua/upb.h"
#include "upb/handlers.h"
-#include "upb/pb/glue.h"
-#include "upb/shim/shim.h"
+#include "upb/msg.h"
-/* Lua metatable types. */
-#define LUPB_ENUMDEF "lupb.enumdef"
-#define LUPB_FIELDDEF "lupb.fielddef"
-#define LUPB_FILEDEF "lupb.filedef"
-#define LUPB_MSGDEF "lupb.msgdef"
-#define LUPB_ONEOFDEF "lupb.oneof"
-#define LUPB_SYMTAB "lupb.symtab"
-
-#define LUPB_ARRAY "lupb.array"
-#define LUPB_MSG "lupb.msg"
-
-/* Other table constants. */
-#define LUPB_OBJCACHE "lupb.objcache"
-
-static void lupb_msgdef_init(lua_State *L);
-static size_t lupb_msgdef_sizeof();
/* Lua compatibility code *****************************************************/
@@ -74,8 +57,6 @@ static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
luaL_register(L, name, funcs);
}
-#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
-
#elif LUA_VERSION_NUM == 502
int luaL_typerror(lua_State *L, int narg, const char *tname) {
@@ -95,8 +76,6 @@ static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
luaL_setfuncs(L, funcs, 0);
}
-#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
-
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
@@ -133,30 +112,17 @@ bool lupb_openlib(lua_State *L, void *ptr, const char *name,
return false;
}
-/* Pushes a new userdata with the given metatable and ensures that it has a
- * uservalue. */
-static void *newudata_with_userval(lua_State *L, size_t size,
- const char *type) {
- void *ret = lua_newuserdata(L, size);
-
- /* Set metatable. */
- luaL_getmetatable(L, type);
- UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
- lua_setmetatable(L, -2);
-
- lua_newtable(L);
- lua_setuservalue(L, -2);
-
- return ret;
+void lupb_checkstatus(lua_State *L, upb_status *s) {
+ if (!upb_ok(s)) {
+ lua_pushstring(L, upb_status_errmsg(s));
+ lua_error(L);
+ }
}
-const char *lupb_checkname(lua_State *L, int narg) {
- size_t len;
- const char *name = luaL_checklstring(L, narg, &len);
- if (strlen(name) != len)
- luaL_error(L, "names cannot have embedded NULLs");
- return name;
-}
+/* Scalar type mapping ********************************************************/
+
+/* Functions that convert scalar/primitive values (numbers, strings, bool)
+ * between Lua and C/upb. Handles type/range checking. */
bool lupb_checkbool(lua_State *L, int narg) {
if (!lua_isboolean(L, narg)) {
@@ -167,9 +133,12 @@ bool lupb_checkbool(lua_State *L, int narg) {
/* Unlike luaL_checkstring(), this does not allow implicit conversion to
* string. */
-void lupb_checkstring(lua_State *L, int narg) {
- if (lua_type(L, narg) != LUA_TSTRING)
+const char *lupb_checkstring(lua_State *L, int narg, size_t *len) {
+ if (lua_type(L, narg) != LUA_TSTRING) {
luaL_error(L, "Expected string");
+ }
+
+ return lua_tolstring(L, narg, len);
}
/* Unlike luaL_checkinteger, these do not implicitly convert from string or
@@ -235,1839 +204,19 @@ void lupb_pushfloat(lua_State *L, float d) {
lua_pushnumber(L, d);
}
-static void lupb_checkval(lua_State *L, int narg, upb_fieldtype_t type) {
- switch(type) {
- case UPB_TYPE_INT32:
- case UPB_TYPE_ENUM:
- lupb_checkint32(L, narg);
- break;
- case UPB_TYPE_INT64:
- lupb_checkint64(L, narg);
- break;
- case UPB_TYPE_UINT32:
- lupb_checkuint32(L, narg);
- break;
- case UPB_TYPE_UINT64:
- lupb_checkuint64(L, narg);
- break;
- case UPB_TYPE_DOUBLE:
- lupb_checkdouble(L, narg);
- break;
- case UPB_TYPE_FLOAT:
- lupb_checkfloat(L, narg);
- break;
- case UPB_TYPE_BOOL:
- lupb_checkbool(L, narg);
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- lupb_checkstring(L, narg);
- break;
- case UPB_TYPE_MESSAGE:
- lupb_assert(L, false);
- }
-}
-
-void lupb_checkstatus(lua_State *L, upb_status *s) {
- if (!upb_ok(s)) {
- lua_pushstring(L, upb_status_errmsg(s));
- lua_error(L);
- }
-}
-
-static upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg) {
- int type = luaL_checkint(L, narg);
- if (!upb_fielddef_checktype(type))
- luaL_argerror(L, narg, "invalid field type");
- return type;
-}
-
-#define CHK(pred) do { \
- upb_status status = UPB_STATUS_INIT; \
- pred; \
- lupb_checkstatus(L, &status); \
- } while (0)
-
-
-/* lupb_refcounted ************************************************************/
-
-/* All upb objects that use upb_refcounted have a userdata that begins with a
- * pointer to that object. Each type has its own metatable. Objects are cached
- * in a weak table indexed by the C pointer of the object they are caching.
- *
- * Note that we consistently use memcpy() to read to/from the object. This
- * allows the userdata to use its own struct without violating aliasing, as
- * long as it begins with a pointer. */
-
-/* Checks type; if it matches, pulls the pointer out of the wrapper. */
-void *lupb_refcounted_check(lua_State *L, int narg, const char *type) {
- void *ud = luaL_checkudata(L, narg, type);
- void *ret;
- memcpy(&ret, ud, sizeof ret);
- if (!ret) luaL_error(L, "called into dead object");
- return ret;
-}
-
-bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
- const char *type, const void *ref_donor,
- size_t size) {
- bool create;
- void *ud;
-
- if (obj == NULL) {
- lua_pushnil(L);
- return false;
- }
-
- /* Lookup our cache in the registry (we don't put our objects in the registry
- * directly because we need our cache to be a weak table). */
- lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
- UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
- lua_pushlightuserdata(L, (void*)obj);
- lua_rawget(L, -2);
- /* Stack is now: objcache, cached value. */
-
- create = false;
-
- if (lua_isnil(L, -1)) {
- create = true;
- } else {
- void *ud = lua_touserdata(L, -1);
- void *ud_obj;
- lupb_assert(L, ud);
- memcpy(&ud_obj, ud, sizeof(void*));
-
- /* A corner case: it is possible for the value to be GC'd
- * already, in which case we should evict this entry and create
- * a new one. */
- if (ud_obj == NULL) {
- create = true;
- }
- }
-
- ud = NULL;
-
- if (create) {
- /* Remove bad cached value and push new value. */
- lua_pop(L, 1);
-
- /* All of our userdata begin with a pointer to the obj. */
- ud = lua_newuserdata(L, size);
- memcpy(ud, &obj, sizeof(void*));
- upb_refcounted_donateref(obj, ref_donor, ud);
-
- luaL_getmetatable(L, type);
- /* Should have been created by luaopen_upb. */
- lupb_assert(L, !lua_isnil(L, -1));
- lua_setmetatable(L, -2);
-
- /* Set it in the cache. */
- lua_pushlightuserdata(L, (void*)obj);
- lua_pushvalue(L, -2);
- lua_rawset(L, -4);
- } else {
- /* Existing wrapper obj already has a ref. */
- ud = lua_touserdata(L, -1);
- upb_refcounted_checkref(obj, ud);
- if (ref_donor)
- upb_refcounted_unref(obj, ref_donor);
- }
-
- lua_insert(L, -2);
- lua_pop(L, 1);
- return create;
-}
-
-void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
- const char *type, const void *ref_donor) {
- bool created =
- lupb_refcounted_pushwrapper(L, obj, type, ref_donor, sizeof(void *));
- UPB_ASSERT(created == true);
-}
-
-static int lupb_refcounted_gc(lua_State *L) {
- void *ud = lua_touserdata(L, 1);
- void *nullp;
- upb_refcounted *obj;
- memcpy(&obj, ud, sizeof(obj));
- upb_refcounted_unref(obj, ud);
-
- /* Zero out pointer so we can detect a call into a GC'd object. */
- nullp = NULL;
- memcpy(ud, &nullp, sizeof(nullp));
-
- return 0;
-}
-
-static const struct luaL_Reg lupb_refcounted_mm[] = {
- {"__gc", lupb_refcounted_gc},
- {NULL, NULL}
-};
-
-
-/* lupb_def *******************************************************************/
-
-static const upb_def *lupb_def_check(lua_State *L, int narg) {
- upb_def *ret;
- void *ud = luaL_testudata(L, narg, LUPB_MSGDEF);
- if (!ud) ud = luaL_testudata(L, narg, LUPB_ENUMDEF);
- if (!ud) ud = luaL_testudata(L, narg, LUPB_FIELDDEF);
- if (!ud) luaL_typerror(L, narg, "upb def");
-
- memcpy(&ret, ud, sizeof ret);
- if (!ret) luaL_error(L, "called into dead object");
- return ret;
-}
-
-static upb_def *lupb_def_checkmutable(lua_State *L, int narg) {
- const upb_def *def = lupb_def_check(L, narg);
- if (upb_def_isfrozen(def))
- luaL_error(L, "not allowed on frozen value");
- return (upb_def*)def;
-}
-
-bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
- const void *ref_donor) {
- const char *type = NULL;
- size_t size = sizeof(void*);
- bool created;
-
- if (def == NULL) {
- lua_pushnil(L);
- return false;
- }
-
- switch (upb_def_type(def)) {
- case UPB_DEF_MSG: {
- type = LUPB_MSGDEF;
- size = lupb_msgdef_sizeof();
- break;
- }
- case UPB_DEF_ENUM: type = LUPB_ENUMDEF; break;
- case UPB_DEF_FIELD: type = LUPB_FIELDDEF; break;
- default: luaL_error(L, "unknown deftype %d", upb_def_type(def));
- }
-
- created =
- lupb_refcounted_pushwrapper(L, upb_def_upcast(def), type, ref_donor, size);
-
- if (created && upb_def_type(def) == UPB_DEF_MSG) {
- lupb_msgdef_init(L);
- }
-
- return created;
-}
-
-void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
- const void *ref_donor) {
- bool created = lupb_def_pushwrapper(L, def, ref_donor);
- UPB_ASSERT(created == true);
-}
-
-static int lupb_def_type(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushinteger(L, upb_def_type(def));
- return 1;
-}
-
-static int lupb_def_freeze(lua_State *L) {
- upb_def *def = lupb_def_checkmutable(L, 1);
- CHK(upb_def_freeze(&def, 1, &status));
- return 0;
-}
-
-static int lupb_def_isfrozen(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushboolean(L, upb_def_isfrozen(def));
- return 1;
-}
-
-static int lupb_def_fullname(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushstring(L, upb_def_fullname(def));
- return 1;
-}
-
-static int lupb_def_name(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushstring(L, upb_def_name(def));
- return 1;
-}
-
-static int lupb_def_setfullname(lua_State *L) {
- const char *name = lupb_checkname(L, 2);
- CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), name, &status));
- return 0;
-}
-
-#define LUPB_COMMON_DEF_METHODS \
- {"def_type", lupb_def_type}, \
- {"full_name", lupb_def_fullname}, \
- {"freeze", lupb_def_freeze}, \
- {"is_frozen", lupb_def_isfrozen}, \
- {"name", lupb_def_name}, \
- {"set_full_name", lupb_def_setfullname}, \
-
-
-/* lupb_fielddef **************************************************************/
-
-void lupb_fielddef_pushwrapper(lua_State *L, const upb_fielddef *f,
- const void *ref_donor) {
- lupb_def_pushwrapper(L, upb_fielddef_upcast(f), ref_donor);
-}
-
-const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_FIELDDEF);
-}
-
-static upb_fielddef *lupb_fielddef_checkmutable(lua_State *L, int narg) {
- const upb_fielddef *f = lupb_fielddef_check(L, narg);
- if (upb_fielddef_isfrozen(f))
- luaL_error(L, "not allowed on frozen value");
- return (upb_fielddef*)f;
-}
-
-static int lupb_fielddef_new(lua_State *L) {
- upb_fielddef *f = upb_fielddef_new(&f);
- lupb_def_pushnewrapper(L, upb_fielddef_upcast(f), &f);
- return 1;
-}
-
-/* Getters */
-
-static int lupb_fielddef_containingtype(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lupb_msgdef_pushwrapper(L, upb_fielddef_containingtype(f), NULL);
- return 1;
-}
-
-static int lupb_fielddef_containingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lua_pushstring(L, upb_fielddef_containingtypename(f));
- return 1;
-}
-
-static int lupb_fielddef_default(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_INT32:
- int32:
- lupb_pushint32(L, upb_fielddef_defaultint32(f)); break;
- case UPB_TYPE_INT64:
- lupb_pushint64(L, upb_fielddef_defaultint64(f)); break;
- case UPB_TYPE_UINT32:
- lupb_pushuint32(L, upb_fielddef_defaultuint32(f)); break;
- case UPB_TYPE_UINT64:
- lupb_pushuint64(L, upb_fielddef_defaultuint64(f)); break;
- case UPB_TYPE_DOUBLE:
- lua_pushnumber(L, upb_fielddef_defaultdouble(f)); break;
- case UPB_TYPE_FLOAT:
- lua_pushnumber(L, upb_fielddef_defaultfloat(f)); break;
- case UPB_TYPE_BOOL:
- lua_pushboolean(L, upb_fielddef_defaultbool(f)); break;
- case UPB_TYPE_ENUM:
- if (upb_fielddef_enumhasdefaultstr(f)) {
- goto str;
- } else if (upb_fielddef_enumhasdefaultint32(f)) {
- goto int32;
- } else {
- lua_pushnil(L);
- }
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- str: {
- size_t len;
- const char *data = upb_fielddef_defaultstr(f, &len);
- lua_pushlstring(L, data, len);
- break;
- }
- case UPB_TYPE_MESSAGE:
- return luaL_error(L, "Message fields do not have explicit defaults.");
- }
- return 1;
-}
-
-static int lupb_fielddef_getsel(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- upb_selector_t sel;
- if (upb_handlers_getselector(f, luaL_checknumber(L, 2), &sel)) {
- lua_pushinteger(L, sel);
- return 1;
- } else {
- return 0;
- }
-}
-
-static int lupb_fielddef_hassubdef(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_hassubdef(f));
- return 1;
-}
-
-static int lupb_fielddef_index(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushinteger(L, upb_fielddef_index(f));
- return 1;
-}
-
-static int lupb_fielddef_intfmt(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushinteger(L, upb_fielddef_intfmt(f));
- return 1;
-}
-
-static int lupb_fielddef_isextension(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_isextension(f));
- return 1;
-}
-
-static int lupb_fielddef_istagdelim(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_istagdelim(f));
- return 1;
-}
-
-static int lupb_fielddef_label(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushinteger(L, upb_fielddef_label(f));
- return 1;
-}
-
-static int lupb_fielddef_lazy(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_lazy(f));
- return 1;
-}
-
-static int lupb_fielddef_name(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushstring(L, upb_fielddef_name(f));
- return 1;
-}
-
-static int lupb_fielddef_number(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- int32_t num = upb_fielddef_number(f);
- if (num)
- lua_pushinteger(L, num);
- else
- lua_pushnil(L);
- return 1;
-}
-
-static int lupb_fielddef_packed(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_packed(f));
- return 1;
-}
-
-static int lupb_fielddef_subdef(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- const upb_def *def;
-
- if (!upb_fielddef_hassubdef(f))
- luaL_error(L, "Tried to get subdef of non-message field");
- def = upb_fielddef_subdef(f);
- lupb_def_pushwrapper(L, def, NULL);
- return 1;
-}
-
-static int lupb_fielddef_subdefname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- if (!upb_fielddef_hassubdef(f))
- luaL_error(L, "Tried to get subdef name of non-message field");
- lua_pushstring(L, upb_fielddef_subdefname(f));
- return 1;
-}
-
-static int lupb_fielddef_type(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- if (upb_fielddef_typeisset(f))
- lua_pushinteger(L, upb_fielddef_type(f));
- else
- lua_pushnil(L);
- return 1;
-}
-
-/* Setters */
-
-static int lupb_fielddef_setcontainingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- const char *name = NULL;
- if (!lua_isnil(L, 2))
- name = lupb_checkname(L, 2);
- CHK(upb_fielddef_setcontainingtypename(f, name, &status));
- return 0;
-}
-
-static int lupb_fielddef_setdefault(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
-
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_INT32:
- upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
- break;
- case UPB_TYPE_INT64:
- upb_fielddef_setdefaultint64(f, lupb_checkint64(L, 2));
- break;
- case UPB_TYPE_UINT32:
- upb_fielddef_setdefaultuint32(f, lupb_checkuint32(L, 2));
- break;
- case UPB_TYPE_UINT64:
- upb_fielddef_setdefaultuint64(f, lupb_checkuint64(L, 2));
- break;
- case UPB_TYPE_DOUBLE:
- upb_fielddef_setdefaultdouble(f, lupb_checkdouble(L, 2));
- break;
- case UPB_TYPE_FLOAT:
- upb_fielddef_setdefaultfloat(f, lupb_checkfloat(L, 2));
- break;
- case UPB_TYPE_BOOL:
- upb_fielddef_setdefaultbool(f, lupb_checkbool(L, 2));
- break;
- case UPB_TYPE_MESSAGE:
- return luaL_error(L, "Message types cannot have defaults.");
- case UPB_TYPE_ENUM:
- if (lua_type(L, 2) != LUA_TSTRING) {
- upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
- break;
- }
- /* Else fall through and set string default. */
- case UPB_TYPE_BYTES:
- case UPB_TYPE_STRING: {
- size_t len;
- const char *str = lua_tolstring(L, 2, &len);
- CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
- }
- }
- return 0;
-}
-
-static int lupb_fielddef_setisextension(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- CHK(upb_fielddef_setisextension(f, lupb_checkbool(L, 2)));
- return 0;
-}
-
-static int lupb_fielddef_setlabel(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- int label = luaL_checkint(L, 2);
- if (!upb_fielddef_checklabel(label))
- luaL_argerror(L, 2, "invalid field label");
- upb_fielddef_setlabel(f, label);
- return 0;
-}
-
-static int lupb_fielddef_setlazy(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- upb_fielddef_setlazy(f, lupb_checkbool(L, 2));
- return 0;
-}
-
-static int lupb_fielddef_setname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- CHK(upb_fielddef_setname(f, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static int lupb_fielddef_setnumber(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- CHK(upb_fielddef_setnumber(f, luaL_checkint(L, 2), &status));
- return 0;
-}
-
-static int lupb_fielddef_setpacked(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- upb_fielddef_setpacked(f, lupb_checkbool(L, 2));
- return 0;
-}
-
-static int lupb_fielddef_setsubdef(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- const upb_def *def = NULL;
- if (!lua_isnil(L, 2))
- def = lupb_def_check(L, 2);
- CHK(upb_fielddef_setsubdef(f, def, &status));
- return 0;
-}
-
-static int lupb_fielddef_setsubdefname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- const char *name = NULL;
- if (!lua_isnil(L, 2))
- name = lupb_checkname(L, 2);
- CHK(upb_fielddef_setsubdefname(f, name, &status));
- return 0;
-}
-
-static int lupb_fielddef_settype(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- upb_fielddef_settype(f, lupb_checkfieldtype(L, 2));
- return 0;
-}
-
-static int lupb_fielddef_setintfmt(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- int32_t intfmt = luaL_checknumber(L, 2);
- if (!upb_fielddef_checkintfmt(intfmt))
- luaL_argerror(L, 2, "invalid intfmt");
- upb_fielddef_setintfmt(f, intfmt);
- return 0;
-}
-
-static int lupb_fielddef_settagdelim(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- bool is_tag_delim = lupb_checkbool(L, 2);
- CHK(upb_fielddef_settagdelim(f, is_tag_delim));
- return 0;
-}
-
-static const struct luaL_Reg lupb_fielddef_m[] = {
- LUPB_COMMON_DEF_METHODS
-
- {"containing_type", lupb_fielddef_containingtype},
- {"containing_type_name", lupb_fielddef_containingtypename},
- {"default", lupb_fielddef_default},
- {"getsel", lupb_fielddef_getsel},
- {"has_subdef", lupb_fielddef_hassubdef},
- {"index", lupb_fielddef_index},
- {"intfmt", lupb_fielddef_intfmt},
- {"is_extension", lupb_fielddef_isextension},
- {"istagdelim", lupb_fielddef_istagdelim},
- {"label", lupb_fielddef_label},
- {"lazy", lupb_fielddef_lazy},
- {"name", lupb_fielddef_name},
- {"number", lupb_fielddef_number},
- {"packed", lupb_fielddef_packed},
- {"subdef", lupb_fielddef_subdef},
- {"subdef_name", lupb_fielddef_subdefname},
- {"type", lupb_fielddef_type},
-
- {"set_containing_type_name", lupb_fielddef_setcontainingtypename},
- {"set_default", lupb_fielddef_setdefault},
- {"set_is_extension", lupb_fielddef_setisextension},
- {"set_label", lupb_fielddef_setlabel},
- {"set_lazy", lupb_fielddef_setlazy},
- {"set_name", lupb_fielddef_setname},
- {"set_number", lupb_fielddef_setnumber},
- {"set_packed", lupb_fielddef_setpacked},
- {"set_subdef", lupb_fielddef_setsubdef},
- {"set_subdef_name", lupb_fielddef_setsubdefname},
- {"set_type", lupb_fielddef_settype},
- {"set_intfmt", lupb_fielddef_setintfmt},
- {"set_tagdelim", lupb_fielddef_settagdelim},
-
- {NULL, NULL}
-};
-
-
-/* lupb_oneofdef **************************************************************/
-
-void lupb_oneofdef_pushwrapper(lua_State *L, const upb_oneofdef *o,
- const void *ref_donor) {
- lupb_refcounted_pushwrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF,
- ref_donor, sizeof(void *));
-}
-
-const upb_oneofdef *lupb_oneofdef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_ONEOFDEF);
-}
-
-static upb_oneofdef *lupb_oneofdef_checkmutable(lua_State *L, int narg) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, narg);
- if (upb_oneofdef_isfrozen(o))
- luaL_error(L, "not allowed on frozen value");
- return (upb_oneofdef*)o;
-}
-
-static int lupb_oneofdef_new(lua_State *L) {
- upb_oneofdef *o = upb_oneofdef_new(&o);
- lupb_refcounted_pushnewrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF, &o);
- return 1;
-}
-
-/* Getters */
-
-static int lupb_oneofdef_containingtype(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- lupb_def_pushwrapper(L, upb_msgdef_upcast(upb_oneofdef_containingtype(o)),
- NULL);
- return 1;
-}
-
-static int lupb_oneofdef_field(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- int type = lua_type(L, 2);
- const upb_fielddef *f;
- if (type == LUA_TNUMBER) {
- f = upb_oneofdef_itof(o, lua_tointeger(L, 2));
- } else if (type == LUA_TSTRING) {
- f = upb_oneofdef_ntofz(o, lua_tostring(L, 2));
- } else {
- const char *msg = lua_pushfstring(L, "number or string expected, got %s",
- luaL_typename(L, 2));
- return luaL_argerror(L, 2, msg);
- }
-
- lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
- return 1;
-}
-
-static int lupb_oneofdef_len(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- lua_pushinteger(L, upb_oneofdef_numfields(o));
- return 1;
-}
-
-static int lupb_oneofdef_name(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- lua_pushstring(L, upb_oneofdef_name(o));
- return 1;
-}
-
-/* Setters */
-
-static int lupb_oneofdef_add(lua_State *L) {
- upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
- CHK(upb_oneofdef_addfield(o, f, NULL, &status));
- return 0;
-}
-
-static int lupb_oneofdef_setname(lua_State *L) {
- upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
- CHK(upb_oneofdef_setname(o, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static const struct luaL_Reg lupb_oneofdef_m[] = {
- {"containing_type", lupb_oneofdef_containingtype},
- {"field", lupb_oneofdef_field},
- {"name", lupb_oneofdef_name},
-
- {"add", lupb_oneofdef_add},
- {"set_name", lupb_oneofdef_setname},
-
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_oneofdef_mm[] = {
- {"__len", lupb_oneofdef_len},
- {NULL, NULL}
-};
-
-
-/* lupb_msgdef ****************************************************************/
-
-typedef struct {
- const upb_msgdef *md;
-
- /* These members are initialized lazily the first time a message is created
- * for this def. */
- uint16_t *field_offsets;
- size_t msg_size;
- size_t hasbits_size;
- lua_State *L;
-} lupb_msgdef;
-
-static size_t lupb_msgdef_sizeof() {
- return sizeof(lupb_msgdef);
-}
-
-void lupb_msgdef_pushwrapper(lua_State *L, const upb_msgdef *m,
- const void *ref_donor) {
- lupb_def_pushwrapper(L, upb_msgdef_upcast(m), ref_donor);
-}
-
-const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_MSGDEF);
-}
-
-lupb_msgdef *lupb_msgdef_check2(lua_State *L, int narg) {
- return luaL_checkudata(L, narg, LUPB_MSGDEF);
-}
-
-static upb_msgdef *lupb_msgdef_checkmutable(lua_State *L, int narg) {
- const upb_msgdef *m = lupb_msgdef_check(L, narg);
- if (upb_msgdef_isfrozen(m))
- luaL_error(L, "not allowed on frozen value");
- return (upb_msgdef*)m;
-}
-
-static int lupb_msgdef_new(lua_State *L) {
- upb_msgdef *md = upb_msgdef_new(&md);
- lupb_def_pushnewrapper(L, upb_msgdef_upcast(md), &md);
- return 1;
-}
-
-/* Unlike other refcounted types we need a custom __gc so that we free our field
- * offsets. */
-static int lupb_msgdef_gc(lua_State *L) {
- lupb_msgdef *lmd = luaL_checkudata(L, 1, LUPB_MSGDEF);
- free(lmd->field_offsets);
- lupb_refcounted_gc(L);
- return 0;
-}
-
-static void lupb_msgdef_init(lua_State *L) {
- lupb_msgdef *lmd = luaL_checkudata(L, -1, LUPB_MSGDEF);
- lmd->L = L;
- lmd->field_offsets = NULL;
-}
-
-static int lupb_msgdef_add(lua_State *L) {
- upb_msgdef *m = lupb_msgdef_checkmutable(L, 1);
-
- /* Both oneofs and fields can be added. */
- if (luaL_testudata(L, 2, LUPB_FIELDDEF)) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
- CHK(upb_msgdef_addfield(m, f, NULL, &status));
- } else if (luaL_testudata(L, 2, LUPB_ONEOFDEF)) {
- upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 2);
- CHK(upb_msgdef_addoneof(m, o, NULL, &status));
- }
- return 0;
-}
-
-static int lupb_msgdef_len(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- lua_pushinteger(L, upb_msgdef_numfields(m));
- return 1;
-}
-
-static int lupb_msgdef_field(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- int type = lua_type(L, 2);
- const upb_fielddef *f;
- if (type == LUA_TNUMBER) {
- f = upb_msgdef_itof(m, lua_tointeger(L, 2));
- } else if (type == LUA_TSTRING) {
- f = upb_msgdef_ntofz(m, lua_tostring(L, 2));
- } else {
- const char *msg = lua_pushfstring(L, "number or string expected, got %s",
- luaL_typename(L, 2));
- return luaL_argerror(L, 2, msg);
- }
-
- lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
- return 1;
-}
-
-static int lupb_msgdef_lookupname(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- const upb_fielddef *f;
- const upb_oneofdef *o;
- if (!upb_msgdef_lookupnamez(m, lua_tostring(L, 2), &f, &o)) {
- lua_pushnil(L);
- } else if (o) {
- lupb_oneofdef_pushwrapper(L, o, NULL);
- } else {
- lupb_fielddef_pushwrapper(L, f, NULL);
- }
- return 1;
-}
-
-static int lupb_msgfielditer_next(lua_State *L) {
- upb_msg_field_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_msg_field_done(i)) return 0;
- lupb_def_pushwrapper(L, upb_fielddef_upcast(upb_msg_iter_field(i)), NULL);
- upb_msg_field_next(i);
- return 1;
-}
-
-static int lupb_msgdef_fields(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- upb_msg_field_iter *i = lua_newuserdata(L, sizeof(upb_msg_field_iter));
- upb_msg_field_begin(i, m);
- /* Need to guarantee that the msgdef outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_msgfielditer_next, 2);
- return 1;
-}
-
-static int lupb_msgoneofiter_next(lua_State *L) {
- upb_msg_oneof_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_msg_oneof_done(i)) return 0;
- lupb_oneofdef_pushwrapper(L, upb_msg_iter_oneof(i), NULL);
- upb_msg_oneof_next(i);
- return 1;
-}
-
-static int lupb_msgdef_oneofs(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- upb_msg_oneof_iter *i = lua_newuserdata(L, sizeof(upb_msg_oneof_iter));
- upb_msg_oneof_begin(i, m);
- /* Need to guarantee that the msgdef outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_msgoneofiter_next, 2);
- return 1;
-}
-
-static int lupb_msgdef_mapentry(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- lua_pushboolean(L, upb_msgdef_mapentry(m));
- return 1;
-}
-
-static int lupb_msgdef_syntax(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- lua_pushinteger(L, upb_msgdef_syntax(m));
- return 1;
-}
-
-static const struct luaL_Reg lupb_msgdef_mm[] = {
- {"__gc", lupb_msgdef_gc},
- {"__len", lupb_msgdef_len},
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_msgdef_m[] = {
- LUPB_COMMON_DEF_METHODS
- {"add", lupb_msgdef_add},
- {"field", lupb_msgdef_field},
- {"fields", lupb_msgdef_fields},
- {"lookup_name", lupb_msgdef_lookupname},
- {"oneofs", lupb_msgdef_oneofs},
- {"syntax", lupb_msgdef_syntax},
- {"_map_entry", lupb_msgdef_mapentry},
-
- {NULL, NULL}
-};
-
-
-/* lupb_enumdef ***************************************************************/
-
-const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_ENUMDEF);
-}
-
-static upb_enumdef *lupb_enumdef_checkmutable(lua_State *L, int narg) {
- const upb_enumdef *f = lupb_enumdef_check(L, narg);
- if (upb_enumdef_isfrozen(f))
- luaL_error(L, "not allowed on frozen value");
- return (upb_enumdef*)f;
-}
-
-static int lupb_enumdef_new(lua_State *L) {
- upb_enumdef *e = upb_enumdef_new(&e);
- lupb_def_pushnewrapper(L, upb_enumdef_upcast(e), &e);
- return 1;
-}
-
-static int lupb_enumdef_add(lua_State *L) {
- upb_enumdef *e = lupb_enumdef_checkmutable(L, 1);
- const char *name = lupb_checkname(L, 2);
- int32_t val = lupb_checkint32(L, 3);
- CHK(upb_enumdef_addval(e, name, val, &status));
- return 0;
-}
-
-static int lupb_enumdef_len(lua_State *L) {
- const upb_enumdef *e = lupb_enumdef_check(L, 1);
- lua_pushinteger(L, upb_enumdef_numvals(e));
- return 1;
-}
-
-static int lupb_enumdef_value(lua_State *L) {
- const upb_enumdef *e = lupb_enumdef_check(L, 1);
- int type = lua_type(L, 2);
- if (type == LUA_TNUMBER) {
- /* Pushes "nil" for a NULL pointer. */
- int32_t key = lupb_checkint32(L, 2);
- lua_pushstring(L, upb_enumdef_iton(e, key));
- } else if (type == LUA_TSTRING) {
- const char *key = lua_tostring(L, 2);
- int32_t num;
- if (upb_enumdef_ntoiz(e, key, &num)) {
- lua_pushinteger(L, num);
- } else {
- lua_pushnil(L);
- }
- } else {
- const char *msg = lua_pushfstring(L, "number or string expected, got %s",
- luaL_typename(L, 2));
- return luaL_argerror(L, 2, msg);
- }
- return 1;
-}
-
-static int lupb_enumiter_next(lua_State *L) {
- upb_enum_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_enum_done(i)) return 0;
- lua_pushstring(L, upb_enum_iter_name(i));
- lua_pushinteger(L, upb_enum_iter_number(i));
- upb_enum_next(i);
- return 2;
-}
-
-static int lupb_enumdef_values(lua_State *L) {
- const upb_enumdef *e = lupb_enumdef_check(L, 1);
- upb_enum_iter *i = lua_newuserdata(L, sizeof(upb_enum_iter));
- upb_enum_begin(i, e);
- /* Need to guarantee that the enumdef outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_enumiter_next, 2);
- return 1;
-}
-
-static const struct luaL_Reg lupb_enumdef_mm[] = {
- {"__len", lupb_enumdef_len},
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_enumdef_m[] = {
- LUPB_COMMON_DEF_METHODS
- {"add", lupb_enumdef_add},
- {"value", lupb_enumdef_value},
- {"values", lupb_enumdef_values},
- {NULL, NULL}
-};
-
-
-/* lupb_filedef ***************************************************************/
-
-void lupb_filedef_pushwrapper(lua_State *L, const upb_filedef *f,
- const void *ref_donor) {
- lupb_refcounted_pushwrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF, ref_donor,
- sizeof(void *));
-}
-
-void lupb_filedef_pushnewrapper(lua_State *L, const upb_filedef *f,
- const void *ref_donor) {
- lupb_refcounted_pushnewrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF,
- ref_donor);
-}
-
-const upb_filedef *lupb_filedef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_FILEDEF);
-}
-
-static upb_filedef *lupb_filedef_checkmutable(lua_State *L, int narg) {
- const upb_filedef *f = lupb_filedef_check(L, narg);
- if (upb_filedef_isfrozen(f))
- luaL_error(L, "not allowed on frozen value");
- return (upb_filedef*)f;
-}
-
-static int lupb_filedef_new(lua_State *L) {
- upb_filedef *f = upb_filedef_new(&f);
- lupb_filedef_pushnewrapper(L, f, &f);
- return 1;
-}
-
-static int lupb_filedef_def(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- int index = luaL_checkint(L, 2);
- const upb_def *def = upb_filedef_def(f, index);
-
- if (!def) {
- return luaL_error(L, "index out of range");
- }
-
- lupb_def_pushwrapper(L, def, NULL);
- return 1;
-}
-
-static int lupb_filedef_name(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- lua_pushstring(L, upb_filedef_name(f));
- return 1;
-}
-
-static int lupb_filedef_package(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- lua_pushstring(L, upb_filedef_package(f));
- return 1;
-}
-
-static int lupb_filedef_len(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- lua_pushinteger(L, upb_filedef_defcount(f));
- return 1;
-}
-
-static int lupb_filedef_setname(lua_State *L) {
- upb_filedef *f = lupb_filedef_checkmutable(L, 1);
- CHK(upb_filedef_setname(f, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static int lupb_filedef_setpackage(lua_State *L) {
- upb_filedef *f = lupb_filedef_checkmutable(L, 1);
- CHK(upb_filedef_setpackage(f, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static int lupb_filedefiter_next(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, lua_upvalueindex(1));
- int type = lua_tointeger(L, lua_upvalueindex(2));
- size_t i = lua_tointeger(L, lua_upvalueindex(3));
- size_t n = upb_filedef_defcount(f);
-
- for (; i < n; i++) {
- const upb_def *def;
-
- def = upb_filedef_def(f, i);
- UPB_ASSERT(def);
-
- if (type == UPB_DEF_ANY || upb_def_type(def) == type) {
- lua_pushinteger(L, i + 1);
- lua_replace(L, lua_upvalueindex(3));
- lupb_def_pushwrapper(L, def, NULL);
- return 1;
- }
- }
-
- return 0;
-}
-
-static int lupb_filedef_defs(lua_State *L) {
- lupb_filedef_check(L, 1);
- luaL_checkinteger(L, 2); /* Def type. Could make this optional. */
- lua_pushnumber(L, 0); /* Index, starts at zero. */
- lua_pushcclosure(L, &lupb_filedefiter_next, 3);
- return 1;
-}
-
-static const struct luaL_Reg lupb_filedef_mm[] = {
- {"__len", lupb_filedef_len},
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_filedef_m[] = {
- {"def", lupb_filedef_def},
- {"defs", lupb_filedef_defs},
- {"name", lupb_filedef_name},
- {"package", lupb_filedef_package},
-
- {"set_name", lupb_filedef_setname},
- {"set_package", lupb_filedef_setpackage},
-
- {NULL, NULL}
-};
-
-/* lupb_symtab ****************************************************************/
-
-/* Inherits a ref on the symtab.
- * Checks that narg is a proper lupb_symtab object. If it is, leaves its
- * metatable on the stack for cache lookups/updates. */
-const upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_SYMTAB);
-}
-
-static upb_symtab *lupb_symtab_checkmutable(lua_State *L, int narg) {
- const upb_symtab *s = lupb_symtab_check(L, narg);
- if (upb_symtab_isfrozen(s))
- luaL_error(L, "not allowed on frozen value");
- return (upb_symtab*)s;
-}
-
-void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
- const void *ref_donor) {
- lupb_refcounted_pushwrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB, ref_donor,
- sizeof(void *));
-}
-
-void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
- const void *ref_donor) {
- lupb_refcounted_pushnewrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB,
- ref_donor);
-}
-
-static int lupb_symtab_new(lua_State *L) {
- upb_symtab *s = upb_symtab_new(&s);
- lupb_symtab_pushnewrapper(L, s, &s);
- return 1;
-}
-
-static int lupb_symtab_freeze(lua_State *L) {
- upb_symtab_freeze(lupb_symtab_checkmutable(L, 1));
- return 0;
-}
-
-static int lupb_symtab_isfrozen(lua_State *L) {
- lua_pushboolean(L, upb_symtab_isfrozen(lupb_symtab_check(L, 1)));
- return 1;
-}
-
-static int lupb_symtab_add(lua_State *L) {
- upb_symtab *s = lupb_symtab_checkmutable(L, 1);
- int n;
- upb_def **defs;
-
- luaL_checktype(L, 2, LUA_TTABLE);
- /* Iterate over table twice. First iteration to count entries and
- * check constraints. */
- n = 0;
- for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
- lupb_def_checkmutable(L, -1);
- ++n;
- }
-
- /* Second iteration to build deflist.
- * Allocate list with lua_newuserdata() so it is anchored as a GC root in
- * case any Lua functions longjmp(). */
- defs = lua_newuserdata(L, n * sizeof(*defs));
- n = 0;
- for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
- upb_def *def = lupb_def_checkmutable(L, -1);
- defs[n++] = def;
- }
-
- CHK(upb_symtab_add(s, defs, n, NULL, &status));
- return 0;
-}
-
-static int lupb_symtab_addfile(lua_State *L) {
- upb_symtab *s = lupb_symtab_checkmutable(L, 1);
- upb_filedef *f = lupb_filedef_checkmutable(L, 2);
- CHK(upb_symtab_addfile(s, f, &status));
- return 0;
-}
-
-static int lupb_symtab_lookup(lua_State *L) {
- const upb_symtab *s = lupb_symtab_check(L, 1);
- int i;
- for (i = 2; i <= lua_gettop(L); i++) {
- const upb_def *def = upb_symtab_lookup(s, luaL_checkstring(L, i));
- lupb_def_pushwrapper(L, def, NULL);
- lua_replace(L, i);
- }
- return lua_gettop(L) - 1;
-}
-
-static int lupb_symtabiter_next(lua_State *L) {
- upb_symtab_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_symtab_done(i)) return 0;
- lupb_def_pushwrapper(L, upb_symtab_iter_def(i), NULL);
- upb_symtab_next(i);
- return 1;
-}
-
-static int lupb_symtab_defs(lua_State *L) {
- const upb_symtab *s = lupb_symtab_check(L, 1);
- upb_deftype_t type = lua_gettop(L) > 1 ? luaL_checkint(L, 2) : UPB_DEF_ANY;
- upb_symtab_iter *i = lua_newuserdata(L, sizeof(upb_symtab_iter));
- upb_symtab_begin(i, s, type);
- /* Need to guarantee that the symtab outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_symtabiter_next, 2);
- return 1;
-}
-
-static const struct luaL_Reg lupb_symtab_m[] = {
- {"add", lupb_symtab_add},
- {"add_file", lupb_symtab_addfile},
- {"defs", lupb_symtab_defs},
- {"freeze", lupb_symtab_freeze},
- {"is_frozen", lupb_symtab_isfrozen},
- {"lookup", lupb_symtab_lookup},
- {NULL, NULL}
-};
-
-
-/* lupb_array *****************************************************************/
-
-/* A lupb_array provides a strongly-typed array.
- *
- * For the moment we store all values in the userdata's environment table /
- * userval, for simplicity. Later we may wish to move the data into raw
- * memory as both a space and time optimization
- *
- * Compared to regular Lua tables:
- *
- * - we only allow integer indices.
- * - all entries must match the type of the table.
- * - we do not allow "holes" in the array; you can only assign to an existing
- * index or one past the end (which will grow the array by one). */
-
-typedef struct {
- uint32_t size;
- upb_fieldtype_t type;
- const upb_msgdef *msgdef; /* Only when type == UPB_TYPE_MESSAGE */
-} lupb_array;
-
-static lupb_array *lupb_array_check(lua_State *L, int narg) {
- return luaL_checkudata(L, narg, LUPB_ARRAY);
-}
-
-static uint32_t lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
- uint32_t n = lupb_checkuint32(L, narg);
- if (n == 0 || n > max) { /* Lua uses 1-based indexing. :( */
- luaL_error(L, "Invalid array index.");
- }
- return n;
-}
-
-static int lupb_array_new(lua_State *L) {
- lupb_array *array = newudata_with_userval(L, sizeof(*array), LUPB_ARRAY);
- array->size = 0;
-
- if (lua_type(L, 1) == LUA_TNUMBER) {
- array->type = lupb_checkfieldtype(L, 1);
- if (array->type == UPB_TYPE_MESSAGE) {
- return luaL_error(
- L, "For message arrays construct with the specific message type.");
- }
- } else {
- array->type = UPB_TYPE_MESSAGE;
- array->msgdef = lupb_msgdef_check(L, 1);
-
- /* Store a reference to this msgdef in the environment table to ensure it
- * outlives this array. */
- lua_getuservalue(L, -1);
- lua_pushvalue(L, 1);
- lua_rawseti(L, -2, 0);
- lua_pop(L, 1); /* Pop userval. */
- }
-
- return 1;
-}
-
-static int lupb_array_newindex(lua_State *L) {
- lupb_array *array = lupb_array_check(L, 1);
- uint32_t n = lupb_array_checkindex(L, 2, array->size + 1);
-
- if (n == array->size + 1) {
- array->size++;
- }
-
- if (array->type == UPB_TYPE_MESSAGE) {
- if (array->msgdef != lupb_msg_checkdef(L, 3)) {
- return luaL_error(L, "Tried to assign wrong message type.");
- }
- } else {
- lupb_checkval(L, 3, array->type);
- }
-
- /* Write value to userval table. */
- lua_getuservalue(L, 1);
- lua_pushvalue(L, 3);
- lua_rawseti(L, -2, n);
-
- return 0; /* 1 for chained assignments? */
-}
-
-static int lupb_array_index(lua_State *L) {
- lupb_array *array = lupb_array_check(L, 1);
- uint32_t n = lupb_array_checkindex(L, 2, array->size);
-
- lua_getuservalue(L, 1);
- lua_rawgeti(L, -1, n);
- return 1;
-}
-
-static int lupb_array_len(lua_State *L) {
- lupb_array *array = lupb_array_check(L, 1);
- lua_pushnumber(L, array->size);
- return 1;
-}
-
-static const struct luaL_Reg lupb_array_mm[] = {
- {"__index", lupb_array_index},
- {"__len", lupb_array_len},
- {"__newindex", lupb_array_newindex},
- {NULL, NULL}
-};
-
-/* lupb_msg **************************************************************/
-
-/* A lupb_msg is a userdata where:
- *
- * - the userdata's memory contains hasbits and primitive fields.
- * - the userdata's environment table / uservalue contains references to string
- * fields, submessage fields, and array fields. */
-
-typedef struct {
- const lupb_msgdef *lmd;
- /* Data follows, in a flat buffer. */
-} lupb_msg;
-
-#define MSGDEF_INDEX 0
-
-static bool in_userval(const upb_fielddef *f) {
- return upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f) ||
- upb_fielddef_isstring(f);
-}
-
-static size_t lupb_sizeof(lua_State *L, const upb_fielddef *f) {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_BOOL:
- return 1;
- case UPB_TYPE_INT32:
- case UPB_TYPE_UINT32:
- case UPB_TYPE_ENUM:
- case UPB_TYPE_FLOAT:
- return 4;
- case UPB_TYPE_INT64:
- case UPB_TYPE_UINT64:
- case UPB_TYPE_DOUBLE:
- return 8;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- case UPB_TYPE_MESSAGE:
- break;
- }
- lupb_assert(L, false);
- return 0;
-}
-
-static int div_round_up(size_t n, size_t d) {
- int ret = n / d;
- /* If there was a positive remainder, then the result was rounded down and we
- * need to compensate by adding one. */
- if (n % d > 0) ++ret;
- return ret;
-}
-
-static size_t align_up(size_t val, size_t align) {
- return val % align == 0 ? val : val + align - (val % align);
-}
-
-/* If we always read/write as a consistent type to each value, this shouldn't
- * violate aliasing.
- *
- * Note that the slightly prettier option of:
- *
- * *(type*)(&msg->data[ofs])
- *
- * ...is potentially more questionable wrt the C standard and aliasing.
- * Does the expression &msg->data[ofs] "access the stored value"? If so,
- * this would violate aliasing. So instead we use the expression:
- *
- * (char*)msg + sizeof(lupb_msg) + ofs
- *
- * ...which unambigiously is doing nothing but calculating a pointer address. */
-#define CHARPTR_AT(msg, ofs) ((char*)msg + sizeof(lupb_msg) + ofs)
-#define DEREF(msg, ofs, type) *(type*)CHARPTR_AT(msg, ofs)
-
-lupb_msg *lupb_msg_check(lua_State *L, int narg) {
- lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
- if (!msg->lmd) luaL_error(L, "called into dead msg");
- return msg;
-}
-
-const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
- return lupb_msg_check(L, narg)->lmd->md;
-}
-
-static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
- const lupb_msgdef *lmd,
- int fieldarg) {
- size_t len;
- const char *fieldname = luaL_checklstring(L, fieldarg, &len);
- const upb_fielddef *f = upb_msgdef_ntof(lmd->md, fieldname, len);
-
- if (!f) {
- const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
- luaL_argerror(L, fieldarg, msg);
- return NULL; /* Never reached. */
- }
-
- return f;
-}
-
-/* Assigns offsets for storing data in instances of messages for this type, if
- * they have not already been assigned. "narg" should be the stack location of
- * a Lua msgdef object. It should be frozen (if it is not, we will throw an
- * error). It should not throw errors in any other case, since we may have
- * values on our stack that would leak if we longjmp'd across them.
- *
- * TODO(haberman): (if we want to avoid this and be robust against even lua
- * errors due to OOM, we should stop using upb_handlers_newfrozen() and
- * implement it ourselves with a Lua table as cache, since that would get
- * cleaned up properly on error). */
-static lupb_msgdef *lupb_msg_assignoffsets(lua_State *L, int narg) {
- int idx;
- upb_msg_field_iter i;
- lupb_msgdef *lmd = lupb_msgdef_check2(L, narg);
-
- if (!upb_msgdef_isfrozen(lmd->md))
- luaL_error(L, "msgdef must be frozen");
-
- if (lmd->field_offsets) {
- /* Already assigned. */
- return lmd;
- }
-
- {
- int n = upb_msgdef_numfields(lmd->md);
- uint16_t *offsets = malloc(sizeof(*offsets) * n);
-
- /* Offset with the raw data part; starts with hasbits. */
- size_t hasbits_size = div_round_up(n, 8);
- size_t data_ofs = hasbits_size;
- /* Index within the userval.
- * Starts at one to not collide with MSGDEF_INDEX. */
- size_t userval_idx = 1;
-
- /* Assign offsets. */
- for (upb_msg_field_begin(&i, lmd->md);
- !upb_msg_field_done(&i);
- upb_msg_field_next(&i)) {
- upb_fielddef *f = upb_msg_iter_field(&i);
- if (in_userval(f)) {
- offsets[upb_fielddef_index(f)] = userval_idx++;
- } else {
- size_t size = lupb_sizeof(L, f);
- data_ofs = align_up(data_ofs, size);
- offsets[upb_fielddef_index(f)] = data_ofs;
- data_ofs += size;
- }
- }
-
- lmd->field_offsets = offsets;
- lmd->msg_size = sizeof(lupb_msg) + data_ofs;
- lmd->hasbits_size = hasbits_size;
- }
-
- /* Now recursively assign offsets for all submessages, and also add them to
- * the uservalue to ensure that all the lupb_msgdef objects for our
- * submessages outlive us. This is particularly important if/when we build
- * handlers to populate this msgdef. */
-
- lua_pushvalue(L, narg);
- lua_newtable(L); /* This will be our userval. */
-
- idx = 1;
- for (upb_msg_field_begin(&i, lmd->md);
- !upb_msg_field_done(&i);
- upb_msg_field_next(&i)) {
- upb_fielddef *f = upb_msg_iter_field(&i);
- if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) {
- bool created = lupb_def_pushwrapper(L, upb_fielddef_subdef(f), NULL);
- UPB_ASSERT(!created);
- lupb_msg_assignoffsets(L, -1);
- lua_rawseti(L, -2, idx++); /* Append to uservalue. */
- }
- }
-
- lua_setuservalue(L, -2);
- lua_pop(L, 1); /* copy of msgdef */
-
- return lmd;
-}
-
-void lupb_msg_pushnew(lua_State *L, int narg) {
- lupb_msgdef *lmd = lupb_msg_assignoffsets(L, narg);
- lupb_msg *msg;
-
- /* Add passed-in MessageDef to a table which will become the msg's userval. */
- lua_pushvalue(L, narg);
- lua_newtable(L);
- lua_pushvalue(L, narg);
- lua_rawseti(L, -2, MSGDEF_INDEX);
-
- msg = newudata_with_userval(L, lmd->msg_size, LUPB_MSG);
- memset(msg, 0, lmd->msg_size);
-
- /* Create a msg->msgdef reference, both:
- * 1. a pointer in the userdata itself (for easy access) and */
- msg->lmd = lmd;
-
- /* 2. a reference in Lua-space from the msg's uservalue to the messagedef
- * wrapper object (so the msgdef wrapper object will always outlive us,
- * GC-wise). */
- lua_pushvalue(L, -2); /* Push the table from before. */
- lua_setuservalue(L, -2); /* Pop table, now msg is at top again. */
- lua_remove(L, -2); /* Remove table, so new message is only new val. */
-}
-
-static int lupb_msg_new(lua_State *L) {
- lupb_msg_pushnew(L, 1);
- return 1;
-}
-
-static bool lupb_msg_has(const lupb_msg *msg, const upb_fielddef *f) {
- uint16_t idx = upb_fielddef_index(f);
- return *CHARPTR_AT(msg, idx / 8) & (1 << (idx % 8));
-}
-
-static void lupb_msg_set(lupb_msg *msg, const upb_fielddef *f) {
- uint16_t idx = upb_fielddef_index(f);
- *CHARPTR_AT(msg, idx / 8) |= (1 << (idx % 8));
-}
-
-static int lupb_msg_index(lua_State *L) {
- lupb_msg *msg = lupb_msg_check(L, 1);
- const upb_fielddef *f = lupb_msg_checkfield(L, msg->lmd, 2);
- int ofs;
-
- if (!upb_fielddef_isseq(f) && !lupb_msg_has(msg, f)) {
- lua_pushnil(L);
- return 1;
- }
-
- ofs = msg->lmd->field_offsets[upb_fielddef_index(f)];
-
- if (in_userval(f)) {
- lua_getuservalue(L, 1);
- lua_pushinteger(L, ofs);
- lua_rawget(L, -2);
- } else {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_FLOAT:
- lupb_pushfloat(L, DEREF(msg, ofs, float));
- break;
- case UPB_TYPE_DOUBLE:
- lupb_pushdouble(L, DEREF(msg, ofs, double));
- break;
- case UPB_TYPE_BOOL:
- lua_pushboolean(L, DEREF(msg, ofs, bool));
- break;
- case UPB_TYPE_ENUM:
- case UPB_TYPE_INT32:
- lupb_pushint32(L, DEREF(msg, ofs, int32_t));
- break;
- case UPB_TYPE_UINT32:
- lupb_pushuint32(L, DEREF(msg, ofs, uint32_t));
- break;
- case UPB_TYPE_INT64:
- if (LUA_VERSION_NUM < 503) {
- /* Check value? Lua < 5.3.0 has no native integer support, lua_Number
- * is probably double which can't exactly represent large int64s. */
- }
- lupb_pushint64(L, DEREF(msg, ofs, int64_t));
- break;
- case UPB_TYPE_UINT64:
- if (LUA_VERSION_NUM < 503) {
- /* Check value? Lua < 5.3.0 has no native integer support, lua_Number
- * is probably double which can't exactly represent large uint64s. */
- }
- lupb_pushuint64(L, DEREF(msg, ofs, uint64_t));
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- case UPB_TYPE_MESSAGE:
- lupb_assert(L, false);
- break;
- }
- }
-
- return 1;
-}
-
-int lupb_msg_newindex(lua_State *L) {
- lupb_msg *msg = lupb_msg_check(L, 1);
- const upb_fielddef *f = lupb_msg_checkfield(L, msg->lmd, 2);
- int ofs;
-
- lupb_msg_set(msg, f);
-
- ofs = msg->lmd->field_offsets[upb_fielddef_index(f)];
-
- if (in_userval(f)) {
- /* Type-check and then store in the userval. */
- if (upb_fielddef_isseq(f)) {
- lupb_array *array = lupb_array_check(L, 3);
- if (array->type != upb_fielddef_type(f) ||
- (array->type == UPB_TYPE_MESSAGE &&
- array->msgdef != upb_fielddef_msgsubdef(f))) {
- return luaL_error(L, "Array type mismatch");
- }
- } else if (upb_fielddef_isstring(f)) {
- lupb_checkstring(L, 3);
- } else {
- if (lupb_msg_checkdef(L, 3) != upb_fielddef_msgsubdef(f)) {
- return luaL_error(L, "Message type mismatch");
- }
- }
- lua_getuservalue(L, 1);
- lua_pushvalue(L, 3);
- lua_rawseti(L, -2, ofs);
- } else {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_FLOAT:
- DEREF(msg, ofs, float) = lupb_checkfloat(L, 3);
- break;
- case UPB_TYPE_DOUBLE:
- DEREF(msg, ofs, double) = lupb_checkdouble(L, 3);
- break;
- case UPB_TYPE_ENUM:
- case UPB_TYPE_INT32:
- DEREF(msg, ofs, int32_t) = lupb_checkint32(L, 3);
- break;
- case UPB_TYPE_UINT32:
- DEREF(msg, ofs, uint32_t) = lupb_checkuint32(L, 3);
- break;
- case UPB_TYPE_INT64:
- DEREF(msg, ofs, int64_t) = lupb_checkint64(L, 3);
- break;
- case UPB_TYPE_UINT64:
- DEREF(msg, ofs, uint64_t) = lupb_checkuint64(L, 3);
- break;
- case UPB_TYPE_BOOL:
- DEREF(msg, ofs, bool) = lupb_checkbool(L, 3);
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- case UPB_TYPE_MESSAGE:
- lupb_assert(L, false);
- }
- }
-
- return 0; /* 1 for chained assignments? */
-}
-
-static const struct luaL_Reg lupb_msg_mm[] = {
- {"__index", lupb_msg_index},
- {"__newindex", lupb_msg_newindex},
- {NULL, NULL}
-};
-
-
-/* lupb_msg populating handlers ***********************************************/
-
-/* NOTE: doesn't support repeated or submessage fields yet. Coming soon. */
-
-typedef struct {
- uint32_t ofs;
- uint32_t hasbit;
-} lupb_handlerdata;
-
-static void lupb_sethasbit(lupb_msg *msg, uint32_t hasbit) {
- *CHARPTR_AT(msg, hasbit / 8) |= 1 << (hasbit % 8);
-}
-
-static size_t strhandler(void *closure, const void *hd, const char *str,
- size_t len, const upb_bufhandle *handle) {
- lupb_msg *msg = closure;
- const lupb_handlerdata *data = hd;
- lua_State *L = msg->lmd->L;
- UPB_UNUSED(handle);
-
- lua_pushlstring(L, str, len);
- lua_rawseti(L, -2, data->ofs);
- lupb_sethasbit(msg, data->hasbit);
- return len;
-}
-
-const void *newhandlerdata(upb_handlers *h, uint32_t ofs, uint32_t hasbit) {
- lupb_handlerdata *data = malloc(sizeof(*data));
- data->ofs = ofs;
- data->hasbit = hasbit;
- upb_handlers_addcleanup(h, data, free);
- return data;
-}
-
-void callback(const void *closure, upb_handlers *h) {
- lua_State *L = (lua_State*)closure;
- lupb_msgdef *lmd;
- upb_msg_field_iter i;
-
- lupb_def_pushwrapper(L, upb_msgdef_upcast(upb_handlers_msgdef(h)), NULL);
- lmd = lupb_msg_assignoffsets(L, -1);
- upb_msg_field_begin(&i, upb_handlers_msgdef(h));
- for (; !upb_msg_field_done(&i); upb_msg_field_next(&i)) {
- upb_fielddef *f = upb_msg_iter_field(&i);
- int hasbit = upb_fielddef_index(f);
- uint16_t ofs = lmd->field_offsets[upb_fielddef_index(f)];
- if (upb_fielddef_isseq(f)) {
- luaL_error(L, "Doesn't support repeated fields yet.");
- } else {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_BOOL:
- case UPB_TYPE_INT32:
- case UPB_TYPE_UINT32:
- case UPB_TYPE_ENUM:
- case UPB_TYPE_FLOAT:
- case UPB_TYPE_INT64:
- case UPB_TYPE_UINT64:
- case UPB_TYPE_DOUBLE:
- hasbit += sizeof(lupb_msg) * 8;
- ofs += sizeof(lupb_msg);
- upb_shim_set(h, f, ofs, hasbit);
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES: {
- upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
- upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, ofs, hasbit));
- /* XXX: does't currently handle split buffers. */
- upb_handlers_setstring(h, f, strhandler, &attr);
- upb_handlerattr_uninit(&attr);
- break;
- }
- case UPB_TYPE_MESSAGE:
- luaL_error(L, "Doesn't support submessages yet.");
- break;
- }
- }
- }
- lua_pop(L, 1); /* msgdef wrapper */
-}
-
-const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
- const void *owner) {
- lupb_msgdef *lmd = lupb_msg_assignoffsets(L, narg);
- return upb_handlers_newfrozen(lmd->md, owner, callback, L);
-}
-
-
-/* lupb toplevel **************************************************************/
-
-static int lupb_freeze(lua_State *L) {
- int n = lua_gettop(L);
- int i;
- /* Scratch memory; lua_newuserdata() anchors it as a GC root in case any Lua
- * functions fail. */
- upb_def **defs = lua_newuserdata(L, n * sizeof(upb_def*));
-
- for (i = 0; i < n; i++) {
- /* Could allow an array of defs here also. */
- defs[i] = lupb_def_checkmutable(L, i + 1);
- }
- CHK(upb_def_freeze(defs, n, &status));
- return 0;
-}
-
-/* This is a *temporary* API that will be removed once pending refactorings are
- * complete (it does not belong here in core because it depends on both
- * the descriptor.proto schema and the protobuf binary format. */
-static int lupb_loaddescriptor(lua_State *L) {
- size_t len;
- const char *str = luaL_checklstring(L, 1, &len);
- size_t i;
- upb_filedef **files = NULL;
- CHK(files = upb_loaddescriptor(str, len, &files, &status));
-
- lua_newtable(L);
- for (i = 1; *files; i++, files++) {
- lupb_filedef_pushnewrapper(L, *files, &files);
- lua_rawseti(L, -2, i);
- }
-
- return 1;
-}
static const struct luaL_Reg lupb_toplevel_m[] = {
- {"Array", lupb_array_new},
- {"EnumDef", lupb_enumdef_new},
- {"FieldDef", lupb_fielddef_new},
- {"FileDef", lupb_filedef_new},
- {"Message", lupb_msg_new},
- {"MessageDef", lupb_msgdef_new},
- {"OneofDef", lupb_oneofdef_new},
- {"SymbolTable", lupb_symtab_new},
- {"freeze", lupb_freeze},
- {"load_descriptor", lupb_loaddescriptor},
-
{NULL, NULL}
};
void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
- const luaL_Reg *mm, bool refcount_gc) {
+ const luaL_Reg *mm) {
luaL_newmetatable(L, name);
if (mm) {
lupb_setfuncs(L, mm);
}
- if (refcount_gc) {
- lupb_setfuncs(L, lupb_refcounted_mm);
- }
-
if (m) {
/* Methods go in the mt's __index method. This implies that you can'
* implement __index and also have methods. */
@@ -2083,102 +232,14 @@ void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
lua_pop(L, 1); /* The mt. */
}
-static void lupb_setfieldi(lua_State *L, const char *field, int i) {
- lua_pushinteger(L, i);
- lua_setfield(L, -2, field);
-}
-
int luaopen_upb_c(lua_State *L) {
static char module_key;
if (lupb_openlib(L, &module_key, "upb_c", lupb_toplevel_m)) {
return 1;
}
- /* Non-refcounted types. */
- lupb_register_type(L, LUPB_ARRAY, NULL, lupb_array_mm, false);
- lupb_register_type(L, LUPB_MSG, NULL, lupb_msg_mm, false);
-
- /* Refcounted types. */
- lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm, true);
- lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, NULL, true);
- lupb_register_type(L, LUPB_FILEDEF, lupb_filedef_m, lupb_filedef_mm, true);
- lupb_register_type(L, LUPB_ONEOFDEF, lupb_oneofdef_m, lupb_oneofdef_mm, true);
- lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, NULL, true);
-
- /* Refcounted but with custom __gc. */
- lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm, false);
-
- /* Create our object cache. */
- lua_newtable(L);
- lua_createtable(L, 0, 1); /* Cache metatable. */
- lua_pushstring(L, "v"); /* Values are weak. */
- lua_setfield(L, -2, "__mode");
- lua_setmetatable(L, -2);
- lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
-
- /* Register constants. */
- lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL_OPTIONAL);
- lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL_REQUIRED);
- lupb_setfieldi(L, "LABEL_REPEATED", UPB_LABEL_REPEATED);
-
- lupb_setfieldi(L, "TYPE_DOUBLE", UPB_TYPE_DOUBLE);
- lupb_setfieldi(L, "TYPE_FLOAT", UPB_TYPE_FLOAT);
- lupb_setfieldi(L, "TYPE_INT64", UPB_TYPE_INT64);
- lupb_setfieldi(L, "TYPE_UINT64", UPB_TYPE_UINT64);
- lupb_setfieldi(L, "TYPE_INT32", UPB_TYPE_INT32);
- lupb_setfieldi(L, "TYPE_BOOL", UPB_TYPE_BOOL);
- lupb_setfieldi(L, "TYPE_STRING", UPB_TYPE_STRING);
- lupb_setfieldi(L, "TYPE_MESSAGE", UPB_TYPE_MESSAGE);
- lupb_setfieldi(L, "TYPE_BYTES", UPB_TYPE_BYTES);
- lupb_setfieldi(L, "TYPE_UINT32", UPB_TYPE_UINT32);
- lupb_setfieldi(L, "TYPE_ENUM", UPB_TYPE_ENUM);
-
- lupb_setfieldi(L, "INTFMT_VARIABLE", UPB_INTFMT_VARIABLE);
- lupb_setfieldi(L, "INTFMT_FIXED", UPB_INTFMT_FIXED);
- lupb_setfieldi(L, "INTFMT_ZIGZAG", UPB_INTFMT_ZIGZAG);
-
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_DOUBLE", UPB_DESCRIPTOR_TYPE_DOUBLE);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_FLOAT", UPB_DESCRIPTOR_TYPE_FLOAT);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT64", UPB_DESCRIPTOR_TYPE_INT64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT64", UPB_DESCRIPTOR_TYPE_UINT64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT32", UPB_DESCRIPTOR_TYPE_INT32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED64", UPB_DESCRIPTOR_TYPE_FIXED64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED32", UPB_DESCRIPTOR_TYPE_FIXED32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_BOOL", UPB_DESCRIPTOR_TYPE_BOOL);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_STRING", UPB_DESCRIPTOR_TYPE_STRING);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_GROUP", UPB_DESCRIPTOR_TYPE_GROUP);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_MESSAGE", UPB_DESCRIPTOR_TYPE_MESSAGE);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_BYTES", UPB_DESCRIPTOR_TYPE_BYTES);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT32", UPB_DESCRIPTOR_TYPE_UINT32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_ENUM", UPB_DESCRIPTOR_TYPE_ENUM);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED32", UPB_DESCRIPTOR_TYPE_SFIXED32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED64", UPB_DESCRIPTOR_TYPE_SFIXED64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT32", UPB_DESCRIPTOR_TYPE_SINT32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT64", UPB_DESCRIPTOR_TYPE_SINT64);
-
- lupb_setfieldi(L, "DEF_MSG", UPB_DEF_MSG);
- lupb_setfieldi(L, "DEF_FIELD", UPB_DEF_FIELD);
- lupb_setfieldi(L, "DEF_ENUM", UPB_DEF_ENUM);
- lupb_setfieldi(L, "DEF_SERVICE", UPB_DEF_SERVICE);
- lupb_setfieldi(L, "DEF_ANY", UPB_DEF_ANY);
-
- lupb_setfieldi(L, "HANDLER_INT32", UPB_HANDLER_INT32);
- lupb_setfieldi(L, "HANDLER_INT64", UPB_HANDLER_INT64);
- lupb_setfieldi(L, "HANDLER_UINT32", UPB_HANDLER_UINT32);
- lupb_setfieldi(L, "HANDLER_UINT64", UPB_HANDLER_UINT64);
- lupb_setfieldi(L, "HANDLER_FLOAT", UPB_HANDLER_FLOAT);
- lupb_setfieldi(L, "HANDLER_DOUBLE", UPB_HANDLER_DOUBLE);
- lupb_setfieldi(L, "HANDLER_BOOL", UPB_HANDLER_BOOL);
- lupb_setfieldi(L, "HANDLER_STARTSTR", UPB_HANDLER_STARTSTR);
- lupb_setfieldi(L, "HANDLER_STRING", UPB_HANDLER_STRING);
- lupb_setfieldi(L, "HANDLER_ENDSTR", UPB_HANDLER_ENDSTR);
- lupb_setfieldi(L, "HANDLER_STARTSUBMSG", UPB_HANDLER_STARTSUBMSG);
- lupb_setfieldi(L, "HANDLER_ENDSUBMSG", UPB_HANDLER_ENDSUBMSG);
- lupb_setfieldi(L, "HANDLER_STARTSEQ", UPB_HANDLER_STARTSEQ);
- lupb_setfieldi(L, "HANDLER_ENDSEQ", UPB_HANDLER_ENDSEQ);
-
- lupb_setfieldi(L, "SYNTAX_PROTO2", UPB_SYNTAX_PROTO2);
- lupb_setfieldi(L, "SYNTAX_PROTO3", UPB_SYNTAX_PROTO3);
+ lupb_def_registertypes(L);
+ lupb_msg_registertypes(L);
return 1; /* Return package table. */
}
diff --git a/upb/bindings/lua/upb.h b/upb/bindings/lua/upb.h
index f37ed9a..30fc59d 100644
--- a/upb/bindings/lua/upb.h
+++ b/upb/bindings/lua/upb.h
@@ -8,6 +8,7 @@
#include "lauxlib.h"
#include "upb/def.h"
#include "upb/handlers.h"
+#include "upb/msg.h"
#include "upb/symtab.h"
/* Lua 5.1/5.2 compatibility code. */
@@ -23,10 +24,14 @@
void *luaL_testudata(lua_State *L, int ud, const char *tname);
+#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
+
#elif LUA_VERSION_NUM == 502
int luaL_typerror(lua_State *L, int narg, const char *tname);
+#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
+
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
@@ -69,6 +74,7 @@ uint32_t lupb_checkuint32(lua_State *L, int narg);
double lupb_checkdouble(lua_State *L, int narg);
float lupb_checkfloat(lua_State *L, int narg);
bool lupb_checkbool(lua_State *L, int narg);
+const char *lupb_checkstring(lua_State *L, int narg, size_t *len);
const char *lupb_checkname(lua_State *L, int narg);
void lupb_pushint64(lua_State *L, int64_t val);
@@ -77,10 +83,30 @@ void lupb_pushuint64(lua_State *L, uint64_t val);
void lupb_pushuint32(lua_State *L, uint32_t val);
void lupb_pushdouble(lua_State *L, double val);
void lupb_pushfloat(lua_State *L, float val);
-void lupb_pushbool(lua_State *L, bool val);
-/* Functions for getting/pushing wrappers to various types defined in the
- * core library. */
+/* Builds and returns a handlers object for populating a lupb_msg described by
+ * the MessageDef at "narg".
+ *
+ * TODO(haberman): factor this so it doesn't have to take a lua_State. We
+ * should be able to generate message handlers for a upb_msgdef that can be used
+ * across many Lua states, so we can shared JIT code across lua_States. */
+const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
+ const void *owner);
+
+/* Registers a type with the given name, methods, and metamethods.
+ * If "refcount_gc" is true, adds a __gc metamethod that does an unref.
+ * Refcounted types must be allocated with lupb_refcounted_push[new]wrapper. */
+void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
+ const luaL_Reg *mm);
+
+/* Checks the given upb_status and throws a Lua error if it is not ok. */
+void lupb_checkstatus(lua_State *L, upb_status *s);
+
+
+/** From def.c. ***************************************************************/
+
+upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg);
+
void *lupb_refcounted_check(lua_State *L, int narg, const char *type);
const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg);
const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg);
@@ -101,26 +127,22 @@ void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
const void *ref_donor);
-/* For constructing a new message. narg is the Lua value for the MessageDef
- * object. */
-void lupb_msg_pushnew(lua_State *L, int narg);
+void lupb_def_registertypes(lua_State *L);
-/* Builds and returns a handlers object for populating a lupb_msg described by
- * the MessageDef at "narg".
- *
- * TODO(haberman): factor this so it doesn't have to take a lua_State. We
- * should be able to generate message handlers for a upb_msgdef that can be used
- * across many Lua states, so we can shared JIT code across lua_States. */
-const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
- const void *owner);
+int lupb_refcounted_gc(lua_State *L);
-/* Registers a type with the given name, methods, and metamethods.
- * If "refcount_gc" is true, adds a __gc metamethod that does an unref.
- * Refcounted types must be allocated with lupb_refcounted_push[new]wrapper. */
-void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
- const luaL_Reg *mm, bool refcount_gc);
-/* Checks the given upb_status and throws a Lua error if it is not ok. */
-void lupb_checkstatus(lua_State *L, upb_status *s);
+/** From msg.c. ***************************************************************/
+
+struct lupb_msgclass;
+typedef struct lupb_msgclass lupb_msgclass;
+
+upb_arena *lupb_arena_check(lua_State *L, int narg);
+int lupb_arena_new(lua_State *L);
+int lupb_msg_pushref(lua_State *L, int msgclass, void *msg);
+
+const upb_msglayout *lupb_msgclass_getlayout(lua_State *L, int narg);
+const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg);
+void lupb_msg_registertypes(lua_State *L);
#endif /* UPB_LUA_UPB_H_ */
diff --git a/upb/bindings/lua/upb/msg.c b/upb/bindings/lua/upb/msg.c
new file mode 100644
index 0000000..9aa5c2b
--- /dev/null
+++ b/upb/bindings/lua/upb/msg.c
@@ -0,0 +1,892 @@
+
+/* lupb_msgfactory **************************************************************/
+
+/* Userval contains a map of:
+ * [1] = SymbolTable (to keep GC-reachable)
+ * const upb_msgdef* -> lupb_msgclass
+ */
+
+#define LUPB_MSGFACTORY_SYMTAB 1
+
+typedef struct lupb_msgfactory {
+ upb_msgfactory *factory;
+} lupb_msgfactory;
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l);
+
+static lupb_msgfactory *lupb_msgfactory_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGFACTORY);
+}
+
+static int lupb_msgfactory_new(lua_State *L) {
+ const upb_symtab *symtab = lupb_symtab_check(L, 1);
+
+ lupb_msgfactory *lmsgfactory =
+ lupb_newuserdata(L, sizeof(lupb_msgfactory), LUPB_MSGFACTORY);
+ lmsgfactory->factory = upb_msgfactory_new(symtab);
+ lupb_uservalseti(L, -1, LUPB_MSGFACTORY_SYMTAB, 1);
+
+ return 1;
+}
+
+static int lupb_msgfactory_gc(lua_State *L) {
+ lupb_msgfactory *lfactory = lupb_msgfactory_check(L, 1);
+
+ if (lfactory->factory) {
+ upb_msgfactory_free(lfactory->factory);
+ lfactory->factory = NULL;
+ }
+
+ return 0;
+}
+
+static void lupb_msgfactory_pushmsgclass(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, narg);
+
+ lua_getuservalue(L, narg);
+ lua_pushlightuserdata(L, (void*)md);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ /* TODO: verify md is in symtab? */
+ lupb_msgclass_pushnew(L, narg,
+ upb_msgfactory_getlayout(lfactory->factory, md));
+
+ /* Set in userval. */
+ lua_pushlightuserdata(L, (void*)md);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ }
+}
+
+static int lupb_msgfactory_getmsgclass(lua_State *L) {
+ lupb_msgfactory_pushmsgclass(L, 1, lupb_msgdef_check(L, 2));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgfactory_m[] = {
+ {"get_message_class", lupb_msgfactory_getmsgclass},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_msgfactory_mm[] = {
+ {"__gc", lupb_msgfactory_gc},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgclass **************************************************************/
+
+#define LUPB_MSGCLASS_FACTORY 1
+#define LUPB_MSGCLASS_MSGDEF 2
+
+typedef struct lupb_msgclass {
+ const upb_msglayout *layout;
+ const lupb_msgfactory *lfactory;
+} lupb_msgclass;
+
+/* Checks that the given object is a lupb_msg with the given lmsgclass. */
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass);
+
+/* Type-checks for assigning to a message field. */
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md);
+
+static lupb_msgclass *lupb_msgclass_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGCLASS);
+}
+
+static void lupb_msgclass_typecheck(lua_State *L, const lupb_msgclass *expected,
+ const lupb_msgclass *actual) {
+ if (expected != actual) {
+ const upb_msgdef *msgdef = upb_msglayout_msgdef(expected->layout);
+ /* TODO: better error message. */
+ luaL_typerror(L, 3, upb_msgdef_fullname(msgdef));
+ }
+}
+
+static const lupb_msgclass *lupb_msgclass_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSGCLASS_FACTORY);
+ lupb_msgfactory_pushmsgclass(L, -1, md);
+ return lupb_msgclass_check(L, -1);
+}
+
+static const lupb_msgclass *lupb_msgclass_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ /* If we wanted we could try to optimize this by caching these pointers in our
+ * msgclass, in an array indexed by field index. We would still need to fall
+ * back to calling msgclassfor(), unless we wanted to eagerly create
+ * message classes for all submessages. But for big schemas that might be a
+ * lot of things to build, and we might end up not using most of them. */
+ return lupb_msgclass_msgclassfor(L, narg, upb_fielddef_msgsubdef(f));
+}
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, factory);
+ lupb_msgclass *lmc = lupb_newuserdata(L, sizeof(*lmc), LUPB_MSGCLASS);
+
+ lupb_uservalseti(L, -1, LUPB_MSGCLASS_FACTORY, factory);
+ lmc->layout = l;
+ lmc->lfactory = lfactory;
+
+ return 1;
+}
+
+static int lupb_msgclass_call(lua_State *L) {
+ lupb_msg_pushnew(L, 1);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgclass_mm[] = {
+ {"__call", lupb_msgclass_call},
+ {NULL, NULL}
+};
+
+
+
+
+/* lupb_string ****************************************************************/
+
+/* A wrapper around a Lua string.
+ *
+ * This type is NOT exposed to users. Users deal with plain Lua strings.
+ *
+ * This type exists for two reasons:
+ *
+ * 1. To provide storage for a upb_string, which is required for interoperating
+ * with upb_msg. It allows upb to visit string data structures without
+ * calling into Lua.
+ * 2. To cache a string's UTF-8 validity. We want to validate that a string is
+ * valid UTF-8 before allowing it to be assigned to a string field. However
+ * if a string is assigned from one message to another, or assigned to
+ * multiple message fields, we don't want to force the UTF-8 check again. We
+ * cache inside this object if the UTF-8 check has been performed.
+ *
+ * TODO(haberman): is this slightly too clever? If we just exposed this object
+ * directly to Lua we could get rid of the cache. But then the object we expose
+ * to users wouldn't be a true string, so expressions like this would fail:
+ *
+ * if msg.string_field == "abc" then
+ * -- ...
+ * end
+ *
+ * Instead users would have to say this, which seems like a drag:
+ *
+ * if tostring(msg.string_field) == "abc" then
+ * -- ...
+ * end
+ */
+
+typedef struct {
+ enum ValidUtf8 {
+ UTF8_UNCHECKED = 0,
+ UTF8_VALID = 1,
+ UTF8_INVALID = 2
+ } utf8_validity; /* Possibly move this into upb_string at some point. */
+ /* upb_string follows. */
+} lupb_string;
+
+#define LUPB_STRING_INDEX 1 /* The index where we reference the Lua string. */
+
+static upb_string *lupb_string_upbstr(lupb_string *lstring) {
+ return lupb_structafter(&lstring[1]);
+}
+
+static size_t lupb_string_sizeof() {
+ return lupb_sizewithstruct(sizeof(lupb_string), upb_string_sizeof());
+}
+
+/* The cache maps char* (lightuserdata) -> lupb_string userdata. The char* is
+ * the string data from a Lua string object. In practice Lua string objects
+ * have a stable char* for the actual string data, so we can safely key by this.
+ * See: http://lua-users.org/lists/lua-l/2011-06/msg00401.html
+ *
+ * The cache's values are weak, so cache entries can be collected if this string
+ * is no longer a member of any message, array, or map. Keeping real Lua
+ * strings as weak keys is not possible, because Lua does make strings subject
+ * to weak collection, so this would prevent these strings from ever being
+ * collected. */
+static void lupb_string_pushcache(lua_State *L) {
+ static char key;
+ lua_pushlightuserdata(L, &key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ /* Lazily create. */
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* nil. */
+ lua_newtable(L);
+ lua_createtable(L, 0, 1); /* Cache metatable. */
+ lua_pushstring(L, "v"); /* Values are weak. */
+ lua_setfield(L, -2, "__mode");
+ lua_setmetatable(L, -2);
+ lua_pushlightuserdata(L, &key);
+ lua_pushvalue(L, -2); /* Cache. */
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+}
+
+static lupb_string *lupb_string_pushwrapper(lua_State *L, int narg) {
+ const char *str;
+ size_t len;
+ lupb_string *lstring;
+
+ lupb_checkstring(L, narg);
+ str = lua_tolstring(L, narg, &len);
+ lupb_string_pushcache(L);
+ lua_pushlightuserdata(L, (void*)str);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ /* String wasn't in cache, need to create it. */
+ lua_pop(L, 1); /* nil. */
+ lstring = lupb_newuserdata(L, lupb_string_sizeof(), LUPB_STRING);
+ lstring->utf8_validity = UTF8_UNCHECKED;
+ upb_string_set(lupb_string_upbstr(lstring), str, len);
+ lua_pushlightuserdata(L, (void*)str);
+ lua_pushvalue(L, -2);
+ /* Stack is [cache, lupb_string, str, lupb_string]. */
+ lua_rawset(L, -4);
+
+ /* Need to create a reference to the underlying string object, so
+ * lupb_string keeps it alive. */
+ lupb_uservalseti(L, -1, LUPB_STRING_INDEX, narg);
+ } else {
+ lstring = lua_touserdata(L, -1);
+ }
+
+ lua_remove(L, -2); /* cache. */
+ return lstring;
+}
+
+/* The value at narg should be a Lua string object. This will push a wrapper
+ * object (which may be from the cache). Returns a upb_string* that is valid
+ * for as long as the pushed object is alive.
+ *
+ * This object should only be used internally, and not exposed to users! */
+static upb_msgval lupb_string_pushbyteswrapper(lua_State *L, int narg) {
+ lupb_string *lstring = lupb_string_pushwrapper(L, narg);
+ return upb_msgval_str(lupb_string_upbstr(lstring));
+}
+
+/* Like lupb_string_pushbyteswrapper(), except it also validates that the string
+ * is valid UTF-8 (if we haven't already) and throws an error if not. */
+static upb_msgval lupb_string_pushstringwrapper(lua_State *L, int narg) {
+ lupb_string *lstring = lupb_string_pushwrapper(L, narg);
+
+ if (lstring->utf8_validity == UTF8_UNCHECKED) {
+ if (true /* TODO: check UTF-8 */) {
+ lstring->utf8_validity = UTF8_VALID;
+ } else {
+ lstring->utf8_validity = UTF8_INVALID;
+ }
+ }
+
+ if (lstring->utf8_validity != UTF8_VALID) {
+ luaL_error(L, "String is not valid UTF-8");
+ }
+
+ return upb_msgval_str(lupb_string_upbstr(lstring));
+}
+
+/* Given a previously pushed wrapper object, unwraps it and pushes the plain
+ * string object underneath. This is the only object we should expose to users.
+ */
+static void lupb_string_unwrap(lua_State *L, int arg) {
+ lupb_uservalgeti(L, arg, LUPB_STRING_INDEX);
+}
+
+
+/* upb <-> Lua type conversion ************************************************/
+
+static bool lupb_isstring(upb_fieldtype_t type) {
+ return type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES;
+}
+
+static bool lupb_istypewrapped(upb_fieldtype_t type) {
+ return type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES ||
+ type == UPB_TYPE_MESSAGE;
+}
+
+static upb_msgval lupb_tomsgval(lua_State *L, upb_fieldtype_t type, int narg,
+ const lupb_msgclass *lmsgclass,
+ bool *pushed_luaobj) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ return upb_msgval_int32(lupb_checkint32(L, narg));
+ case UPB_TYPE_INT64:
+ return upb_msgval_int64(lupb_checkint64(L, narg));
+ case UPB_TYPE_UINT32:
+ return upb_msgval_uint32(lupb_checkuint32(L, narg));
+ case UPB_TYPE_UINT64:
+ return upb_msgval_uint64(lupb_checkuint64(L, narg));
+ case UPB_TYPE_DOUBLE:
+ return upb_msgval_double(lupb_checkdouble(L, narg));
+ case UPB_TYPE_FLOAT:
+ return upb_msgval_float(lupb_checkfloat(L, narg));
+ case UPB_TYPE_BOOL:
+ return upb_msgval_bool(lupb_checkbool(L, narg));
+ case UPB_TYPE_STRING:
+ /* For map lookup by key, we might want a lighter-weight way of creating a
+ * temporary string. */
+ *pushed_luaobj = true;
+ return lupb_string_pushstringwrapper(L, narg);
+ case UPB_TYPE_BYTES:
+ *pushed_luaobj = true;
+ return lupb_string_pushbyteswrapper(L, narg);
+ case UPB_TYPE_MESSAGE:
+ UPB_ASSERT(lmsgclass);
+ *pushed_luaobj = true;
+ lua_pushvalue(L, narg);
+ return lupb_msg_typecheck(L, narg, lmsgclass);
+ }
+}
+
+static void lupb_pushmsgval(lua_State *L, upb_fieldtype_t type,
+ upb_msgval val) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ lupb_pushint32(L, upb_msgval_getint32(val));
+ break;
+ case UPB_TYPE_INT64:
+ lupb_pushint64(L, upb_msgval_getint64(val));
+ break;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, upb_msgval_getuint32(val));
+ break;
+ case UPB_TYPE_UINT64:
+ lupb_pushuint64(L, upb_msgval_getuint64(val));
+ break;
+ case UPB_TYPE_DOUBLE:
+ lupb_pushdouble(L, upb_msgval_getdouble(val));
+ break;
+ case UPB_TYPE_FLOAT:
+ lupb_pushfloat(L, upb_msgval_getdouble(val));
+ break;
+ case UPB_TYPE_BOOL:
+ lupb_pushbool(L, upb_msgval_getbool(val));
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ lupb_assert(L, false);
+ }
+}
+
+
+/* lupb_array *****************************************************************/
+
+/* A strongly typed array. Implemented by wrapping upb_array.
+ *
+ * - we only allow integer indices.
+ * - all entries must have the correct type.
+ * - we do not allow "holes" in the array; you can only assign to an existing
+ * index or one past the end (which will grow the array by one).
+ */
+
+typedef struct {
+ /* Only needed for array of message. This wastes space in the non-message
+ * case but simplifies the code. Could optimize away if desired. */
+ lupb_msgclass *lmsgclass;
+
+ /* upb_array follows. */
+} lupb_array;
+
+static size_t lupb_array_sizeof(upb_fieldtype_t type) {
+ return lupb_sizewithstruct(sizeof(lupb_array), upb_array_sizeof(type));
+}
+
+static upb_array *lupb_array_upbarr(lupb_array *arr) {
+ return lupb_structafter(&arr[1]);
+}
+
+static lupb_array *lupb_array_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+static upb_array *lupb_array_check2(lua_State *L, int narg) {
+ return lupb_array_upbarr(lupb_array_check(L, narg));
+}
+
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_array *larray = lupb_array_check(L, narg);
+ upb_array *array = lupb_array_upbarr(larray);
+
+ if (upb_array_type(array) != upb_fielddef_type(f) ||
+ lupb_msg_getsubmsgclass(L, msg, f) != larray->lmsgclass) {
+ luaL_error(L, "Array had incorrect type (expected: %s, got: %s)",
+ upb_fielddef_type(f), upb_array_type(array));
+ }
+
+ if (upb_array_type(array) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(L, lupb_msg_getsubmsgclass(L, msg, f),
+ larray->lmsgclass);
+ }
+
+ return upb_msgval_arr(array);
+}
+
+/* We use "int" because of lua_rawseti/lua_rawgeti -- can re-evaluate if we want
+ * arrays bigger than 2^31. */
+static int lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
+ uint32_t n = lupb_checkuint32(L, narg);
+ if (n == 0 || n > max || n > INT_MAX) { /* Lua uses 1-based indexing. :( */
+ luaL_error(L, "Invalid array index.");
+ }
+ return n;
+}
+
+static int lupb_array_new(lua_State *L) {
+ lupb_array *larray;
+ upb_fieldtype_t type;
+ lupb_msgclass *lmsgclass = NULL;
+
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ type = lupb_checkfieldtype(L, 1);
+ } else {
+ type = UPB_TYPE_MESSAGE;
+ lmsgclass = lupb_msgclass_check(L, 1);
+ lupb_uservalseti(L, -1, MSGCLASS_INDEX, 1); /* GC-root lmsgclass. */
+ }
+
+ larray = lupb_newuserdata(L, lupb_array_sizeof(type), LUPB_ARRAY);
+ larray->lmsgclass = lmsgclass;
+ upb_array_init(lupb_array_upbarr(larray), type);
+
+ return 1;
+}
+
+static int lupb_array_gc(lua_State *L) {
+ upb_array *array = lupb_array_check2(L, 1);
+ WITH_ALLOC(upb_array_uninit(array, alloc));
+ return 0;
+}
+
+static int lupb_array_newindex(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array *array = lupb_array_upbarr(larray);
+ upb_fieldtype_t type = upb_array_type(array);
+ bool hasuserval = false;
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(array) + 1);
+ upb_msgval msgval = lupb_tomsgval(L, type, 3, larray->lmsgclass, &hasuserval);
+
+ WITH_ALLOC(upb_array_set(array, n, msgval, alloc));
+
+ if (hasuserval) {
+ lupb_uservalseti(L, 1, n, -1);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static int lupb_array_index(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array *array = lupb_array_upbarr(larray);
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(array));
+ upb_fieldtype_t type = upb_array_type(array);
+
+ if (lupb_istypewrapped(type)) {
+ lupb_uservalgeti(L, 1, n);
+ if (lupb_isstring(type)) {
+ lupb_string_unwrap(L, -1);
+ }
+ } else {
+ lupb_pushmsgval(L, upb_array_type(array), upb_array_get(array, n));
+ }
+
+ return 1;
+}
+
+static int lupb_array_len(lua_State *L) {
+ upb_array *array = lupb_array_check2(L, 1);
+ lua_pushnumber(L, upb_array_size(array));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_array_mm[] = {
+ {"__gc", lupb_array_gc},
+ {"__index", lupb_array_index},
+ {"__len", lupb_array_len},
+ {"__newindex", lupb_array_newindex},
+ {NULL, NULL}
+};
+
+
+/* lupb_map *******************************************************************/
+
+/* A map object. Implemented by wrapping upb_map.
+ *
+ * When the value type is string/bytes/message, the userval consists of:
+ *
+ * [Lua number/string] -> [lupb_string/lupb_msg userdata]
+ *
+ * We always keep this synced to the underlying upb_map. For other value types
+ * we don't use the userdata, and we just read/write the underlying upb_map.
+ *
+ *
+ */
+
+typedef struct {
+ const lupb_msgclass *value_lmsgclass;
+ /* upb_map follows */
+} lupb_map;
+
+/* lupb_map internal functions */
+
+static size_t lupb_map_sizeof(upb_fieldtype_t ktype, upb_fieldtype_t vtype) {
+ return lupb_sizewithstruct(sizeof(lupb_map), upb_map_sizeof(ktype, vtype));
+}
+
+static upb_map *lupb_map_upbmap(lupb_map *lmap) {
+ return lupb_structafter(&lmap[1]);
+}
+
+static lupb_map *lupb_map_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+static upb_map *lupb_map_check2(lua_State *L, int narg) {
+ return lupb_map_upbmap(lupb_map_check(L, narg));
+}
+
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_map *lmap = lupb_map_check(L, narg);
+ upb_map *map = lupb_map_upbmap(lmap);
+ const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+ const upb_fielddef *key_field = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
+ const upb_fielddef *value_field = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
+
+ UPB_ASSERT(entry && key_field && value_field);
+
+ if (upb_map_keytype(map) != upb_fielddef_type(key_field)) {
+ luaL_error(L, "Map key type invalid");
+ }
+
+ if (upb_map_valuetype(map) != upb_fielddef_type(value_field)) {
+ luaL_error(L, "Map had incorrect value type (expected: %s, got: %s)",
+ upb_fielddef_type(value_field), upb_map_valuetype(map));
+ }
+
+ if (upb_map_valuetype(map) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(
+ L, lupb_msg_msgclassfor(L, msg, upb_fielddef_msgsubdef(value_field)),
+ lmap->value_lmsgclass);
+ }
+
+ return upb_msgval_map(map);
+}
+
+static void lupb_map_lazy
+
+static int lupb_map_gc(lua_State *L) {
+ upb_map *map = lupb_map_check2(L, 1);
+ WITH_ALLOC(upb_map_uninit(map, alloc));
+ return 0;
+}
+
+/* lupb_map Public API */
+
+static int lupb_map_new(lua_State *L) {
+ lupb_map *lmap;
+ upb_map *map;
+ upb_fieldtype_t key_type = lupb_checkfieldtype(L, 1);
+ upb_fieldtype_t value_type;
+ lupb_msgclass *value_lmsgclass = NULL;
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ value_type = lupb_checkfieldtype(L, 2);
+ } else {
+ value_type = UPB_TYPE_MESSAGE;
+ }
+
+ lmap = lupb_newuserdata(L, lupb_map_sizeof(key_type, value_type), LUPB_MAP);
+ map = lupb_map_upbmap(lmap);
+
+ if (value_type == UPB_TYPE_MESSAGE) {
+ value_lmsgclass = lupb_msgclass_check(L, 2);
+ lupb_uservalseti(L, -1, MSGCLASS_INDEX, 2); /* GC-root lmsgclass. */
+ }
+
+ lmap->value_lmsgclass = value_lmsgclass;
+ WITH_ALLOC(upb_map_init(map, key_type, value_type, alloc));
+
+ return 1;
+}
+
+static int lupb_map_index(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ upb_fieldtype_t valtype = upb_map_valuetype(map);
+ bool pushedobj;
+ /* We don't always use "key", but this call checks the key type. */
+ upb_msgval key = lupb_map_tokeymsgval(L, upb_map_keytype(map), 2);
+
+ if (lupb_istypewrapped(valtype)) {
+ /* Userval contains the full map, lookup there by key. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_rawget(L, -2);
+ } else {
+ /* Lookup in upb_map. */
+ upb_msgval val;
+ if (upb_map_get(map, key, &val)) {
+ lupb_map_pushmsgval(L, upb_map_valuetype(map), val);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+
+ return 1;
+}
+
+static int lupb_map_len(lua_State *L) {
+ upb_map *map = lupb_map_check2(L, 1);
+ lua_pushnumber(L, upb_map_size(map));
+ return 1;
+}
+
+static int lupb_map_newindex(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ bool keyobj = false;
+ upb_msgval key = lupb_tomsgval(L, upb_map_keytype(map), 2, NULL, &keyobj);
+
+ if (lua_isnil(L, 3)) {
+ /* Delete from map. */
+ WITH_ALLOC(upb_map_del(map, key, alloc));
+
+ if (lupb_istypewrapped(upb_map_valuetype(map))) {
+ /* Delete in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ } else {
+ /* Set in map. */
+ bool valobj = false;
+ upb_msgval val = lupb_tomsgval(L, upb_map_valuetype(map), 3,
+ lmap->value_lmsgclass, &valobj);
+
+ WITH_ALLOC(upb_map_set(map, key, val, NULL, alloc));
+
+ if (valobj) {
+ /* Set in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, -3);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ }
+
+ return 0;
+}
+
+/* upb_mapiter [[[ */
+
+static int lupb_mapiter_next(lua_State *L) {
+ upb_mapiter *i = lua_touserdata(L, lua_upvalueindex(1));
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ upb_string *str = malloc(upb_string_sizeof());
+
+ if (upb_mapiter_done(i)) {
+ return 0;
+ }
+
+ lupb_map_pushmsgval(L, upb_map_keytype(map), upb_mapiter_key(i));
+ lupb_map_pushmsgval(L, upb_map_valuetype(map), upb_mapiter_value(i));
+ upb_mapiter_next(i);
+
+ free(str);
+
+ return 2;
+}
+
+static int lupb_map_pairs(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ upb_mapiter *i = lua_newuserdata(L, upb_mapiter_sizeof());
+
+ upb_mapiter_begin(i, map);
+ lua_pushvalue(L, 1);
+
+ /* Upvalues are [upb_mapiter, lupb_map]. */
+ lua_pushcclosure(L, &lupb_mapiter_next, 2);
+
+ return 1;
+}
+
+/* upb_mapiter ]]] */
+
+static const struct luaL_Reg lupb_map_mm[] = {
+ {"__gc", lupb_map_gc},
+ {"__index", lupb_map_index},
+ {"__len", lupb_map_len},
+ {"__newindex", lupb_map_newindex},
+ {"__pairs", lupb_map_pairs},
+ {NULL, NULL}
+};
+
+
+/* lupb_msg *******************************************************************/
+
+/* A message object. Implemented by wrapping upb_msg.
+ *
+ * Our userval contains:
+ *
+ * - [0] = our message class
+ * - [upb_fielddef_index(f)] = any submessage/string/map/repeated obj.
+ */
+
+#define LUPB_MSG_MSGCLASSINDEX 0
+
+typedef struct {
+ const lupb_msgclass *lmsgclass;
+ /* Data follows, in a flat buffer. */
+} lupb_msg;
+
+/* lupb_msg helpers */
+
+static bool in_userval(const upb_fielddef *f) {
+ return upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f) ||
+ upb_fielddef_isstring(f) || upb_fielddef_ismap(f);
+}
+
+lupb_msg *lupb_msg_check(lua_State *L, int narg) {
+ lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
+ if (!msg->lmsgclass) luaL_error(L, "called into dead msg");
+ return msg;
+}
+
+void *lupb_msg_upbmsg(lupb_msg *lmsg) {
+ return lupb_structafter(&lmsg[1]);
+}
+
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass) {
+ lupb_msg *msg = lupb_msg_check(L, narg);
+ lupb_msgclass_typecheck(L, msg->lmsgclass, lmsgclass);
+ return upb_msgval_msg(msg);
+}
+
+const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
+ return upb_msglayout_msgdef(lupb_msg_check(L, narg)->lmsgclass->layout);
+}
+
+static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
+ const lupb_msg *msg,
+ int fieldarg) {
+ size_t len;
+ const char *fieldname = luaL_checklstring(L, fieldarg, &len);
+ const upb_msgdef *msgdef = upb_msglayout_msgdef(msg->lmsgclass->layout);
+ const upb_fielddef *f = upb_msgdef_ntof(msgdef, fieldname, len);
+
+ if (!f) {
+ const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
+ luaL_argerror(L, fieldarg, msg);
+ return NULL; /* Never reached. */
+ }
+
+ return f;
+}
+
+static int lupb_msg_pushnew(lua_State *L, int narg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg);
+ size_t size = upb_msg_sizeof(lmsgclass->layout);
+ lupb_msg *msg = lupb_newuserdata(L, size, LUPB_MSG);
+
+ msg->lmsgclass = lmsgclass;
+ lupb_uservalseti(L, -1, LUPB_MSG_MSGCLASSINDEX, narg);
+
+ return 1;
+}
+
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_msgclassfor(L, -1, md);
+}
+
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_getsubmsgclass(L, -1, f);
+}
+
+/* lupb_msg Public API */
+
+static int lupb_msg_index(lua_State *L) {
+ lupb_msg *msg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, msg, 2);
+
+ if (in_userval(f)) {
+ lupb_uservalgeti(L, 1, upb_fielddef_index(f));
+ if (upb_fielddef_isseq(f) && lua_isnil(L, -1)) {
+ /* TODO(haberman): default-construct empty array. */
+ }
+ } else {
+ lupb_pushmsgval(L, upb_fielddef_type(f),
+ upb_msg_get(msg, f, msg->lmsgclass->layout));
+ }
+
+ return 1;
+}
+
+static int lupb_msg_newindex(lua_State *L) {
+ lupb_msg *lmsg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, lmsg, 2);
+ bool luaobj = false;
+ upb_msgval msgval;
+
+ /* Typecheck and get msgval. */
+
+ if (upb_fielddef_isseq(f)) {
+ msgval = lupb_array_typecheck(L, 3, 1, f);
+ luaobj = true;
+ } else if (upb_fielddef_ismap(f)) {
+ msgval = lupb_map_typecheck(L, 3, 1, f);
+ luaobj = true;
+ } else {
+ const lupb_msgclass *lmsgclass = NULL;
+ upb_fieldtype_t type = upb_fielddef_type(f);
+
+ if (type == UPB_TYPE_MESSAGE) {
+ lmsgclass = lupb_msg_getsubmsgclass(L, 1, f);
+ }
+
+ msgval = lupb_tomsgval(L, upb_fielddef_type(f), 3, lmsgclass, &luaobj);
+ }
+
+ /* Set in upb_msg and userval (if necessary). */
+
+ WITH_ALLOC(upb_msg_set(lmsg, f, msgval, lmsg->lmsgclass->layout, alloc));
+
+ if (luaobj) {
+ lupb_uservalseti(L, 1, upb_fielddef_index(f), -1);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static const struct luaL_Reg lupb_msg_mm[] = {
+ {"__index", lupb_msg_index},
+ {"__newindex", lupb_msg_newindex},
+ {NULL, NULL}
+};
+
+
diff --git a/upb/bindings/lua/upb/pb.c b/upb/bindings/lua/upb/pb.c
index f095a61..d6f13d9 100644
--- a/upb/bindings/lua/upb/pb.c
+++ b/upb/bindings/lua/upb/pb.c
@@ -10,59 +10,24 @@
#define LUPB_PBDECODERMETHOD "lupb.pb.decodermethod"
-#define MSGDEF_INDEX 1
-
-static upb_pbdecodermethod *lupb_pbdecodermethod_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_PBDECODERMETHOD);
-}
-
-static int lupb_pbdecodermethod_new(lua_State *L) {
- const upb_handlers *handlers = lupb_msg_newwritehandlers(L, 1, &handlers);
- const upb_pbdecodermethod *m;
-
- upb_pbdecodermethodopts opts;
- upb_pbdecodermethodopts_init(&opts, handlers);
-
- m = upb_pbdecodermethod_new(&opts, &m);
- upb_handlers_unref(handlers, &handlers);
- lupb_refcounted_pushnewrapper(
- L, upb_pbdecodermethod_upcast(m), LUPB_PBDECODERMETHOD, &m);
-
- /* We need to keep a pointer to the MessageDef (in Lua space) so we can
- * construct new messages in parse(). */
- lua_newtable(L);
- lua_pushvalue(L, 1);
- lua_rawseti(L, -2, MSGDEF_INDEX);
- lua_setuservalue(L, -2);
-
- return 1; /* The DecoderMethod wrapper. */
-}
-
-/* Unlike most of our exposed Lua functions, this does not correspond to an
- * actual method on the underlying DecoderMethod. But it's convenient, and
- * important to implement in C because we can do stack allocation and
- * initialization of our runtime structures like the Decoder and Sink. */
-static int lupb_pbdecodermethod_parse(lua_State *L) {
+static int lupb_pb_strtomessage(lua_State *L) {
size_t len;
- const upb_pbdecodermethod *method = lupb_pbdecodermethod_check(L, 1);
- const char *pb = lua_tolstring(L, 2, &len);
- void *msg;
upb_status status = UPB_STATUS_INIT;
+ const char *pb = lua_tolstring(L, 1, &len);
+ const upb_msglayout *layout = lua_touserdata(L, lua_upvalueindex(1));
+ const upb_pbdecodermethod *method = lua_touserdata(L, lua_upvalueindex(2));
+ const upb_handlers *handlers = upb_pbdecodermethod_desthandlers(method);
+
+ upb_arena *msg_arena;
upb_env env;
upb_sink sink;
upb_pbdecoder *decoder;
+ void *msg;
- const upb_handlers *handlers = upb_pbdecodermethod_desthandlers(method);
-
- lua_getuservalue(L, 1);
- lua_rawgeti(L, -1, MSGDEF_INDEX);
- lupb_assert(L, !lua_isnil(L, -1));
- lupb_msg_pushnew(L, -1); /* Push new message. */
- msg = lua_touserdata(L, -1);
-
- /* Handlers need this. */
- lua_getuservalue(L, -1);
+ lupb_arena_new(L);
+ msg_arena = lupb_arena_check(L, -1);
+ msg = upb_msg_new(layout, upb_arena_alloc(msg_arena));
upb_env_init(&env);
upb_env_reporterrorsto(&env, &status);
upb_sink_reset(&sink, handlers, msg);
@@ -76,18 +41,42 @@ static int lupb_pbdecodermethod_parse(lua_State *L) {
lupb_checkstatus(L, &status);
- lua_pop(L, 1); /* Uservalue. */
-
+ /* References the arena at the top of the stack. */
+ lupb_msg_pushref(L, lua_upvalueindex(3), msg);
return 1;
}
-static const struct luaL_Reg lupb_pbdecodermethod_m[] = {
- {"parse", lupb_pbdecodermethod_parse},
+static int lupb_pb_makestrtomsgdecoder(lua_State *L) {
+ const upb_msglayout *layout = lupb_msgclass_getlayout(L, 1);
+ const upb_handlers *handlers = lupb_msgclass_getmergehandlers(L, 1);
+ const upb_pbdecodermethod *m;
+
+ upb_pbdecodermethodopts opts;
+ upb_pbdecodermethodopts_init(&opts, handlers);
+
+ m = upb_pbdecodermethod_new(&opts, &m);
+
+ /* Push upvalues for the closure. */
+ lua_pushlightuserdata(L, (void*)layout);
+ lua_pushlightuserdata(L, (void*)m);
+ lua_pushvalue(L, 1);
+
+ /* Upvalue for the closure, only to keep the decodermethod alive. */
+ lupb_refcounted_pushnewrapper(
+ L, upb_pbdecodermethod_upcast(m), LUPB_PBDECODERMETHOD, &m);
+
+ lua_pushcclosure(L, &lupb_pb_strtomessage, 4);
+
+ return 1; /* The decoder closure. */
+}
+
+static const struct luaL_Reg decodermethod_mm[] = {
+ {"__gc", lupb_refcounted_gc},
{NULL, NULL}
};
static const struct luaL_Reg toplevel_m[] = {
- {"DecoderMethod", lupb_pbdecodermethod_new},
+ {"MakeStringToMessageDecoder", lupb_pb_makestrtomsgdecoder},
{NULL, NULL}
};
@@ -97,8 +86,7 @@ int luaopen_upb_pb_c(lua_State *L) {
return 1;
}
- lupb_register_type(L, LUPB_PBDECODERMETHOD, lupb_pbdecodermethod_m, NULL,
- true);
+ lupb_register_type(L, LUPB_PBDECODERMETHOD, NULL, decodermethod_mm);
return 1;
}
diff --git a/upb/def.c b/upb/def.c
index 92e89e0..bd8c129 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -133,7 +133,8 @@ upb_def *upb_def_dup(const upb_def *def, const void *o) {
case UPB_DEF_ENUM:
return upb_enumdef_upcast_mutable(
upb_enumdef_dup(upb_downcast_enumdef(def), o));
- default: UPB_ASSERT(false); return NULL;
+ default:
+ UPB_UNREACHABLE();
}
}
@@ -283,6 +284,7 @@ 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;
+ upb_msg_oneof_iter k;
int i;
uint32_t selector;
int n = upb_msgdef_numfields(m);
@@ -367,6 +369,13 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) {
#undef TRY
#endif
+ for(upb_msg_oneof_begin(&k, m), i = 0;
+ !upb_msg_oneof_done(&k);
+ upb_msg_oneof_next(&k), i++) {
+ upb_oneofdef *o = upb_msg_iter_oneof(&k);
+ o->index = i;
+ }
+
upb_gfree(fields);
return true;
}
@@ -1833,6 +1842,10 @@ int upb_oneofdef_numfields(const upb_oneofdef *o) {
return upb_strtable_count(&o->ntof);
}
+uint32_t upb_oneofdef_index(const upb_oneofdef *o) {
+ return o->index;
+}
+
bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f,
const void *ref_donor,
upb_status *s) {
diff --git a/upb/def.h b/upb/def.h
index b4f2352..724a85c 100644
--- a/upb/def.h
+++ b/upb/def.h
@@ -222,15 +222,19 @@ UPB_DECLARE_DEF_TYPE(upb::EnumDef, enumdef, ENUM)
* 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_FLOAT = 1,
- UPB_TYPE_DOUBLE = 2,
- UPB_TYPE_BOOL = 3,
- UPB_TYPE_STRING = 4,
- UPB_TYPE_BYTES = 5,
- UPB_TYPE_MESSAGE = 6,
- UPB_TYPE_ENUM = 7, /* Enum values are int32. */
- UPB_TYPE_INT32 = 8,
- UPB_TYPE_UINT32 = 9,
+ /* Types stored in 1 byte. */
+ UPB_TYPE_BOOL = 1,
+ /* Types stored in 4 bytes. */
+ UPB_TYPE_FLOAT = 2,
+ UPB_TYPE_INT32 = 3,
+ UPB_TYPE_UINT32 = 4,
+ UPB_TYPE_ENUM = 5, /* Enum values are int32. */
+ /* Types stored as pointers (probably 4 or 8 bytes). */
+ UPB_TYPE_STRING = 6,
+ UPB_TYPE_BYTES = 7,
+ UPB_TYPE_MESSAGE = 8,
+ /* Types stored as 8 bytes. */
+ UPB_TYPE_DOUBLE = 9,
UPB_TYPE_INT64 = 10,
UPB_TYPE_UINT64 = 11
} upb_fieldtype_t;
@@ -404,16 +408,10 @@ class upb::FieldDef {
bool IsPrimitive() const;
bool IsMap() const;
- /* Whether this field must be able to explicitly represent presence:
+ /* Returns whether this field explicitly represents presence.
*
- * * This is always false for repeated fields (an empty repeated field is
- * equivalent to a repeated field with zero entries).
- *
- * * This is always true for submessages.
- *
- * * For other fields, it depends on the message (see
- * MessageDef::SetPrimitivesHavePresence())
- */
+ * For proto2 messages: Returns true for any scalar (non-repeated) field.
+ * For proto3 messages: Returns true for scalar submessage or oneof fields. */
bool HasPresence() const;
/* How integers are encoded. Only meaningful for integer types.
@@ -1151,6 +1149,7 @@ int32_t upb_enum_iter_number(upb_enum_iter *iter);
UPB_END_EXTERN_C
+
/* upb::OneofDef **************************************************************/
typedef upb_inttable_iter upb_oneof_iter;
@@ -1270,10 +1269,11 @@ upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner);
UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast)
const char *upb_oneofdef_name(const upb_oneofdef *o);
-bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s);
-
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_setname(upb_oneofdef *o, const char *name, upb_status *s);
bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f,
const void *ref_donor,
upb_status *s);
diff --git a/upb/descriptor/descriptor.pb b/upb/descriptor/descriptor.pb
deleted file mode 100644
index d61554b..0000000
--- a/upb/descriptor/descriptor.pb
+++ /dev/null
Binary files differ
diff --git a/upb/descriptor/reader.c b/upb/descriptor/reader.c
index e219f18..02019e1 100644
--- a/upb/descriptor/reader.c
+++ b/upb/descriptor/reader.c
@@ -63,7 +63,7 @@ struct upb_descreader {
upb_fielddef *f;
};
-static char *upb_strndup(const char *buf, size_t n) {
+static char *upb_gstrndup(const char *buf, size_t n) {
char *ret = upb_gmalloc(n + 1);
if (!ret) return NULL;
memcpy(ret, buf, n);
@@ -213,7 +213,7 @@ static size_t file_onname(void *closure, const void *hd, const char *buf,
UPB_UNUSED(hd);
UPB_UNUSED(handle);
- name = upb_strndup(buf, n);
+ name = upb_gstrndup(buf, n);
/* XXX: see comment at the top of the file. */
ok = upb_filedef_setname(r->file, name, NULL);
upb_gfree(name);
@@ -229,7 +229,7 @@ static size_t file_onpackage(void *closure, const void *hd, const char *buf,
UPB_UNUSED(hd);
UPB_UNUSED(handle);
- package = upb_strndup(buf, n);
+ package = upb_gstrndup(buf, n);
/* XXX: see comment at the top of the file. */
upb_descreader_setscopename(r, package);
ok = upb_filedef_setpackage(r->file, package, NULL);
@@ -301,7 +301,7 @@ static size_t enumval_onname(void *closure, const void *hd, const char *buf,
UPB_UNUSED(handle);
/* XXX: see comment at the top of the file. */
upb_gfree(r->name);
- r->name = upb_strndup(buf, n);
+ r->name = upb_gstrndup(buf, n);
r->saw_name = true;
return n;
}
@@ -352,7 +352,7 @@ static bool enum_endmsg(void *closure, const void *hd, upb_status *status) {
static size_t enum_onname(void *closure, const void *hd, const char *buf,
size_t n, const upb_bufhandle *handle) {
upb_descreader *r = closure;
- char *fullname = upb_strndup(buf, n);
+ char *fullname = upb_gstrndup(buf, n);
UPB_UNUSED(hd);
UPB_UNUSED(handle);
/* XXX: see comment at the top of the file. */
@@ -520,7 +520,7 @@ static bool field_onnumber(void *closure, const void *hd, int32_t val) {
static size_t field_onname(void *closure, const void *hd, const char *buf,
size_t n, const upb_bufhandle *handle) {
upb_descreader *r = closure;
- char *name = upb_strndup(buf, n);
+ char *name = upb_gstrndup(buf, n);
UPB_UNUSED(hd);
UPB_UNUSED(handle);
@@ -533,7 +533,7 @@ static size_t field_onname(void *closure, const void *hd, const char *buf,
static size_t field_ontypename(void *closure, const void *hd, const char *buf,
size_t n, const upb_bufhandle *handle) {
upb_descreader *r = closure;
- char *name = upb_strndup(buf, n);
+ char *name = upb_gstrndup(buf, n);
UPB_UNUSED(hd);
UPB_UNUSED(handle);
@@ -546,7 +546,7 @@ static size_t field_ontypename(void *closure, const void *hd, const char *buf,
static size_t field_onextendee(void *closure, const void *hd, const char *buf,
size_t n, const upb_bufhandle *handle) {
upb_descreader *r = closure;
- char *name = upb_strndup(buf, n);
+ char *name = upb_gstrndup(buf, n);
UPB_UNUSED(hd);
UPB_UNUSED(handle);
@@ -566,7 +566,7 @@ static size_t field_ondefaultval(void *closure, const void *hd, const char *buf,
* type yet, so we save it as a string until the end of the field.
* XXX: see comment at the top of the file. */
upb_gfree(r->default_string);
- r->default_string = upb_strndup(buf, n);
+ r->default_string = upb_gstrndup(buf, n);
return n;
}
@@ -587,7 +587,7 @@ static size_t oneof_name(void *closure, const void *hd, const char *buf,
upb_descreader *r = closure;
upb_descreader_frame *f = &r->stack[r->stack_len-1];
upb_oneofdef *o = upb_descreader_getoneof(r, f->oneof_index++);
- char *name_null_terminated = upb_strndup(buf, n);
+ char *name_null_terminated = upb_gstrndup(buf, n);
bool ok = upb_oneofdef_setname(o, name_null_terminated, NULL);
UPB_UNUSED(hd);
UPB_UNUSED(handle);
@@ -624,7 +624,7 @@ static size_t msg_name(void *closure, const void *hd, const char *buf,
upb_descreader *r = closure;
upb_msgdef *m = upb_descreader_top(r);
/* XXX: see comment at the top of the file. */
- char *name = upb_strndup(buf, n);
+ char *name = upb_gstrndup(buf, n);
UPB_UNUSED(hd);
UPB_UNUSED(handle);
diff --git a/upb/json/parser.rl b/upb/json/parser.rl
index 15ca477..b7f1386 100644
--- a/upb/json/parser.rl
+++ b/upb/json/parser.rl
@@ -19,7 +19,6 @@
** - handling of keys/escape-sequences/etc that span input buffers.
*/
-#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
diff --git a/upb/msg.c b/upb/msg.c
new file mode 100644
index 0000000..113dce9
--- /dev/null
+++ b/upb/msg.c
@@ -0,0 +1,1205 @@
+
+#include "upb/msg.h"
+
+static bool is_power_of_two(size_t val) {
+ return (val & (val - 1)) == 0;
+}
+
+/* Align up to the given power of 2. */
+static size_t align_up(size_t val, size_t align) {
+ UPB_ASSERT(is_power_of_two(align));
+ return (val + align - 1) & ~(align - 1);
+}
+
+static size_t div_round_up(size_t n, size_t d) {
+ return (n + d - 1) / d;
+}
+
+#if 0
+static void *alloc(void *p, size_t *ofs, size_t size, size_t n, size_t align) {
+ char *obj;
+
+ *ofs = align_up(*ofs, align);
+ obj = (char*)p + *ofs;
+ *ofs += n;
+ return *ofs <= size ? obj : NULL;
+}
+
+static void addsize(size_t *size, size_t mysize, size_t myalign) {
+ *size = align_up(*size, myalign);
+ *size += mysize;
+}
+
+static void upb_array_packedsize(const upb_array *arr, size_t *size);
+static void upb_map_packedsize(const upb_map *map, size_t *size);
+#endif
+
+bool upb_fieldtype_mapkeyok(upb_fieldtype_t type) {
+ return type == UPB_TYPE_BOOL || type == UPB_TYPE_INT32 ||
+ type == UPB_TYPE_UINT32 || type == UPB_TYPE_INT64 ||
+ type == UPB_TYPE_UINT64 || type == UPB_TYPE_STRING;
+}
+
+void *upb_array_pack(const upb_array *arr, void *p, size_t *ofs, size_t size);
+void *upb_map_pack(const upb_map *map, void *p, size_t *ofs, size_t size);
+
+
+/** upb_msgval ****************************************************************/
+
+#define upb_alignof(t) offsetof(struct { char c; t x; }, x)
+
+/* These functions will generate real memcpy() calls on ARM sadly, because
+ * the compiler assumes they might not be aligned. */
+
+static upb_msgval upb_msgval_read(const void *p, size_t ofs,
+ uint8_t size) {
+ upb_msgval val;
+ p = (char*)p + ofs;
+ memcpy(&val, p, size);
+ return val;
+}
+
+static void upb_msgval_write(void *p, size_t ofs, upb_msgval val,
+ uint8_t size) {
+ p = (char*)p + ofs;
+ memcpy(p, &val, size);
+}
+
+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_BYTES:
+ case UPB_TYPE_MESSAGE:
+ return sizeof(void*);
+ case UPB_TYPE_STRING:
+ return sizeof(char*) + sizeof(size_t);
+ }
+ UPB_UNREACHABLE();
+}
+
+static uint8_t upb_msg_fieldsize(const upb_fielddef *f) {
+ if (upb_fielddef_isseq(f)) {
+ return sizeof(void*);
+ } else {
+ return upb_msgval_sizeof(upb_fielddef_type(f));
+ }
+}
+
+static upb_value upb_toval(upb_msgval val) {
+ upb_value ret;
+ memcpy(&ret, &val, sizeof(upb_msgval));
+ return ret;
+}
+
+static upb_msgval upb_msgval_fromval(upb_value val) {
+ upb_msgval ret;
+ memcpy(&ret, &val, sizeof(upb_msgval));
+ return ret;
+}
+
+static upb_value upb_msgval_toval(upb_msgval val, upb_fieldtype_t type) {
+ upb_value ret;
+ UPB_UNUSED(type);
+ memcpy(&ret, &val, sizeof(upb_msgval));
+#ifndef NDEBUG
+ switch (type) {
+ case UPB_TYPE_FLOAT:
+ return upb_value_float(upb_msgval_getfloat(val));
+ case UPB_TYPE_DOUBLE:
+ return upb_value_double(upb_msgval_getdouble(val));
+ case UPB_TYPE_BOOL:
+ return upb_value_bool(upb_msgval_getbool(val));
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ return upb_value_constptr(val.str.ptr); /* TODO */
+ case UPB_TYPE_MESSAGE:
+ return upb_value_constptr(upb_msgval_getmsg(val));
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_INT32:
+ return upb_value_int32(upb_msgval_getint32(val));
+ case UPB_TYPE_UINT32:
+ return upb_value_uint32(upb_msgval_getuint32(val));
+ case UPB_TYPE_INT64:
+ return upb_value_int64(upb_msgval_getint64(val));
+ case UPB_TYPE_UINT64:
+ return upb_value_uint64(upb_msgval_getuint64(val));
+ }
+#endif
+ return ret;
+}
+
+static upb_ctype_t upb_fieldtotabtype(upb_fieldtype_t type) {
+ switch (type) {
+ case UPB_TYPE_FLOAT: return UPB_CTYPE_FLOAT;
+ case UPB_TYPE_DOUBLE: return UPB_CTYPE_DOUBLE;
+ case UPB_TYPE_BOOL: return UPB_CTYPE_BOOL;
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ case UPB_TYPE_STRING: return UPB_CTYPE_CONSTPTR;
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_INT32: return UPB_CTYPE_INT32;
+ case UPB_TYPE_UINT32: return UPB_CTYPE_UINT32;
+ case UPB_TYPE_INT64: return UPB_CTYPE_INT64;
+ case UPB_TYPE_UINT64: return UPB_CTYPE_UINT64;
+ default: UPB_ASSERT(false); return 0;
+ }
+}
+
+static upb_msgval upb_msgval_fromdefault(const upb_fielddef *f) {
+ /* TODO(haberman): improve/optimize this (maybe use upb_msgval in fielddef) */
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_FLOAT:
+ return upb_msgval_float(upb_fielddef_defaultfloat(f));
+ case UPB_TYPE_DOUBLE:
+ return upb_msgval_double(upb_fielddef_defaultdouble(f));
+ case UPB_TYPE_BOOL:
+ return upb_msgval_bool(upb_fielddef_defaultbool(f));
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ size_t len;
+ const char *ptr = upb_fielddef_defaultstr(f, &len);
+ return upb_msgval_str(ptr, len);
+ }
+ case UPB_TYPE_MESSAGE:
+ return upb_msgval_msg(NULL);
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_INT32:
+ return upb_msgval_int32(upb_fielddef_defaultint32(f));
+ case UPB_TYPE_UINT32:
+ return upb_msgval_uint32(upb_fielddef_defaultuint32(f));
+ case UPB_TYPE_INT64:
+ return upb_msgval_int64(upb_fielddef_defaultint64(f));
+ case UPB_TYPE_UINT64:
+ return upb_msgval_uint64(upb_fielddef_defaultuint64(f));
+ default:
+ UPB_ASSERT(false);
+ return upb_msgval_msg(NULL);
+ }
+}
+
+
+/** upb_msglayout *************************************************************/
+
+struct upb_msglayout {
+ const upb_msgdef *msgdef;
+ size_t size;
+ size_t extdict_offset;
+ void *default_msg;
+ uint32_t *offsets;
+ uint32_t *case_offsets;
+ uint32_t *hasbits;
+ bool has_extdict;
+ uint8_t align;
+};
+
+static void upb_msg_check(const upb_msglayout *l, const upb_fielddef *f) {
+ UPB_ASSERT(l->msgdef == upb_fielddef_containingtype(f));
+}
+
+static void upb_msglayout_free(upb_msglayout *l) {
+ upb_gfree(l->offsets);
+ upb_gfree(l->hasbits);
+ upb_gfree(l->default_msg);
+ upb_gfree(l);
+}
+
+const upb_msgdef *upb_msglayout_msgdef(const upb_msglayout *l) {
+ return l->msgdef;
+}
+
+static size_t upb_msglayout_place(upb_msglayout *l, size_t size) {
+ size_t ret;
+
+ l->size = align_up(l->size, size);
+ l->align = align_up(l->align, size);
+ ret = l->size;
+ l->size += size;
+ return ret;
+}
+
+static uint32_t upb_msglayout_offset(const upb_msglayout *l,
+ const upb_fielddef *f) {
+ return l->offsets[upb_fielddef_index(f)];
+}
+
+static uint32_t upb_msglayout_hasbit(const upb_msglayout *l,
+ const upb_fielddef *f) {
+ return l->hasbits[upb_fielddef_index(f)];
+}
+
+static upb_msglayout *upb_msglayout_new(const upb_msgdef *m) {
+ upb_msg_field_iter it;
+ upb_msg_oneof_iter oit;
+ upb_msglayout *l;
+ size_t hasbit;
+
+ l = upb_gmalloc(sizeof(*l));
+ if (!l) return NULL;
+
+ memset(l, 0, sizeof(*l));
+
+ l->msgdef = m;
+
+ if (upb_msgdef_syntax(m) == UPB_SYNTAX_PROTO2) {
+ l->hasbits = upb_gmalloc(sizeof(*l->hasbits) * upb_msgdef_numfields(m));
+ }
+
+ l->offsets = upb_gmalloc(sizeof(*l->offsets) * upb_msgdef_numfields(m));
+ l->case_offsets =
+ upb_gmalloc(sizeof(*l->case_offsets) * upb_msgdef_numoneofs(m));
+ l->align = 1;
+
+ /* 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. Start at sizeof(void*) for upb_alloc*. */
+ for (upb_msg_field_begin(&it, m), hasbit = sizeof(void*) * 8;
+ !upb_msg_field_done(&it);
+ upb_msg_field_next(&it)) {
+ const upb_fielddef* f = upb_msg_iter_field(&it);
+
+ if (upb_fielddef_haspresence(f) && !upb_fielddef_containingoneof(f)) {
+ l->hasbits[upb_fielddef_index(f)] = hasbit++;
+ }
+ }
+
+ /* 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_fieldsize(f);
+
+ if (upb_fielddef_containingoneof(f)) {
+ /* Oneofs are handled separately below. */
+ continue;
+ }
+
+ l->offsets[upb_fielddef_index(f)] = 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* oneof = 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;
+ size_t case_offset;
+ size_t val_offset;
+
+ /* Calculate field size: the max of all field sizes. */
+ for (upb_oneof_begin(&fit, oneof);
+ !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_fieldsize(f));
+ }
+
+ /* Align and allocate case offset. */
+ case_offset = upb_msglayout_place(l, case_size);
+ val_offset = upb_msglayout_place(l, field_size);
+
+ l->case_offsets[upb_oneofdef_index(oneof)] = case_offset;
+
+ /* Assign all fields in the oneof this same offset. */
+ for (upb_oneof_begin(&fit, oneof); !upb_oneof_done(&fit);
+ upb_oneof_next(&fit)) {
+ const upb_fielddef* f = upb_oneof_iter_field(&fit);
+ l->offsets[upb_fielddef_index(f)] = val_offset;
+ }
+ }
+
+ /* Size of the entire structure should be a multiple of its greatest
+ * alignment. */
+ l->size = align_up(l->size, l->align);
+
+ if (upb_msgdef_syntax(m) == UPB_SYNTAX_PROTO2 && l->size) {
+ /* Allocate default message and set default values in it. */
+ l->default_msg = upb_gmalloc(l->size);
+ if (!l->default_msg) {
+ goto err;
+ }
+
+ memset(l->default_msg, 0, l->size);
+
+ for (upb_msg_field_begin(&it, m), hasbit = 0; !upb_msg_field_done(&it);
+ upb_msg_field_next(&it)) {
+ const upb_fielddef* f = upb_msg_iter_field(&it);
+
+ if (upb_fielddef_containingoneof(f)) {
+ continue;
+ }
+
+ if (!upb_fielddef_isstring(f) &&
+ !upb_fielddef_issubmsg(f) &&
+ !upb_fielddef_isseq(f)) {
+ upb_msg_set(l->default_msg, f, upb_msgval_fromdefault(f), l);
+ }
+ }
+ }
+
+ return l;
+
+ err:
+ upb_msglayout_free(l);
+ return NULL;
+}
+
+
+/** upb_msgfactory ************************************************************/
+
+struct upb_msgfactory {
+ const upb_symtab *symtab; /* We own a ref. */
+ upb_inttable layouts;
+ upb_inttable mergehandlers;
+};
+
+upb_msgfactory *upb_msgfactory_new(const upb_symtab *symtab) {
+ upb_msgfactory *ret = upb_gmalloc(sizeof(*ret));
+
+ ret->symtab = symtab;
+ upb_symtab_ref(ret->symtab, &ret->symtab);
+ upb_inttable_init(&ret->layouts, UPB_CTYPE_PTR);
+ upb_inttable_init(&ret->mergehandlers, UPB_CTYPE_CONSTPTR);
+
+ return ret;
+}
+
+void upb_msgfactory_free(upb_msgfactory *f) {
+ upb_inttable_iter i;
+ upb_inttable_begin(&i, &f->layouts);
+ for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+ upb_msglayout *l = upb_value_getptr(upb_inttable_iter_value(&i));
+ upb_msglayout_free(l);
+ }
+
+ upb_inttable_begin(&i, &f->mergehandlers);
+ for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+ const upb_handlers *h = upb_value_getconstptr(upb_inttable_iter_value(&i));
+ upb_handlers_unref(h, f);
+ }
+
+ upb_inttable_uninit(&f->layouts);
+ upb_inttable_uninit(&f->mergehandlers);
+ upb_symtab_unref(f->symtab, &f->symtab);
+ upb_gfree(f);
+}
+
+/* Requires:
+ * - m is in upb_msgfactory_symtab(f)
+ * - upb_msgdef_mapentry(m) == false (since map messages can't have layouts).
+ *
+ * The returned layout will live for as long as the msgfactory does.
+ */
+const upb_msglayout *upb_msgfactory_getlayout(const upb_msgfactory *f,
+ const upb_msgdef *m) {
+ upb_value v;
+ UPB_ASSERT(upb_symtab_lookupmsg(f->symtab, upb_msgdef_fullname(m)) == m);
+ UPB_ASSERT(!upb_msgdef_mapentry(m));
+
+ if (upb_inttable_lookupptr(&f->layouts, m, &v)) {
+ UPB_ASSERT(upb_value_getptr(v));
+ return upb_value_getptr(v);
+ } else {
+ /* This method should be made thread-safe so the "const" is accurate. */
+ upb_msgfactory *mutable_f = (void*)f;
+ upb_msglayout *l = upb_msglayout_new(m);
+ upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l));
+ UPB_ASSERT(l);
+ return l;
+ }
+}
+
+/* Our handlers that we don't expose externally. */
+
+void *upb_msg_startstr(void *msg, const void *hd, size_t size_hint) {
+ uint32_t ofs = (uintptr_t)hd;
+ /* We pass NULL here because we know we can get away with it. */
+ upb_alloc *alloc = upb_msg_alloc(msg, NULL);
+ upb_msgval val;
+ UPB_UNUSED(size_hint);
+
+ val = upb_msgval_read(msg, ofs, upb_msgval_sizeof(UPB_TYPE_STRING));
+
+ upb_free(alloc, (void*)val.str.ptr);
+ val.str.ptr = NULL;
+ val.str.len = 0;
+
+ upb_msgval_write(msg, ofs, val, upb_msgval_sizeof(UPB_TYPE_STRING));
+ return msg;
+}
+
+size_t upb_msg_str(void *msg, const void *hd, const char *ptr, size_t size,
+ const upb_bufhandle *handle) {
+ uint32_t ofs = (uintptr_t)hd;
+ /* We pass NULL here because we know we can get away with it. */
+ upb_alloc *alloc = upb_msg_alloc(msg, NULL);
+ upb_msgval val;
+ size_t newsize;
+ UPB_UNUSED(handle);
+
+ val = upb_msgval_read(msg, ofs, upb_msgval_sizeof(UPB_TYPE_STRING));
+
+ newsize = val.str.len + size;
+ val.str.ptr = upb_realloc(alloc, (void*)val.str.ptr, val.str.len, newsize);
+
+ if (!val.str.ptr) {
+ return false;
+ }
+
+ memcpy((char*)val.str.ptr + val.str.len, ptr, size);
+ val.str.len = newsize;
+ upb_msgval_write(msg, ofs, val, upb_msgval_sizeof(UPB_TYPE_STRING));
+ return size;
+}
+
+static void callback(const void *closure, upb_handlers *h) {
+ const upb_msgfactory *factory = closure;
+ const upb_msgdef *md = upb_handlers_msgdef(h);
+ const upb_msglayout* layout = upb_msgfactory_getlayout(factory, md);
+ upb_msg_field_iter i;
+ UPB_UNUSED(factory);
+
+ for(upb_msg_field_begin(&i, md);
+ !upb_msg_field_done(&i);
+ upb_msg_field_next(&i)) {
+ const upb_fielddef *f = upb_msg_iter_field(&i);
+ size_t offset = upb_msglayout_offset(layout, f);
+ upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+ upb_handlerattr_sethandlerdata(&attr, (void*)offset);
+
+ if (upb_fielddef_isseq(f)) {
+ } else if (upb_fielddef_isstring(f)) {
+ upb_handlers_setstartstr(h, f, upb_msg_startstr, &attr);
+ upb_handlers_setstring(h, f, upb_msg_str, &attr);
+ } else {
+ upb_msg_setscalarhandler(
+ h, f, offset, upb_msglayout_hasbit(layout, f));
+ }
+ }
+}
+
+const upb_handlers *upb_msgfactory_getmergehandlers(const upb_msgfactory *f,
+ const upb_msgdef *m) {
+ /* This method should be made thread-safe so the "const" is accurate. */
+ upb_msgfactory *mutable_f = (void*)f;
+
+ /* TODO(haberman): properly cache these. */
+ const upb_handlers *ret = upb_handlers_newfrozen(m, f, callback, f);
+ upb_inttable_push(&mutable_f->mergehandlers, upb_value_constptr(ret));
+
+ return ret;
+}
+
+
+/** upb_msg *******************************************************************/
+
+/* If we always read/write as a consistent type to each address, this shouldn't
+ * violate aliasing.
+ */
+#define CHARPTR_AT(msg, ofs) ((char*)msg + ofs)
+#define DEREF(msg, ofs, type) *(type*)CHARPTR_AT(msg, ofs)
+
+static upb_inttable *upb_msg_trygetextdict(const upb_msg *msg,
+ const upb_msglayout *l) {
+ return l->has_extdict ? DEREF(msg, l->extdict_offset, upb_inttable*) : NULL;
+}
+
+static upb_inttable *upb_msg_getextdict(upb_msg *msg,
+ const upb_msglayout *l,
+ upb_alloc *a) {
+ upb_inttable *ext_dict;
+ UPB_ASSERT(l->has_extdict);
+
+ ext_dict = upb_msg_trygetextdict(msg, l);
+
+ if (!ext_dict) {
+ ext_dict = upb_malloc(a, sizeof(upb_inttable));
+
+ if (!ext_dict) {
+ return NULL;
+ }
+
+ /* Use an 8-byte type to ensure all bytes are copied. */
+ if (!upb_inttable_init2(ext_dict, UPB_CTYPE_INT64, a)) {
+ upb_free(a, ext_dict);
+ return NULL;
+ }
+
+ DEREF(msg, l->extdict_offset, upb_inttable*) = ext_dict;
+ }
+
+ return ext_dict;
+}
+
+static uint32_t upb_msg_getoneofint(const upb_msg *msg,
+ const upb_oneofdef *o,
+ const upb_msglayout *l) {
+ size_t oneof_ofs = l->case_offsets[upb_oneofdef_index(o)];
+ return DEREF(msg, oneof_ofs, uint8_t);
+}
+
+static void upb_msg_setoneofcase(const upb_msg *msg,
+ const upb_oneofdef *o,
+ const upb_msglayout *l,
+ uint32_t val) {
+ size_t oneof_ofs = l->case_offsets[upb_oneofdef_index(o)];
+ DEREF(msg, oneof_ofs, uint8_t) = val;
+}
+
+
+static bool upb_msg_oneofis(const upb_msg *msg, const upb_msglayout *l,
+ const upb_oneofdef *o, const upb_fielddef *f) {
+ return upb_msg_getoneofint(msg, o, l) == upb_fielddef_number(f);
+}
+
+size_t upb_msg_sizeof(const upb_msglayout *l) { return l->size; }
+
+void upb_msg_init(upb_msg *msg, const upb_msglayout *l, upb_alloc *a) {
+ if (l->default_msg) {
+ memcpy(msg, l->default_msg, l->size);
+ } else {
+ memset(msg, 0, l->size);
+ }
+
+ /* Set arena pointer. */
+ memcpy(msg, &a, sizeof(a));
+}
+
+void upb_msg_uninit(upb_msg *msg, const upb_msglayout *l) {
+ upb_inttable *ext_dict = upb_msg_trygetextdict(msg, l);
+ if (ext_dict) {
+ upb_inttable_uninit2(ext_dict, upb_msg_alloc(msg, l));
+ }
+}
+
+upb_msg *upb_msg_new(const upb_msglayout *l, upb_alloc *a) {
+ upb_msg *msg = upb_malloc(a, upb_msg_sizeof(l));
+
+ if (msg) {
+ upb_msg_init(msg, l, a);
+ }
+
+ return msg;
+}
+
+void upb_msg_free(upb_msg *msg, const upb_msglayout *l) {
+ upb_msg_uninit(msg, l);
+ upb_free(upb_msg_alloc(msg, l), msg);
+}
+
+upb_alloc *upb_msg_alloc(const upb_msg *msg, const upb_msglayout *l) {
+ upb_alloc *alloc;
+ UPB_UNUSED(l);
+ memcpy(&alloc, msg, sizeof(alloc));
+ return alloc;
+}
+
+#if 0
+void upb_msg_packedsize(const void *msg, const upb_msglayout *l, size_t *size) {
+ upb_msg_field_iter i;
+ upb_inttable *exttab;
+
+ if (!msg) {
+ return;
+ }
+
+ addsize(size, l->size, l->align);
+
+ exttab = upb_msg_trygetextdict(msg, l);
+ if (exttab) {
+ upb_inttable_packedsize(exttab, size);
+ }
+
+ /* TODO(haberman): should there be an easier (and maybe more efficient) way of
+ * iterating over only strings/submessages/repeated fields? */
+ for(upb_msg_field_begin(&i, l->msgdef);
+ !upb_msg_field_done(&i);
+ upb_msg_field_next(&i)) {
+ upb_fielddef *f = upb_msg_iter_field(&i);
+ if (upb_fielddef_isseq(f)) {
+ upb_array_packedsize(upb_msgval_getarr(upb_msg_get(msg, f, l)), size);
+ } else if (upb_fielddef_ismap(f)) {
+ upb_map_packedsize(upb_msgval_getmap(upb_msg_get(msg, f, l)), size);
+ } else if (upb_fielddef_issubmsg(f)) {
+ upb_msg_packedsize(upb_msgval_getmsg(upb_msg_get(msg, f, l)),
+ upb_msglayout_sublayout(l, f), size);
+ } else if (upb_fielddef_isstring(f)) {
+ addsize(size, upb_msg_get(msg, f, l).str.len, 1);
+ }
+ }
+}
+
+void *upb_msg_pack(const void *msg, const upb_msglayout *l,
+ void *p, size_t *ofs, size_t size) {
+ upb_msg_field_iter i;
+ void *packed_msg = alloc(p, ofs, size, l->size, l->align);
+ upb_inttable *exttab = upb_msg_trygetextdict(msg, l);
+
+ memcpy(packed_msg, msg, l->size);
+
+ if (exttab) {
+ upb_inttable *packed_exttab = upb_inttable_pack(exttab, p, ofs, size);
+
+ if (!packed_exttab) {
+ return NULL;
+ }
+
+ /* TODO(haberman): need to copy over repeated/submsg/string exts. */
+ DEREF(packed_msg, l->extdict_offset, upb_inttable*) = packed_exttab;
+ }
+
+ for(upb_msg_field_begin(&i, l->msgdef);
+ !upb_msg_field_done(&i);
+ upb_msg_field_next(&i)) {
+ upb_fielddef *f = upb_msg_iter_field(&i);
+ upb_msgval v;
+
+ if (upb_fielddef_isprimitive(f) && !upb_fielddef_isseq(f)) {
+ continue;
+ }
+
+ v = upb_msg_get(msg, f, l);
+
+ if (upb_fielddef_isseq(f)) {
+ v = upb_msgval_arr(upb_array_pack(upb_msgval_getarr(v), p, ofs, size));
+ } else if (upb_fielddef_ismap(f)) {
+ v = upb_msgval_map(upb_map_pack(upb_msgval_getmap(v), p, ofs, size));
+ } else if (upb_fielddef_issubmsg(f)) {
+ const upb_msglayout *sl = upb_msglayout_sublayout(l, f);
+ v = upb_msgval_msg(upb_msg_pack(upb_msgval_getmsg(v), sl, p, ofs, size));
+ } else if (upb_fielddef_isstring(f)) {
+ void *packed_str = alloc(p, ofs, size, v.str.len, 1);
+ memcpy(packed_str, v.str.ptr, v.str.len);
+ v = upb_msgval_str(packed_str, v.str.len);
+ }
+
+ upb_msg_set(packed_msg, f, v, l, NULL);
+ }
+
+ return packed_msg;
+}
+#endif
+
+bool upb_msg_has(const upb_msg *msg,
+ const upb_fielddef *f,
+ const upb_msglayout *l) {
+ const upb_oneofdef *o;
+ upb_msg_check(l, f);
+ UPB_ASSERT(upb_fielddef_haspresence(f));
+
+ if (upb_fielddef_isextension(f)) {
+ /* Extensions are set when they are present in the extension dict. */
+ upb_inttable *ext_dict = upb_msg_trygetextdict(msg, l);
+ upb_value v;
+ return ext_dict != NULL &&
+ upb_inttable_lookup32(ext_dict, upb_fielddef_number(f), &v);
+ } else if ((o = upb_fielddef_containingoneof(f)) != NULL) {
+ /* Oneofs are set when the oneof number is set to this field. */
+ return upb_msg_getoneofint(msg, o, l) == upb_fielddef_number(f);
+ } else {
+ /* Other fields are set when their hasbit is set. */
+ uint32_t hasbit = l->hasbits[upb_fielddef_index(f)];
+ return DEREF(msg, hasbit / 8, char) | (1 << (hasbit % 8));
+ }
+}
+
+upb_msgval upb_msg_get(const upb_msg *msg, const upb_fielddef *f,
+ const upb_msglayout *l) {
+ upb_msg_check(l, f);
+
+ if (upb_fielddef_isextension(f)) {
+ upb_inttable *ext_dict = upb_msg_trygetextdict(msg, l);
+ upb_value val;
+ if (upb_inttable_lookup32(ext_dict, upb_fielddef_number(f), &val)) {
+ return upb_msgval_fromval(val);
+ } else {
+ return upb_msgval_fromdefault(f);
+ }
+ } else {
+ size_t ofs = l->offsets[upb_fielddef_index(f)];
+ const upb_oneofdef *o = upb_fielddef_containingoneof(f);
+ upb_msgval ret;
+
+ if (o && !upb_msg_oneofis(msg, l, o, f)) {
+ /* Oneof defaults can't come from the message because the memory is reused
+ * by all types in the oneof. */
+ return upb_msgval_fromdefault(f);
+ }
+
+ ret = upb_msgval_read(msg, ofs, upb_msg_fieldsize(f));
+ return ret;
+ }
+}
+
+bool upb_msg_set(upb_msg *msg,
+ const upb_fielddef *f,
+ upb_msgval val,
+ const upb_msglayout *l) {
+ upb_alloc *a = upb_msg_alloc(msg, l);
+ upb_msg_check(l, f);
+
+ if (upb_fielddef_isextension(f)) {
+ /* TODO(haberman): introduce table API that can do this in one call. */
+ upb_inttable *ext = upb_msg_getextdict(msg, l, a);
+ upb_value val2 = upb_toval(val);
+ if (!upb_inttable_replace(ext, upb_fielddef_number(f), val2) &&
+ !upb_inttable_insert2(ext, upb_fielddef_number(f), val2, a)) {
+ return false;
+ }
+ } else {
+ size_t ofs = l->offsets[upb_fielddef_index(f)];
+ const upb_oneofdef *o = upb_fielddef_containingoneof(f);
+
+ if (o) {
+ upb_msg_setoneofcase(msg, o, l, upb_fielddef_number(f));
+ }
+
+ upb_msgval_write(msg, ofs, val, upb_msg_fieldsize(f));
+ }
+ return true;
+}
+
+
+/** upb_array *****************************************************************/
+
+struct upb_array {
+ upb_fieldtype_t type;
+ uint8_t element_size;
+ void *data; /* Each element is element_size. */
+ size_t len; /* Measured in elements. */
+ size_t size; /* Measured in elements. */
+ upb_alloc *alloc;
+};
+
+#define DEREF_ARR(arr, i, type) ((type*)arr->data)[i]
+
+size_t upb_array_sizeof(upb_fieldtype_t type) {
+ UPB_UNUSED(type);
+ return sizeof(upb_array);
+}
+
+void upb_array_init(upb_array *arr, upb_fieldtype_t type, upb_alloc *alloc) {
+ arr->type = type;
+ arr->data = NULL;
+ arr->len = 0;
+ arr->size = 0;
+ arr->element_size = upb_msgval_sizeof(type);
+ arr->alloc = alloc;
+}
+
+void upb_array_uninit(upb_array *arr) {
+ upb_free(arr->alloc, arr->data);
+}
+
+upb_array *upb_array_new(upb_fieldtype_t type, upb_alloc *a) {
+ upb_array *ret = upb_malloc(a, upb_array_sizeof(type));
+
+ if (ret) {
+ upb_array_init(ret, type, a);
+ }
+
+ return ret;
+}
+
+void upb_array_free(upb_array *arr) {
+ upb_array_uninit(arr);
+ upb_free(arr->alloc, arr);
+}
+
+#if 0
+void upb_array_packedsize(const upb_array *arr, size_t *size) {
+ if (!arr) {
+ return;
+ }
+
+ addsize(size, sizeof(upb_array), upb_alignof(upb_array));
+ addsize(size, arr->len * arr->element_size, arr->element_size);
+}
+#endif
+
+size_t upb_array_size(const upb_array *arr) {
+ return arr->len;
+}
+
+upb_fieldtype_t upb_array_type(const upb_array *arr) {
+ return arr->type;
+}
+
+upb_msgval upb_array_get(const upb_array *arr, size_t i) {
+ UPB_ASSERT(i < arr->len);
+ return upb_msgval_read(arr->data, i * arr->element_size, arr->element_size);
+}
+
+bool upb_array_set(upb_array *arr, size_t i, upb_msgval val) {
+ UPB_ASSERT(i <= arr->len);
+
+ if (i == arr->len) {
+ /* Extending the array. */
+
+ if (i == arr->size) {
+ /* Need to reallocate. */
+ size_t new_size = UPB_MAX(arr->size * 2, 8);
+ size_t new_bytes = new_size * arr->element_size;
+ size_t old_bytes = arr->size * arr->element_size;
+ upb_msgval *new_data =
+ upb_realloc(arr->alloc, arr->data, old_bytes, new_bytes);
+
+ if (!new_data) {
+ return false;
+ }
+
+ arr->data = new_data;
+ arr->size = new_size;
+ }
+
+ arr->len = i + 1;
+ }
+
+ upb_msgval_write(arr->data, i * arr->element_size, val, arr->element_size);
+ return true;
+}
+
+
+/** upb_map *******************************************************************/
+
+struct upb_map {
+ upb_fieldtype_t key_type;
+ upb_fieldtype_t val_type;
+ /* We may want to optimize this to use inttable where possible, for greater
+ * efficiency and lower memory footprint. */
+ upb_strtable strtab;
+ upb_alloc *alloc;
+};
+
+static void upb_map_tokey(upb_fieldtype_t type, upb_msgval *key,
+ const char **out_key, size_t *out_len) {
+ switch (type) {
+ case UPB_TYPE_STRING:
+ /* Point to string data of the input key. */
+ *out_key = key->str.ptr;
+ *out_len = key->str.len;
+ return;
+ case UPB_TYPE_BOOL:
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_UINT32:
+ case UPB_TYPE_INT64:
+ case UPB_TYPE_UINT64:
+ /* Point to the key itself. XXX: big-endian. */
+ *out_key = (const char*)key;
+ *out_len = upb_msgval_sizeof(type);
+ return;
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_DOUBLE:
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_FLOAT:
+ case UPB_TYPE_MESSAGE:
+ break; /* Cannot be a map key. */
+ }
+ UPB_UNREACHABLE();
+}
+
+static upb_msgval upb_map_fromkey(upb_fieldtype_t type, const char *key,
+ size_t len) {
+ switch (type) {
+ case UPB_TYPE_STRING:
+ return upb_msgval_str(key, len);
+ case UPB_TYPE_BOOL:
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_UINT32:
+ case UPB_TYPE_INT64:
+ case UPB_TYPE_UINT64:
+ return upb_msgval_read(key, 0, upb_msgval_sizeof(type));
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_DOUBLE:
+ case UPB_TYPE_ENUM:
+ case UPB_TYPE_FLOAT:
+ case UPB_TYPE_MESSAGE:
+ break; /* Cannot be a map key. */
+ }
+ UPB_UNREACHABLE();
+}
+
+size_t upb_map_sizeof(upb_fieldtype_t ktype, upb_fieldtype_t vtype) {
+ /* Size does not currently depend on key/value type. */
+ UPB_UNUSED(ktype);
+ UPB_UNUSED(vtype);
+ return sizeof(upb_map);
+}
+
+#if 0
+void upb_map_packedsize(const upb_map *map, size_t *size) {
+ if (!map) {
+ return;
+ }
+
+ addsize(size, sizeof(upb_map), upb_alignof(upb_map));
+ upb_strtable_packedsize(&map->strtab, size);
+}
+#endif
+
+bool upb_map_init(upb_map *map, upb_fieldtype_t ktype, upb_fieldtype_t vtype,
+ upb_alloc *a) {
+ upb_ctype_t vtabtype = upb_fieldtotabtype(vtype);
+ UPB_ASSERT(upb_fieldtype_mapkeyok(ktype));
+ map->key_type = ktype;
+ map->val_type = vtype;
+ map->alloc = a;
+
+ if (!upb_strtable_init2(&map->strtab, vtabtype, a)) {
+ return false;
+ }
+
+ return true;
+}
+
+void upb_map_uninit(upb_map *map) {
+ upb_strtable_uninit2(&map->strtab, map->alloc);
+}
+
+upb_map *upb_map_new(upb_fieldtype_t ktype, upb_fieldtype_t vtype,
+ upb_alloc *a) {
+ upb_map *map = upb_malloc(a, upb_map_sizeof(ktype, vtype));
+
+ if (!map) {
+ return NULL;
+ }
+
+ if (!upb_map_init(map, ktype, vtype, a)) {
+ return NULL;
+ }
+
+ return map;
+}
+
+void upb_map_free(upb_map *map) {
+ upb_map_uninit(map);
+ upb_free(map->alloc, map);
+}
+
+size_t upb_map_size(const upb_map *map) {
+ return upb_strtable_count(&map->strtab);
+}
+
+upb_fieldtype_t upb_map_keytype(const upb_map *map) {
+ return map->key_type;
+}
+
+upb_fieldtype_t upb_map_valuetype(const upb_map *map) {
+ return map->val_type;
+}
+
+bool upb_map_get(const upb_map *map, upb_msgval key, upb_msgval *val) {
+ upb_value tabval;
+ const char *key_str;
+ size_t key_len;
+ bool ret;
+
+ upb_map_tokey(map->key_type, &key, &key_str, &key_len);
+ ret = upb_strtable_lookup2(&map->strtab, key_str, key_len, &tabval);
+ if (ret) {
+ memcpy(val, &tabval, sizeof(tabval));
+ }
+
+ return ret;
+}
+
+bool upb_map_set(upb_map *map, upb_msgval key, upb_msgval val,
+ upb_msgval *removed) {
+ const char *key_str;
+ size_t key_len;
+ upb_value tabval = upb_msgval_toval(val, map->val_type);
+ upb_value removedtabval;
+ upb_alloc *a = map->alloc;
+
+ upb_map_tokey(map->key_type, &key, &key_str, &key_len);
+
+ if (upb_strtable_lookup2(&map->strtab, key_str, key_len, NULL)) {
+ upb_strtable_remove3(&map->strtab, key_str, key_len, &removedtabval, a);
+ memcpy(&removed, &removedtabval, sizeof(removed));
+ }
+
+ return upb_strtable_insert3(&map->strtab, key_str, key_len, tabval, a);
+}
+
+bool upb_map_del(upb_map *map, upb_msgval key) {
+ const char *key_str;
+ size_t key_len;
+ upb_alloc *a = map->alloc;
+
+ upb_map_tokey(map->key_type, &key, &key_str, &key_len);
+ return upb_strtable_remove3(&map->strtab, key_str, key_len, NULL, a);
+}
+
+
+/** upb_mapiter ***************************************************************/
+
+struct upb_mapiter {
+ upb_strtable_iter iter;
+ upb_fieldtype_t key_type;
+};
+
+size_t upb_mapiter_sizeof() {
+ return sizeof(upb_mapiter);
+}
+
+void upb_mapiter_begin(upb_mapiter *i, const upb_map *map) {
+ upb_strtable_begin(&i->iter, &map->strtab);
+ i->key_type = map->key_type;
+}
+
+upb_mapiter *upb_mapiter_new(const upb_map *t, upb_alloc *a) {
+ upb_mapiter *ret = upb_malloc(a, upb_mapiter_sizeof());
+
+ if (!ret) {
+ return NULL;
+ }
+
+ upb_mapiter_begin(ret, t);
+ return ret;
+}
+
+void upb_mapiter_free(upb_mapiter *i, upb_alloc *a) {
+ upb_free(a, i);
+}
+
+void upb_mapiter_next(upb_mapiter *i) {
+ upb_strtable_next(&i->iter);
+}
+
+bool upb_mapiter_done(const upb_mapiter *i) {
+ return upb_strtable_done(&i->iter);
+}
+
+upb_msgval upb_mapiter_key(const upb_mapiter *i) {
+ return upb_map_fromkey(i->key_type, upb_strtable_iter_key(&i->iter),
+ upb_strtable_iter_keylength(&i->iter));
+}
+
+upb_msgval upb_mapiter_value(const upb_mapiter *i) {
+ return upb_msgval_fromval(upb_strtable_iter_value(&i->iter));
+}
+
+void upb_mapiter_setdone(upb_mapiter *i) {
+ upb_strtable_iter_setdone(&i->iter);
+}
+
+bool upb_mapiter_isequal(const upb_mapiter *i1, const upb_mapiter *i2) {
+ return upb_strtable_iter_isequal(&i1->iter, &i2->iter);
+}
+
+
+/** Handlers for upb_msg ******************************************************/
+
+typedef struct {
+ size_t offset;
+ int32_t hasbit;
+} upb_msg_handlerdata;
+
+/* Fallback implementation if the handler is not specialized by the producer. */
+#define MSG_WRITER(type, ctype) \
+ bool upb_msg_set ## type (void *c, const void *hd, ctype val) { \
+ uint8_t *m = c; \
+ const upb_msg_handlerdata *d = hd; \
+ if (d->hasbit > 0) \
+ *(uint8_t*)&m[d->hasbit / 8] |= 1 << (d->hasbit % 8); \
+ *(ctype*)&m[d->offset] = val; \
+ return true; \
+ } \
+
+MSG_WRITER(double, double)
+MSG_WRITER(float, float)
+MSG_WRITER(int32, int32_t)
+MSG_WRITER(int64, int64_t)
+MSG_WRITER(uint32, uint32_t)
+MSG_WRITER(uint64, uint64_t)
+MSG_WRITER(bool, bool)
+
+bool upb_msg_setscalarhandler(upb_handlers *h, const upb_fielddef *f,
+ size_t offset, int32_t hasbit) {
+ upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
+ bool ok;
+
+ upb_msg_handlerdata *d = upb_gmalloc(sizeof(*d));
+ if (!d) return false;
+ d->offset = offset;
+ d->hasbit = hasbit;
+
+ upb_handlerattr_sethandlerdata(&attr, d);
+ upb_handlerattr_setalwaysok(&attr, true);
+ upb_handlers_addcleanup(h, d, upb_gfree);
+
+#define TYPE(u, l) \
+ case UPB_TYPE_##u: \
+ ok = upb_handlers_set##l(h, f, upb_msg_set##l, &attr); break;
+
+ ok = false;
+
+ switch (upb_fielddef_type(f)) {
+ TYPE(INT64, int64);
+ TYPE(INT32, int32);
+ TYPE(ENUM, int32);
+ TYPE(UINT64, uint64);
+ TYPE(UINT32, uint32);
+ TYPE(DOUBLE, double);
+ TYPE(FLOAT, float);
+ TYPE(BOOL, bool);
+ default: UPB_ASSERT(false); break;
+ }
+#undef TYPE
+
+ upb_handlerattr_uninit(&attr);
+ return ok;
+}
+
+bool upb_msg_getscalarhandlerdata(const upb_handlers *h,
+ upb_selector_t s,
+ upb_fieldtype_t *type,
+ size_t *offset,
+ int32_t *hasbit) {
+ const upb_msg_handlerdata *d;
+ upb_func *f = upb_handlers_gethandler(h, s);
+
+ if ((upb_int64_handlerfunc*)f == upb_msg_setint64) {
+ *type = UPB_TYPE_INT64;
+ } else if ((upb_int32_handlerfunc*)f == upb_msg_setint32) {
+ *type = UPB_TYPE_INT32;
+ } else if ((upb_uint64_handlerfunc*)f == upb_msg_setuint64) {
+ *type = UPB_TYPE_UINT64;
+ } else if ((upb_uint32_handlerfunc*)f == upb_msg_setuint32) {
+ *type = UPB_TYPE_UINT32;
+ } else if ((upb_double_handlerfunc*)f == upb_msg_setdouble) {
+ *type = UPB_TYPE_DOUBLE;
+ } else if ((upb_float_handlerfunc*)f == upb_msg_setfloat) {
+ *type = UPB_TYPE_FLOAT;
+ } else if ((upb_bool_handlerfunc*)f == upb_msg_setbool) {
+ *type = UPB_TYPE_BOOL;
+ } else {
+ return false;
+ }
+
+ d = upb_handlers_gethandlerdata(h, s);
+ *offset = d->offset;
+ *hasbit = d->hasbit;
+ return true;
+}
diff --git a/upb/msg.h b/upb/msg.h
new file mode 100644
index 0000000..912d6ac
--- /dev/null
+++ b/upb/msg.h
@@ -0,0 +1,340 @@
+/*
+** upb::Message is a representation for protobuf messages.
+**
+** However it differs from other common representations like
+** google::protobuf::Message in one key way: it does not prescribe any
+** ownership semantics, and it does not perform any memory management.
+**
+** A client can access a upb::Message without knowing anything about
+** ownership semantics, but to create or mutate a message a user needs
+** to implement the memory management themselves.
+**
+** Currently all messages, arrays, and maps store a upb_alloc* internally.
+** Mutating operations use this when they require dynamically-allocated
+** memory. We could potentially eliminate this size overhead later by
+** letting the user flip a bit on the factory that prevents this from
+** being stored. The user would then need to use separate functions where
+** the upb_alloc* is passed explicitly. However for handlers to populate
+** such structures, they would need a place to store this upb_alloc* during
+** parsing; upb_handlers don't currently have a good way to accommodate this.
+**
+** TODO: UTF-8 checking?
+**/
+
+#ifndef UPB_MSG_H_
+#define UPB_MSG_H_
+
+#include "upb/def.h"
+#include "upb/handlers.h"
+#include "upb/sink.h"
+#include "upb/symtab.h"
+
+#ifdef __cplusplus
+
+namespace upb {
+class MessageLayout;
+class MessageFactory;
+}
+
+#endif
+
+UPB_DECLARE_TYPE(upb::MessageFactory, upb_msgfactory)
+UPB_DECLARE_TYPE(upb::MessageLayout, upb_msglayout)
+UPB_DECLARE_TYPE(upb::Array, upb_array)
+UPB_DECLARE_TYPE(upb::Map, upb_map)
+UPB_DECLARE_TYPE(upb::MapIterator, upb_mapiter)
+UPB_DECLARE_TYPE(upb::Visitor, upb_visitor)
+UPB_DECLARE_TYPE(upb::VisitorPlan, upb_visitorplan)
+
+/* TODO(haberman): C++ accessors */
+
+UPB_BEGIN_EXTERN_C
+
+
+/** upb_msglayout *************************************************************/
+
+/* Please note that map_entry messages (upb_msgdef_mapentry(m) == true) cannot
+ * have layouts. They can only be represented as upb_map, not as a message. */
+
+/* Requires that upb_fielddef_issubmsg(upb_msglayout_msgdef(l)) == true.
+ *
+ * Since map entry messages don't have layouts, if upb_fielddef_ismap(f) == true
+ * then this function will return the layout for the map's value. It requires
+ * that the value type of the map field is a submessage. */
+const upb_msglayout *upb_msglayout_sublayout(const upb_msglayout *l,
+ const upb_fielddef *f);
+
+const upb_msgdef *upb_msglayout_msgdef(const upb_msglayout *l);
+
+
+/** upb_msgfactory ************************************************************/
+
+upb_msgfactory *upb_msgfactory_new(const upb_symtab *symtab);
+void upb_msgfactory_free(upb_msgfactory *f);
+
+/* Requires:
+ * - m is in upb_msgfactory_symtab(f)
+ * - upb_msgdef_mapentry(m) == false (since map messages can't have layouts).
+ *
+ * The returned layout will live for as long as the msgfactory does.
+ */
+const upb_msglayout *upb_msgfactory_getlayout(const upb_msgfactory *f,
+ const upb_msgdef *m);
+
+/* Returns handlers for populating a message with the given msgdef. */
+const upb_handlers *upb_msgfactory_getmergehandlers(const upb_msgfactory *f,
+ const upb_msgdef *m);
+
+/* Returns a plan for visiting the data and submessages of the given msgdef. */
+const upb_visitorplan *upb_visitorplan_new(const upb_msgfactory *f,
+ const upb_msgdef *m);
+
+/* For actually visiting a message and its submessages. */
+upb_visitor *upb_visitor_create(upb_env *e, const upb_visitorplan *vp,
+ upb_sink *output);
+bool upb_visitor_visitmsg(upb_visitor *v, const void *msg);
+
+
+/** upb_msgval ****************************************************************/
+
+/* A union representing all possible protobuf values. Used for generic get/set
+ * operations. */
+
+typedef void upb_msg;
+
+typedef union {
+ bool b;
+ float flt;
+ double dbl;
+ int32_t i32;
+ int64_t i64;
+ uint32_t u32;
+ uint64_t u64;
+ const void* ptr;
+ struct {
+ const char *ptr;
+ size_t len;
+ } str;
+} upb_msgval;
+
+#define ACCESSORS(name, membername, ctype) \
+ UPB_INLINE ctype upb_msgval_get ## name(upb_msgval v) { \
+ return v.membername; \
+ } \
+ UPB_INLINE void upb_msgval_set ## name(upb_msgval *v, ctype cval) { \
+ v->membername = cval; \
+ } \
+ UPB_INLINE upb_msgval upb_msgval_ ## name(ctype v) { \
+ upb_msgval ret; \
+ ret.membername = v; \
+ return ret; \
+ }
+
+ACCESSORS(bool, b, bool)
+ACCESSORS(float, flt, float)
+ACCESSORS(double, dbl, double)
+ACCESSORS(int32, i32, int32_t)
+ACCESSORS(int64, i64, int64_t)
+ACCESSORS(uint32, u32, uint32_t)
+ACCESSORS(uint64, u64, uint64_t)
+ACCESSORS(map, ptr, const upb_map*)
+ACCESSORS(msg, ptr, const upb_msg*)
+ACCESSORS(ptr, ptr, const void*)
+ACCESSORS(arr, ptr, const upb_array*)
+
+#undef ACCESSORS
+
+UPB_INLINE upb_msgval upb_msgval_str(const char *ptr, size_t len) {
+ upb_msgval ret;
+ ret.str.ptr = ptr;
+ ret.str.len = len;
+ return ret;
+}
+
+
+/** upb_msg *******************************************************************/
+
+size_t upb_msg_sizeof(const upb_msglayout *l);
+void upb_msg_init(upb_msg *msg, const upb_msglayout *l, upb_alloc *a);
+void upb_msg_uninit(upb_msg *msg, const upb_msglayout *l);
+
+upb_msg *upb_msg_new(const upb_msglayout *l, upb_alloc *a);
+void upb_msg_free(upb_msg *msg, const upb_msglayout *l);
+
+upb_alloc *upb_msg_alloc(const upb_msg *msg, const upb_msglayout *l);
+
+/* Packs the tree of messages rooted at "msg" into a single hunk of memory,
+ * allocated from the given allocator. */
+void *upb_msg_pack(const upb_msg *msg, const upb_msglayout *l,
+ void *p, size_t *ofs, size_t size);
+
+/* Read-only message API. Can be safely called by anyone. */
+
+/* Returns the value associated with this field:
+ * - for scalar fields (including strings), the value directly.
+ * - return upb_msg*, or upb_map* for msg/map.
+ * If the field is unset for these field types, returns NULL.
+ *
+ * TODO(haberman): should we let users store cached array/map/msg
+ * pointers here for fields that are unset? Could be useful for the
+ * strongly-owned submessage model (ie. generated C API that doesn't use
+ * arenas).
+ */
+upb_msgval upb_msg_get(const upb_msg *msg,
+ const upb_fielddef *f,
+ const upb_msglayout *l);
+
+/* May only be called for fields where upb_fielddef_haspresence(f) == true. */
+bool upb_msg_has(const upb_msg *msg,
+ const upb_fielddef *f,
+ const upb_msglayout *l);
+
+/* Returns NULL if no field in the oneof is set. */
+const upb_fielddef *upb_msg_getoneofcase(const upb_msg *msg,
+ const upb_oneofdef *o,
+ const upb_msglayout *l);
+
+/* Returns true if any field in the oneof is set. */
+bool upb_msg_hasoneof(const upb_msg *msg,
+ const upb_oneofdef *o,
+ const upb_msglayout *l);
+
+
+/* Mutable message API. May only be called by the owner of the message who
+ * knows its ownership scheme and how to keep it consistent. */
+
+/* Sets the given field to the given value. Does not perform any memory
+ * management: if you overwrite a pointer to a msg/array/map/string without
+ * cleaning it up (or using an arena) it will leak.
+ */
+bool upb_msg_set(upb_msg *msg,
+ const upb_fielddef *f,
+ upb_msgval val,
+ const upb_msglayout *l);
+
+/* For a primitive field, set it back to its default. For repeated, string, and
+ * submessage fields set it back to NULL. This could involve releasing some
+ * internal memory (for example, from an extension dictionary), but it is not
+ * recursive in any way and will not recover any memory that may be used by
+ * arrays/maps/strings/msgs that this field may have pointed to.
+ */
+bool upb_msg_clearfield(upb_msg *msg,
+ const upb_fielddef *f,
+ const upb_msglayout *l);
+
+/* Clears all fields in the oneof such that none of them are set. */
+bool upb_msg_clearoneof(upb_msg *msg,
+ const upb_oneofdef *o,
+ const upb_msglayout *l);
+
+/* TODO(haberman): copyfrom()/mergefrom()? */
+
+
+/** upb_array *****************************************************************/
+
+size_t upb_array_sizeof(upb_fieldtype_t type);
+void upb_array_init(upb_array *arr, upb_fieldtype_t type, upb_alloc *a);
+void upb_array_uninit(upb_array *arr);
+upb_array *upb_array_new(upb_fieldtype_t type, upb_alloc *a);
+void upb_array_free(upb_array *arr);
+
+/* Read-only interface. Safe for anyone to call. */
+
+size_t upb_array_size(const upb_array *arr);
+upb_fieldtype_t upb_array_type(const upb_array *arr);
+upb_msgval upb_array_get(const upb_array *arr, size_t i);
+
+/* Write interface. May only be called by the message's owner who can enforce
+ * its memory management invariants. */
+
+bool upb_array_set(upb_array *arr, size_t i, upb_msgval val);
+
+
+/** upb_map *******************************************************************/
+
+/* Stores data for a map field. The map will internally allocate (and free, if
+ * desired) all the internal storage used for the hash table or tree, using the
+ * given allocator. It will also copy and internally store the data for string
+ * keys, but *not* for string or message *values*. So the caller must ensure
+ * that any string or message values outlive the map. */
+
+size_t upb_map_sizeof(upb_fieldtype_t ktype, upb_fieldtype_t vtype);
+bool upb_map_init(upb_map *map, upb_fieldtype_t ktype, upb_fieldtype_t vtype,
+ upb_alloc *a);
+void upb_map_uninit(upb_map *map);
+upb_map *upb_map_new(upb_fieldtype_t ktype, upb_fieldtype_t vtype, upb_alloc *a);
+void upb_map_free(upb_map *map);
+
+/* Read-only interface. Safe for anyone to call. */
+
+size_t upb_map_size(const upb_map *map);
+upb_fieldtype_t upb_map_keytype(const upb_map *map);
+upb_fieldtype_t upb_map_valuetype(const upb_map *map);
+bool upb_map_get(const upb_map *map, upb_msgval key, upb_msgval *val);
+
+/* Write interface. May only be called by the message's owner who can enforce
+ * its memory management invariants. */
+
+/* Sets or overwrites an entry in the map. Return value indicates whether
+ * the operation succeeded or failed with OOM, and also whether an existing
+ * key was replaced or not. */
+bool upb_map_set(upb_map *map,
+ upb_msgval key, upb_msgval val,
+ upb_msgval *valremoved);
+
+/* Deletes an entry in the map. Returns true if the key was present. */
+bool upb_map_del(upb_map *map, upb_msgval key);
+
+
+/** upb_mapiter ***************************************************************/
+
+/* For iterating over a map. Map iterators are invalidated by mutations to the
+ * map, but an invalidated iterator will never return junk or crash the process.
+ * An invalidated iterator may return entries that were already returned though,
+ * and if you keep invalidating the iterator during iteration, the program may
+ * enter an infinite loop. */
+
+size_t upb_mapiter_sizeof();
+
+void upb_mapiter_begin(upb_mapiter *i, const upb_map *t);
+upb_mapiter *upb_mapiter_new(const upb_map *t, upb_alloc *a);
+void upb_mapiter_free(upb_mapiter *i, upb_alloc *a);
+void upb_mapiter_next(upb_mapiter *i);
+bool upb_mapiter_done(const upb_mapiter *i);
+
+/* For string keys, the value will be in upb_msgval_strkey(), *not*
+ * upb_msgval_str(). */
+upb_msgval upb_mapiter_key(const upb_mapiter *i);
+upb_msgval upb_mapiter_value(const upb_mapiter *i);
+void upb_mapiter_setdone(upb_mapiter *i);
+bool upb_mapiter_isequal(const upb_mapiter *i1, const upb_mapiter *i2);
+
+
+/** Handlers ******************************************************************/
+
+/* These are the handlers used internally by upb_msgfactory_getmergehandlers().
+ * They write scalar data to a known offset from the message pointer.
+ *
+ * These would be trivial for anyone to implement themselves, but it's better
+ * to use these because some JITs will recognize and specialize these instead
+ * of actually calling the function. */
+
+/* Sets a handler for the given primitive field that will write the data at the
+ * given offset. If hasbit > 0, also sets a hasbit at the given bit offset
+ * (addressing each byte low to high). */
+bool upb_msg_setscalarhandler(upb_handlers *h,
+ const upb_fielddef *f,
+ size_t offset,
+ int32_t hasbit);
+
+/* If the given handler is a msghandlers_primitive field, returns true and sets
+ * *type, *offset and *hasbit. Otherwise returns false. */
+bool upb_msg_getscalarhandlerdata(const upb_handlers *h,
+ upb_selector_t s,
+ upb_fieldtype_t *type,
+ size_t *offset,
+ int32_t *hasbit);
+
+UPB_END_EXTERN_C
+
+#endif /* UPB_MSG_H_ */
diff --git a/upb/shim/shim.c b/upb/shim/shim.c
deleted file mode 100644
index 1429332..0000000
--- a/upb/shim/shim.c
+++ /dev/null
@@ -1,84 +0,0 @@
-
-#include "upb/shim/shim.h"
-
-/* Fallback implementation if the shim is not specialized by the JIT. */
-#define SHIM_WRITER(type, ctype) \
- bool upb_shim_set ## type (void *c, const void *hd, ctype val) { \
- uint8_t *m = c; \
- const upb_shim_data *d = hd; \
- if (d->hasbit > 0) \
- *(uint8_t*)&m[d->hasbit / 8] |= 1 << (d->hasbit % 8); \
- *(ctype*)&m[d->offset] = val; \
- return true; \
- } \
-
-SHIM_WRITER(double, double)
-SHIM_WRITER(float, float)
-SHIM_WRITER(int32, int32_t)
-SHIM_WRITER(int64, int64_t)
-SHIM_WRITER(uint32, uint32_t)
-SHIM_WRITER(uint64, uint64_t)
-SHIM_WRITER(bool, bool)
-#undef SHIM_WRITER
-
-bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset,
- int32_t hasbit) {
- upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
- bool ok;
-
- upb_shim_data *d = upb_gmalloc(sizeof(*d));
- if (!d) return false;
- d->offset = offset;
- d->hasbit = hasbit;
-
- upb_handlerattr_sethandlerdata(&attr, d);
- upb_handlerattr_setalwaysok(&attr, true);
- upb_handlers_addcleanup(h, d, upb_gfree);
-
-#define TYPE(u, l) \
- case UPB_TYPE_##u: \
- ok = upb_handlers_set##l(h, f, upb_shim_set##l, &attr); break;
-
- ok = false;
-
- switch (upb_fielddef_type(f)) {
- TYPE(INT64, int64);
- TYPE(INT32, int32);
- TYPE(ENUM, int32);
- TYPE(UINT64, uint64);
- TYPE(UINT32, uint32);
- TYPE(DOUBLE, double);
- TYPE(FLOAT, float);
- TYPE(BOOL, bool);
- default: UPB_ASSERT(false); break;
- }
-#undef TYPE
-
- upb_handlerattr_uninit(&attr);
- return ok;
-}
-
-const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s,
- upb_fieldtype_t *type) {
- upb_func *f = upb_handlers_gethandler(h, s);
-
- if ((upb_int64_handlerfunc*)f == upb_shim_setint64) {
- *type = UPB_TYPE_INT64;
- } else if ((upb_int32_handlerfunc*)f == upb_shim_setint32) {
- *type = UPB_TYPE_INT32;
- } else if ((upb_uint64_handlerfunc*)f == upb_shim_setuint64) {
- *type = UPB_TYPE_UINT64;
- } else if ((upb_uint32_handlerfunc*)f == upb_shim_setuint32) {
- *type = UPB_TYPE_UINT32;
- } else if ((upb_double_handlerfunc*)f == upb_shim_setdouble) {
- *type = UPB_TYPE_DOUBLE;
- } else if ((upb_float_handlerfunc*)f == upb_shim_setfloat) {
- *type = UPB_TYPE_FLOAT;
- } else if ((upb_bool_handlerfunc*)f == upb_shim_setbool) {
- *type = UPB_TYPE_BOOL;
- } else {
- return NULL;
- }
-
- return (const upb_shim_data*)upb_handlers_gethandlerdata(h, s);
-}
diff --git a/upb/shim/shim.h b/upb/shim/shim.h
deleted file mode 100644
index 935f085..0000000
--- a/upb/shim/shim.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-** For handlers that do very tiny, very simple operations, the function call
-** overhead of calling a handler can be significant. This file allows the
-** user to define handlers that do something very simple like store the value
-** to memory and/or set a hasbit. JIT compilers can then special-case these
-** handlers and emit specialized code for them instead of actually calling the
-** handler.
-**
-** The functionality is very simple/limited right now but may expand to be able
-** to call another function.
-*/
-
-#ifndef UPB_SHIM_H
-#define UPB_SHIM_H
-
-#include "upb/handlers.h"
-
-typedef struct {
- size_t offset;
- int32_t hasbit;
-} upb_shim_data;
-
-#ifdef __cplusplus
-
-namespace upb {
-
-struct Shim {
- typedef upb_shim_data Data;
-
- /* Sets a handler for the given field that writes the value to the given
- * offset and, if hasbit >= 0, sets a bit at the given bit offset. Returns
- * true if the handler was set successfully. */
- static bool Set(Handlers *h, const FieldDef *f, size_t ofs, int32_t hasbit);
-
- /* If this handler is a shim, returns the corresponding upb::Shim::Data and
- * stores the type in "type". Otherwise returns NULL. */
- static const Data* GetData(const Handlers* h, Handlers::Selector s,
- FieldDef::Type* type);
-};
-
-} /* namespace upb */
-
-#endif
-
-UPB_BEGIN_EXTERN_C
-
-/* C API. */
-bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset,
- int32_t hasbit);
-const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s,
- upb_fieldtype_t *type);
-
-UPB_END_EXTERN_C
-
-#ifdef __cplusplus
-/* C++ Wrappers. */
-namespace upb {
-inline bool Shim::Set(Handlers* h, const FieldDef* f, size_t ofs,
- int32_t hasbit) {
- return upb_shim_set(h, f, ofs, hasbit);
-}
-inline const Shim::Data* Shim::GetData(const Handlers* h, Handlers::Selector s,
- FieldDef::Type* type) {
- return upb_shim_getdata(h, s, type);
-}
-} /* namespace upb */
-#endif
-
-#endif /* UPB_SHIM_H */
diff --git a/upb/structdefs.int.h b/upb/structdefs.int.h
index 6b19537..740b2dc 100644
--- a/upb/structdefs.int.h
+++ b/upb/structdefs.int.h
@@ -152,6 +152,7 @@ extern const struct upb_refcounted_vtbl upb_enumdef_vtbl;
struct upb_oneofdef {
upb_refcounted base;
+ uint32_t index; /* Index within oneofs. */
const char *name;
upb_strtable ntof;
upb_inttable itof;
@@ -161,7 +162,7 @@ struct upb_oneofdef {
extern const struct upb_refcounted_vtbl upb_oneofdef_vtbl;
#define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \
- { UPB_REFCOUNT_INIT(&upb_oneofdef_vtbl, refs, ref2s), name, ntof, itof }
+ { UPB_REFCOUNT_INIT(&upb_oneofdef_vtbl, refs, ref2s), 0, name, ntof, itof }
/* upb_symtab *****************************************************************/
diff --git a/upb/table.int.h b/upb/table.int.h
index f0b395a..225235e 100644
--- a/upb/table.int.h
+++ b/upb/table.int.h
@@ -43,7 +43,9 @@ typedef enum {
UPB_CTYPE_CSTR = 6,
UPB_CTYPE_PTR = 7,
UPB_CTYPE_CONSTPTR = 8,
- UPB_CTYPE_FPTR = 9
+ UPB_CTYPE_FPTR = 9,
+ UPB_CTYPE_FLOAT = 10,
+ UPB_CTYPE_DOUBLE = 11
} upb_ctype_t;
typedef struct {
@@ -117,6 +119,29 @@ 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));
+ SET_TYPE(val->ctype, UPB_CTYPE_FLOAT);
+}
+
+UPB_INLINE void upb_value_setdouble(upb_value *val, double cval) {
+ memcpy(&val->val, &cval, sizeof(cval));
+ SET_TYPE(val->ctype, UPB_CTYPE_DOUBLE);
+}
+
+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
@@ -359,6 +384,13 @@ 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);
+
/* 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.
diff --git a/upb/upb.c b/upb/upb.c
index 72e6fdb..c24ac04 100644
--- a/upb/upb.c
+++ b/upb/upb.c
@@ -231,6 +231,10 @@ void upb_arena_uninit(upb_arena *a) {
block = next;
}
+
+ /* Protect against multiple-uninit. */
+ a->cleanup_head = NULL;
+ a->block_head = NULL;
}
bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud) {
diff --git a/upb/upb.h b/upb/upb.h
index 6c7a627..aafc993 100644
--- a/upb/upb.h
+++ b/upb/upb.h
@@ -208,6 +208,12 @@ template <int N> class InlinedEnvironment;
* exist in debug mode. This turns into regular assert. */
#define UPB_ASSERT_DEBUGVAR(expr) assert(expr)
+#ifdef __GNUC__
+#define UPB_UNREACHABLE() do { assert(0); __builtin_unreachable(); } while(0)
+#else
+#define UPB_UNREACHABLE() do { assert(0); } while(0)
+#endif
+
/* Generic function type. */
typedef void upb_func();
@@ -441,17 +447,18 @@ struct upb_alloc {
};
UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) {
- UPB_ASSERT(size > 0);
+ 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(size > 0);
+ 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);
}
@@ -500,7 +507,6 @@ UPB_BEGIN_EXTERN_C
void upb_arena_init(upb_arena *a);
void upb_arena_init2(upb_arena *a, void *mem, size_t n, upb_alloc *alloc);
void upb_arena_uninit(upb_arena *a);
-upb_alloc *upb_arena_alloc(upb_arena *a);
bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud);
size_t upb_arena_bytesallocated(const upb_arena *a);
void upb_arena_setnextblocksize(upb_arena *a, size_t size);
@@ -585,6 +591,10 @@ struct upb_arena {
void *future2;
};
+UPB_BEGIN_EXTERN_C
+UPB_INLINE upb_alloc *upb_arena_alloc(upb_arena *a) { return &a->alloc; }
+UPB_END_EXTERN_C
+
/* upb::Environment ***********************************************************/
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback