From 15c388b819ebfebb83851950893413041288970f Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Mon, 12 Dec 2016 18:47:55 +0000 Subject: Basic serialization for upb_msg and Lua. Doesn't yet include strings, submessages, maps, or repeated fields. --- Makefile | 5 +- tests/bindings/lua/test_upb.pb.lua | 14 +- upb/bindings/lua/msg.c | 30 +- upb/bindings/lua/upb.h | 4 +- upb/bindings/lua/upb/msg.c | 892 ------------------------------------- upb/bindings/lua/upb/pb.c | 67 ++- upb/msg.c | 151 +++++++ upb/msg.h | 21 +- upb/sink.c | 88 ++++ upb/sink.h | 26 +- 10 files changed, 358 insertions(+), 940 deletions(-) delete mode 100644 upb/bindings/lua/upb/msg.c create mode 100644 upb/sink.c diff --git a/Makefile b/Makefile index cf915c1..6b5bff4 100644 --- a/Makefile +++ b/Makefile @@ -152,6 +152,7 @@ upb_SRCS = \ upb/handlers.c \ upb/msg.c \ upb/refcounted.c \ + upb/sink.c \ upb/symtab.c \ upb/table.c \ upb/upb.c \ @@ -487,11 +488,11 @@ upb/bindings/lua/upb_c.so: upb/bindings/lua/upb.c upb/bindings/lua/def.c upb/bin upb/bindings/lua/upb/table_c.so: upb/bindings/lua/upb/table.c lib/libupb_pic.a $(E) CC upb/bindings/lua/upb/table.c - $(Q) $(CC) $(OPT) $(CSTD) $(WARNFLAGS) $(CPPFLAGS) $(CFLAGS) -fpic -shared -o $@ $< $(LUA_LDFLAGS) + $(Q) $(CC) $(OPT) $(CSTD) $(WARNFLAGS) $(CPPFLAGS) $(CFLAGS) -fpic -shared -o $@ $^ $(LUA_LDFLAGS) upb/bindings/lua/upb/pb_c.so: upb/bindings/lua/upb/pb.c $(LUA_LIB_DEPS) $(E) CC upb/bindings/lua/upb/pb.c - $(Q) $(CC) $(OPT) $(CSTD) $(WARNFLAGS) $(CPPFLAGS) $(CFLAGS) -fpic -shared -o $@ $< $(LUA_LDFLAGS) + $(Q) $(CC) $(OPT) $(CSTD) $(WARNFLAGS) $(CPPFLAGS) $(CFLAGS) -fpic -shared -o $@ $^ $(LUA_LDFLAGS) # Python extension ############################################################# diff --git a/tests/bindings/lua/test_upb.pb.lua b/tests/bindings/lua/test_upb.pb.lua index 6f0eda9..106dc55 100644 --- a/tests/bindings/lua/test_upb.pb.lua +++ b/tests/bindings/lua/test_upb.pb.lua @@ -43,7 +43,19 @@ function test_parse_primitive() .. "\128\128\128\128\128\002\032\128\128\128\128\128\128\128\001\041\000" .. "\000\000\000\000\000\248\063\053\000\000\096\064\056\001" local decoder = pb.MakeStringToMessageDecoder(TestMessage) - msg = decoder(binary_pb) + local encoder = pb.MakeMessageToStringEncoder(TestMessage) + collectgarbage() -- ensure encoder/decoder GC-anchor what they need. + local msg = decoder(binary_pb) + assert_equal(536870912, msg.i32) + assert_equal(1073741824, msg.u32) + assert_equal(1125899906842624, msg.i64) + assert_equal(562949953421312, msg.u64) + assert_equal(1.5, msg.dbl) + assert_equal(3.5, msg.flt) + assert_equal(true, msg.bool) + + local encoded = encoder(msg) + local msg2 = decoder(encoded) assert_equal(536870912, msg.i32) assert_equal(1073741824, msg.u32) assert_equal(1125899906842624, msg.i64) diff --git a/upb/bindings/lua/msg.c b/upb/bindings/lua/msg.c index d77c429..64c8e7c 100644 --- a/upb/bindings/lua/msg.c +++ b/upb/bindings/lua/msg.c @@ -293,10 +293,6 @@ struct lupb_msgclass { 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); @@ -307,7 +303,7 @@ static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg, 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) { +const lupb_msgclass *lupb_msgclass_check(lua_State *L, int narg) { return luaL_checkudata(L, narg, LUPB_MSGCLASS); } @@ -316,7 +312,7 @@ const upb_msglayout *lupb_msgclass_getlayout(lua_State *L, int narg) { } const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg) { - lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg); + const lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg); return upb_msgfactory_getmergehandlers( lmsgclass->lfactory->factory, upb_msglayout_msgdef(lmsgclass->layout)); } @@ -430,7 +426,7 @@ static upb_msgval lupb_tomsgval(lua_State *L, upb_fieldtype_t type, int narg, } case UPB_TYPE_MESSAGE: UPB_ASSERT(lmsgclass); - return lupb_msg_typecheck(L, narg, lmsgclass); + return upb_msgval_msg(lupb_msg_checkmsg(L, narg, lmsgclass)); } UPB_UNREACHABLE(); } @@ -486,7 +482,7 @@ static void lupb_pushmsgval(lua_State *L, upb_fieldtype_t type, 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; + const lupb_msgclass *lmsgclass; upb_array *arr; } lupb_array; @@ -550,7 +546,7 @@ static int lupb_array_gc(lua_State *L) { static int lupb_array_new(lua_State *L) { lupb_array *larray; upb_fieldtype_t type; - lupb_msgclass *lmsgclass = NULL; + const lupb_msgclass *lmsgclass = NULL; if (lua_type(L, 1) == LUA_TNUMBER) { type = lupb_checkfieldtype(L, 1); @@ -691,7 +687,7 @@ 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; + const lupb_msgclass *value_lmsgclass = NULL; if (lua_type(L, 2) == LUA_TNUMBER) { value_type = lupb_checkfieldtype(L, 2); @@ -897,11 +893,11 @@ lupb_msg *lupb_msg_check(lua_State *L, int narg) { 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_msg *lupb_msg_checkmsg(lua_State *L, int narg, + const lupb_msgclass *lmsgclass) { + lupb_msg *lmsg = lupb_msg_check(L, narg); + lupb_msgclass_typecheck(L, lmsgclass, lmsg->lmsgclass); + return lmsg->msg; } const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) { @@ -938,7 +934,7 @@ static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg, } int lupb_msg_pushref(lua_State *L, int msgclass, void *msg) { - lupb_msgclass *lmsgclass = lupb_msgclass_check(L, msgclass); + const lupb_msgclass *lmsgclass = lupb_msgclass_check(L, msgclass); lupb_msg *lmsg = lupb_newuserdata(L, sizeof(lupb_msg), LUPB_MSG); lmsg->lmsgclass = lmsgclass; @@ -965,7 +961,7 @@ static int lupb_msg_gc(lua_State *L) { * new_msg = MessageClass() */ static int lupb_msg_pushnew(lua_State *L, int narg) { - lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg); + const 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); diff --git a/upb/bindings/lua/upb.h b/upb/bindings/lua/upb.h index 109113c..a46474b 100644 --- a/upb/bindings/lua/upb.h +++ b/upb/bindings/lua/upb.h @@ -99,7 +99,6 @@ void lupb_checkstatus(lua_State *L, upb_status *s); 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); const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg); const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg); @@ -131,7 +130,10 @@ 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_msg *lupb_msg_checkmsg(lua_State *L, int narg, + const lupb_msgclass *lmsgclass); +const lupb_msgclass *lupb_msgclass_check(lua_State *L, int narg); 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); diff --git a/upb/bindings/lua/upb/msg.c b/upb/bindings/lua/upb/msg.c deleted file mode 100644 index 9aa5c2b..0000000 --- a/upb/bindings/lua/upb/msg.c +++ /dev/null @@ -1,892 +0,0 @@ - -/* 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 d6f13d9..a25d1ac 100644 --- a/upb/bindings/lua/upb/pb.c +++ b/upb/bindings/lua/upb/pb.c @@ -7,6 +7,7 @@ #include "upb/bindings/lua/upb.h" #include "upb/pb/decoder.h" +#include "upb/pb/encoder.h" #define LUPB_PBDECODERMETHOD "lupb.pb.decodermethod" @@ -32,13 +33,11 @@ static int lupb_pb_strtomessage(lua_State *L) { upb_env_reporterrorsto(&env, &status); upb_sink_reset(&sink, handlers, msg); decoder = upb_pbdecoder_create(&env, method, &sink); + upb_bufsrc_putbuf(pb, len, upb_pbdecoder_input(decoder)); - /* TODO: This won't get called in the error case, which longjmp's across us. - * This will cause the memory to leak. To remedy this, we should make the - * upb_env wrapped in a userdata that guarantees this will get called. */ + /* Free resources before we potentially bail on error. */ upb_env_uninit(&env); - lupb_checkstatus(L, &status); /* References the arena at the top of the stack. */ @@ -46,6 +45,39 @@ static int lupb_pb_strtomessage(lua_State *L) { return 1; } +static int lupb_pb_messagetostr(lua_State *L) { + const lupb_msgclass *lmsgclass = lua_touserdata(L, lua_upvalueindex(1)); + const upb_msg *msg = lupb_msg_checkmsg(L, 1, lmsgclass); + const upb_visitorplan *vp = lua_touserdata(L, lua_upvalueindex(2)); + const upb_handlers *encode_handlers = lua_touserdata(L, lua_upvalueindex(3)); + + upb_env env; + upb_bufsink *bufsink; + upb_bytessink *bytessink; + upb_pb_encoder *encoder; + upb_visitor *visitor; + const char *buf; + size_t len; + upb_status status = UPB_STATUS_INIT; + + upb_env_init(&env); + upb_env_reporterrorsto(&env, &status); + bufsink = upb_bufsink_new(&env); + bytessink = upb_bufsink_sink(bufsink); + encoder = upb_pb_encoder_create(&env, encode_handlers, bytessink); + visitor = upb_visitor_create(&env, vp, upb_pb_encoder_input(encoder)); + + upb_visitor_visitmsg(visitor, msg); + buf = upb_bufsink_getdata(bufsink, &len); + lua_pushlstring(L, buf, len); + + /* Free resources before we potentially bail on error. */ + upb_env_uninit(&env); + lupb_checkstatus(L, &status); + + return 1; +} + 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); @@ -70,6 +102,32 @@ static int lupb_pb_makestrtomsgdecoder(lua_State *L) { return 1; /* The decoder closure. */ } +static int lupb_pb_makemsgtostrencoder(lua_State *L) { + const upb_msglayout *layout = lupb_msgclass_getlayout(L, 1); + const lupb_msgclass *lmsgclass = lupb_msgclass_check(L, 1); + const upb_msgdef *md = upb_msglayout_msgdef(layout); + upb_msgfactory *factory = upb_msglayout_factory(layout); + const upb_handlers *encode_handlers; + const upb_visitorplan *vp; + + encode_handlers = upb_pb_encoder_newhandlers(md, &encode_handlers); + vp = upb_msgfactory_getvisitorplan(factory, encode_handlers); + + /* Push upvalues for the closure. */ + lua_pushlightuserdata(L, (void*)lmsgclass); + lua_pushlightuserdata(L, (void*)vp); + lua_pushlightuserdata(L, (void*)encode_handlers); + + /* Upvalues for the closure, only to keep the other upvalues alive. */ + lua_pushvalue(L, 1); + lupb_refcounted_pushnewrapper( + L, upb_handlers_upcast(encode_handlers), LUPB_PBDECODERMETHOD, &encode_handlers); + + lua_pushcclosure(L, &lupb_pb_messagetostr, 5); + + return 1; /* The decoder closure. */ +} + static const struct luaL_Reg decodermethod_mm[] = { {"__gc", lupb_refcounted_gc}, {NULL, NULL} @@ -77,6 +135,7 @@ static const struct luaL_Reg decodermethod_mm[] = { static const struct luaL_Reg toplevel_m[] = { {"MakeStringToMessageDecoder", lupb_pb_makestrtomsgdecoder}, + {"MakeMessageToStringEncoder", lupb_pb_makemsgtostrencoder}, {NULL, NULL} }; diff --git a/upb/msg.c b/upb/msg.c index 1a1066a..7275338 100644 --- a/upb/msg.c +++ b/upb/msg.c @@ -25,6 +25,7 @@ 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); #define CHARPTR_AT(msg, ofs) ((char*)msg + ofs) +#define ENCODE_MAX_NESTING 64 /** upb_msgval ****************************************************************/ @@ -148,6 +149,7 @@ static upb_msgval upb_msgval_fromdefault(const upb_fielddef *f) { /** upb_msglayout *************************************************************/ struct upb_msglayout { + upb_msgfactory *factory; const upb_msgdef *msgdef; size_t size; size_t extdict_offset; @@ -330,6 +332,10 @@ static upb_msglayout *upb_msglayout_new(const upb_msgdef *m) { } } +upb_msgfactory *upb_msglayout_factory(const upb_msglayout *layout) { + return layout->factory; +} + /** upb_msgfactory ************************************************************/ @@ -388,6 +394,7 @@ const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f, upb_msglayout *l = upb_msglayout_new(m); upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l)); UPB_ASSERT(l); + l->factory = f; return l; } } @@ -472,6 +479,150 @@ const upb_handlers *upb_msgfactory_getmergehandlers(upb_msgfactory *f, return ret; } +const upb_visitorplan *upb_msgfactory_getvisitorplan(upb_msgfactory *f, + const upb_handlers *h) { + const upb_msgdef *md = upb_handlers_msgdef(h); + return (const upb_visitorplan*)upb_msgfactory_getlayout(f, md); +} + + +/** upb_visitor ***************************************************************/ + +struct upb_visitor { + const upb_msglayout *layout; + upb_sink *sink; +}; + +static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { + upb_selector_t ret; + bool ok = upb_handlers_getselector(f, type, &ret); + UPB_ASSERT(ok); + return ret; +} + +static bool upb_visitor_hasfield(const upb_msg *msg, const upb_fielddef *f, + const upb_msglayout *layout) { + if (upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f)) { + return upb_msgval_getptr(upb_msg_get(msg, f, layout)) != NULL; + } else if (upb_msgdef_syntax(upb_fielddef_containingtype(f)) == + UPB_SYNTAX_PROTO2) { + return upb_msg_has(msg, f, layout); + } else { + upb_msgval val = upb_msg_get(msg, f, layout); + switch (upb_fielddef_type(f)) { + case UPB_TYPE_FLOAT: + return upb_msgval_getfloat(val) != 0; + case UPB_TYPE_DOUBLE: + return upb_msgval_getdouble(val) != 0; + case UPB_TYPE_BOOL: + return upb_msgval_getbool(val); + case UPB_TYPE_ENUM: + case UPB_TYPE_INT32: + return upb_msgval_getint32(val) != 0; + case UPB_TYPE_UINT32: + return upb_msgval_getuint32(val) != 0; + case UPB_TYPE_INT64: + return upb_msgval_getint64(val) != 0; + case UPB_TYPE_UINT64: + return upb_msgval_getuint64(val) != 0; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + return upb_msgval_getstr(val) && upb_msgval_getstrlen(val) > 0; + case UPB_TYPE_MESSAGE: + return upb_msgval_getmsg(val) != NULL; + } + UPB_UNREACHABLE(); + } +} + +static void upb_visitor_visitmsg2(const upb_msg *msg, + const upb_msglayout *layout, upb_sink *sink, + int depth) { + const upb_msgdef *md = upb_msglayout_msgdef(layout); + upb_msg_field_iter i; + upb_status status; + + upb_sink_startmsg(sink); + + /* Protect against cycles (possible because users may freely reassign message + * and repeated fields) by imposing a maximum recursion depth. */ + if (depth > ENCODE_MAX_NESTING) { + return; + } + + for (upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + upb_msgval val; + + if (!upb_visitor_hasfield(msg, f, layout)) { + continue; + } + + val = upb_msg_get(msg, f, layout); + + if (upb_fielddef_isseq(f)) { + const upb_array *arr = upb_msgval_getarr(val); + UPB_ASSERT(arr); + /* TODO: putary(ary, f, sink, depth);*/ + } else if (upb_fielddef_issubmsg(f)) { + const upb_map *map = upb_msgval_getmap(val); + UPB_ASSERT(map); + /* TODO: putmap(map, f, sink, depth);*/ + } else if (upb_fielddef_isstring(f)) { + /* TODO putstr(); */ + } else { + upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); + UPB_ASSERT(upb_fielddef_isprimitive(f)); + + switch (upb_fielddef_type(f)) { + case UPB_TYPE_FLOAT: + upb_sink_putfloat(sink, sel, upb_msgval_getfloat(val)); + break; + case UPB_TYPE_DOUBLE: + upb_sink_putdouble(sink, sel, upb_msgval_getdouble(val)); + break; + case UPB_TYPE_BOOL: + upb_sink_putbool(sink, sel, upb_msgval_getbool(val)); + break; + case UPB_TYPE_ENUM: + case UPB_TYPE_INT32: + upb_sink_putint32(sink, sel, upb_msgval_getint32(val)); + break; + case UPB_TYPE_UINT32: + upb_sink_putuint32(sink, sel, upb_msgval_getuint32(val)); + break; + case UPB_TYPE_INT64: + upb_sink_putint64(sink, sel, upb_msgval_getint64(val)); + break; + case UPB_TYPE_UINT64: + upb_sink_putuint64(sink, sel, upb_msgval_getuint64(val)); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + case UPB_TYPE_MESSAGE: + UPB_UNREACHABLE(); + } + } + } + + upb_sink_endmsg(sink, &status); +} + +upb_visitor *upb_visitor_create(upb_env *e, const upb_visitorplan *vp, + upb_sink *output) { + upb_visitor *visitor = upb_env_malloc(e, sizeof(*visitor)); + visitor->layout = (const upb_msglayout*)vp; + visitor->sink = output; + return visitor; +} + +bool upb_visitor_visitmsg(upb_visitor *visitor, const upb_msg *msg) { + upb_visitor_visitmsg2(msg, visitor->layout, visitor->sink, 0); + return true; +} + /** upb_msg *******************************************************************/ diff --git a/upb/msg.h b/upb/msg.h index 11f0d5c..141095c 100644 --- a/upb/msg.h +++ b/upb/msg.h @@ -57,6 +57,8 @@ UPB_DECLARE_TYPE(upb::VisitorPlan, upb_visitorplan) UPB_BEGIN_EXTERN_C +typedef void upb_msg; + /** upb_msglayout *************************************************************/ @@ -64,6 +66,9 @@ UPB_BEGIN_EXTERN_C * instances of this from a upb_msgfactory, and the factory always owns the * msglayout. */ +/* Gets the factory for this layout */ +upb_msgfactory *upb_msglayout_factory(const upb_msglayout *l); + /* Get the msglayout for a submessage. This requires that this field is a * submessage, ie. upb_fielddef_issubmsg(upb_msglayout_msgdef(l)) == true. * @@ -84,7 +89,7 @@ const upb_msgdef *upb_msglayout_msgdef(const upb_msglayout *l); 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); +bool upb_visitor_visitmsg(upb_visitor *v, const upb_msg *msg); /** upb_msgfactory ************************************************************/ @@ -117,8 +122,8 @@ const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f, const upb_msgdef *m); const upb_handlers *upb_msgfactory_getmergehandlers(upb_msgfactory *f, const upb_msgdef *m); -const upb_visitorplan *upb_msgfactory_getvisitorplan(const upb_msgfactory *f, - const upb_msgdef *m); +const upb_visitorplan *upb_msgfactory_getvisitorplan(upb_msgfactory *f, + const upb_handlers *h); /** upb_msgval ****************************************************************/ @@ -126,8 +131,6 @@ const upb_visitorplan *upb_msgfactory_getvisitorplan(const upb_msgfactory *f, /* A union representing all possible protobuf values. Used for generic get/set * operations. */ -typedef void upb_msg; - typedef union { bool b; float flt; @@ -180,6 +183,14 @@ UPB_INLINE upb_msgval upb_msgval_str(const char *ptr, size_t len) { return ret; } +UPB_INLINE const char* upb_msgval_getstr(upb_msgval val) { + return val.str.ptr; +} + +UPB_INLINE size_t upb_msgval_getstrlen(upb_msgval val) { + return val.str.len; +} + /** upb_msg *******************************************************************/ diff --git a/upb/sink.c b/upb/sink.c new file mode 100644 index 0000000..ed0972b --- /dev/null +++ b/upb/sink.c @@ -0,0 +1,88 @@ + +#include "upb/sink.h" + +bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink *sink) { + void *subc; + bool ret; + upb_bufhandle handle; + upb_bufhandle_init(&handle); + upb_bufhandle_setbuf(&handle, buf, 0); + ret = upb_bytessink_start(sink, len, &subc); + if (ret && len != 0) { + ret = (upb_bytessink_putbuf(sink, subc, buf, len, &handle) >= len); + } + if (ret) { + ret = upb_bytessink_end(sink); + } + upb_bufhandle_uninit(&handle); + return ret; +} + +struct upb_bufsink { + upb_byteshandler handler; + upb_bytessink sink; + upb_env *env; + char *ptr; + size_t len, size; +}; + +static void *upb_bufsink_start(void *_sink, const void *hd, size_t size_hint) { + upb_bufsink *sink = _sink; + UPB_UNUSED(hd); + UPB_UNUSED(size_hint); + sink->len = 0; + return sink; +} + +static size_t upb_bufsink_string(void *_sink, const void *hd, const char *ptr, + size_t len, const upb_bufhandle *handle) { + upb_bufsink *sink = _sink; + size_t new_size = sink->size; + + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + while (sink->len + len > new_size) { + new_size *= 2; + } + + if (new_size != sink->size) { + sink->ptr = upb_env_realloc(sink->env, sink->ptr, sink->size, new_size); + sink->size = new_size; + } + + memcpy(sink->ptr + sink->len, ptr, len); + sink->len += len; + + return len; +} + +upb_bufsink *upb_bufsink_new(upb_env *env) { + upb_bufsink *sink = upb_env_malloc(env, sizeof(upb_bufsink)); + upb_byteshandler_init(&sink->handler); + upb_byteshandler_setstartstr(&sink->handler, upb_bufsink_start, NULL); + upb_byteshandler_setstring(&sink->handler, upb_bufsink_string, NULL); + + upb_bytessink_reset(&sink->sink, &sink->handler, sink); + + sink->env = env; + sink->size = 32; + sink->ptr = upb_env_malloc(env, sink->size); + sink->len = 0; + + return sink; +} + +void upb_bufsink_free(upb_bufsink *sink) { + upb_env_free(sink->env, sink->ptr); + upb_env_free(sink->env, sink); +} + +upb_bytessink *upb_bufsink_sink(upb_bufsink *sink) { + return &sink->sink; +} + +const char *upb_bufsink_getdata(const upb_bufsink *sink, size_t *len) { + *len = sink->len; + return sink->ptr; +} diff --git a/upb/sink.h b/upb/sink.h index 765916e..d44cb60 100644 --- a/upb/sink.h +++ b/upb/sink.h @@ -28,6 +28,7 @@ class Sink; } #endif +UPB_DECLARE_TYPE(upb::BufferSink, upb_bufsink) UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc) UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink) UPB_DECLARE_TYPE(upb::Sink, upb_sink) @@ -212,7 +213,12 @@ struct upb_bufsrc { #endif }; -UPB_BEGIN_EXTERN_C +/* A class for accumulating output string data in a flat buffer. */ + +upb_bufsink *upb_bufsink_new(upb_env *env); +void upb_bufsink_free(upb_bufsink *sink); +upb_bytessink *upb_bufsink_sink(upb_bufsink *sink); +const char *upb_bufsink_getdata(const upb_bufsink *sink, size_t *len); /* Inline definitions. */ @@ -263,23 +269,7 @@ UPB_INLINE bool upb_bytessink_end(upb_bytessink *s) { &s->handler->table[UPB_ENDSTR_SELECTOR].attr)); } -UPB_INLINE bool upb_bufsrc_putbuf(const char *buf, size_t len, - upb_bytessink *sink) { - void *subc; - bool ret; - upb_bufhandle handle; - upb_bufhandle_init(&handle); - upb_bufhandle_setbuf(&handle, buf, 0); - ret = upb_bytessink_start(sink, len, &subc); - if (ret && len != 0) { - ret = (upb_bytessink_putbuf(sink, subc, buf, len, &handle) >= len); - } - if (ret) { - ret = upb_bytessink_end(sink); - } - upb_bufhandle_uninit(&handle); - return ret; -} +bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink *sink); #define PUTVAL(type, ctype) \ UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \ -- cgit v1.2.3 From ce1f63fde226f367c1ce2172a416d9b9f57c297e Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Mon, 12 Dec 2016 18:57:13 +0000 Subject: A few C++ fixes for BufferSink. --- upb/sink.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/upb/sink.h b/upb/sink.h index d44cb60..a50bafb 100644 --- a/upb/sink.h +++ b/upb/sink.h @@ -22,6 +22,7 @@ #ifdef __cplusplus namespace upb { +class BufferSink; class BufferSource; class BytesSink; class Sink; @@ -213,6 +214,8 @@ struct upb_bufsrc { #endif }; +UPB_BEGIN_EXTERN_C + /* A class for accumulating output string data in a flat buffer. */ upb_bufsink *upb_bufsink_new(upb_env *env); -- cgit v1.2.3 From 6cccfe16493c9c88ea198ca37af915179ccf7eaf Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 14 Dec 2016 16:39:04 +0000 Subject: Addressed PR comments. --- upb/msg.c | 30 +++++++++++++++++------------- upb/sink.c | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/upb/msg.c b/upb/msg.c index 7275338..517b814 100644 --- a/upb/msg.c +++ b/upb/msg.c @@ -26,6 +26,7 @@ void *upb_map_pack(const upb_map *map, void *p, size_t *ofs, size_t size); #define CHARPTR_AT(msg, ofs) ((char*)msg + ofs) #define ENCODE_MAX_NESTING 64 +#define CHECK_RETURN(x) if (!(x)) { return false; } /** upb_msgval ****************************************************************/ @@ -502,8 +503,8 @@ static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { static bool upb_visitor_hasfield(const upb_msg *msg, const upb_fielddef *f, const upb_msglayout *layout) { - if (upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f)) { - return upb_msgval_getptr(upb_msg_get(msg, f, layout)) != NULL; + if (upb_fielddef_isseq(f)) { + return upb_msgval_getarr(upb_msg_get(msg, f, layout)) != NULL; } else if (upb_msgdef_syntax(upb_fielddef_containingtype(f)) == UPB_SYNTAX_PROTO2) { return upb_msg_has(msg, f, layout); @@ -535,7 +536,7 @@ static bool upb_visitor_hasfield(const upb_msg *msg, const upb_fielddef *f, } } -static void upb_visitor_visitmsg2(const upb_msg *msg, +static bool upb_visitor_visitmsg2(const upb_msg *msg, const upb_msglayout *layout, upb_sink *sink, int depth) { const upb_msgdef *md = upb_msglayout_msgdef(layout); @@ -547,7 +548,7 @@ static void upb_visitor_visitmsg2(const upb_msg *msg, /* Protect against cycles (possible because users may freely reassign message * and repeated fields) by imposing a maximum recursion depth. */ if (depth > ENCODE_MAX_NESTING) { - return; + return false; } for (upb_msg_field_begin(&i, md); @@ -578,26 +579,29 @@ static void upb_visitor_visitmsg2(const upb_msg *msg, switch (upb_fielddef_type(f)) { case UPB_TYPE_FLOAT: - upb_sink_putfloat(sink, sel, upb_msgval_getfloat(val)); + CHECK_RETURN(upb_sink_putfloat(sink, sel, upb_msgval_getfloat(val))); break; case UPB_TYPE_DOUBLE: - upb_sink_putdouble(sink, sel, upb_msgval_getdouble(val)); + CHECK_RETURN( + upb_sink_putdouble(sink, sel, upb_msgval_getdouble(val))); break; case UPB_TYPE_BOOL: - upb_sink_putbool(sink, sel, upb_msgval_getbool(val)); + CHECK_RETURN(upb_sink_putbool(sink, sel, upb_msgval_getbool(val))); break; case UPB_TYPE_ENUM: case UPB_TYPE_INT32: - upb_sink_putint32(sink, sel, upb_msgval_getint32(val)); + CHECK_RETURN(upb_sink_putint32(sink, sel, upb_msgval_getint32(val))); break; case UPB_TYPE_UINT32: - upb_sink_putuint32(sink, sel, upb_msgval_getuint32(val)); + CHECK_RETURN( + upb_sink_putuint32(sink, sel, upb_msgval_getuint32(val))); break; case UPB_TYPE_INT64: - upb_sink_putint64(sink, sel, upb_msgval_getint64(val)); + CHECK_RETURN(upb_sink_putint64(sink, sel, upb_msgval_getint64(val))); break; case UPB_TYPE_UINT64: - upb_sink_putuint64(sink, sel, upb_msgval_getuint64(val)); + CHECK_RETURN( + upb_sink_putuint64(sink, sel, upb_msgval_getuint64(val))); break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: @@ -608,6 +612,7 @@ static void upb_visitor_visitmsg2(const upb_msg *msg, } upb_sink_endmsg(sink, &status); + return true; } upb_visitor *upb_visitor_create(upb_env *e, const upb_visitorplan *vp, @@ -619,8 +624,7 @@ upb_visitor *upb_visitor_create(upb_env *e, const upb_visitorplan *vp, } bool upb_visitor_visitmsg(upb_visitor *visitor, const upb_msg *msg) { - upb_visitor_visitmsg2(msg, visitor->layout, visitor->sink, 0); - return true; + return upb_visitor_visitmsg2(msg, visitor->layout, visitor->sink, 0); } diff --git a/upb/sink.c b/upb/sink.c index ed0972b..e6ede49 100644 --- a/upb/sink.c +++ b/upb/sink.c @@ -39,6 +39,7 @@ static size_t upb_bufsink_string(void *_sink, const void *hd, const char *ptr, upb_bufsink *sink = _sink; size_t new_size = sink->size; + UPB_ASSERT(new_size > 0); UPB_UNUSED(hd); UPB_UNUSED(handle); -- cgit v1.2.3