Basic serialization for upb_msg and Lua.

Doesn't yet include strings, submessages, maps,
or repeated fields.
pull/13171/head
Josh Haberman 8 years ago
parent 076a82ee7e
commit 15c388b819
  1. 5
      Makefile
  2. 14
      tests/bindings/lua/test_upb.pb.lua
  3. 28
      upb/bindings/lua/msg.c
  4. 4
      upb/bindings/lua/upb.h
  5. 892
      upb/bindings/lua/upb/msg.c
  6. 67
      upb/bindings/lua/upb/pb.c
  7. 151
      upb/msg.c
  8. 21
      upb/msg.h
  9. 88
      upb/sink.c
  10. 26
      upb/sink.h

@ -152,6 +152,7 @@ upb_SRCS = \
upb/handlers.c \ upb/handlers.c \
upb/msg.c \ upb/msg.c \
upb/refcounted.c \ upb/refcounted.c \
upb/sink.c \
upb/symtab.c \ upb/symtab.c \
upb/table.c \ upb/table.c \
upb/upb.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 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 $(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) upb/bindings/lua/upb/pb_c.so: upb/bindings/lua/upb/pb.c $(LUA_LIB_DEPS)
$(E) CC upb/bindings/lua/upb/pb.c $(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 ############################################################# # Python extension #############################################################

@ -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" .. "\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" .. "\000\000\000\000\000\248\063\053\000\000\096\064\056\001"
local decoder = pb.MakeStringToMessageDecoder(TestMessage) 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(536870912, msg.i32)
assert_equal(1073741824, msg.u32) assert_equal(1073741824, msg.u32)
assert_equal(1125899906842624, msg.i64) assert_equal(1125899906842624, msg.i64)

@ -293,10 +293,6 @@ struct lupb_msgclass {
const lupb_msgfactory *lfactory; 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. */ /* Type-checks for assigning to a message field. */
static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg, static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
const upb_fielddef *f); 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, static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
const upb_msgdef *md); 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); 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) { 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( return upb_msgfactory_getmergehandlers(
lmsgclass->lfactory->factory, upb_msglayout_msgdef(lmsgclass->layout)); 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: case UPB_TYPE_MESSAGE:
UPB_ASSERT(lmsgclass); UPB_ASSERT(lmsgclass);
return lupb_msg_typecheck(L, narg, lmsgclass); return upb_msgval_msg(lupb_msg_checkmsg(L, narg, lmsgclass));
} }
UPB_UNREACHABLE(); UPB_UNREACHABLE();
} }
@ -486,7 +482,7 @@ static void lupb_pushmsgval(lua_State *L, upb_fieldtype_t type,
typedef struct { typedef struct {
/* Only needed for array of message. This wastes space in the non-message /* Only needed for array of message. This wastes space in the non-message
* case but simplifies the code. Could optimize away if desired. */ * case but simplifies the code. Could optimize away if desired. */
lupb_msgclass *lmsgclass; const lupb_msgclass *lmsgclass;
upb_array *arr; upb_array *arr;
} lupb_array; } lupb_array;
@ -550,7 +546,7 @@ static int lupb_array_gc(lua_State *L) {
static int lupb_array_new(lua_State *L) { static int lupb_array_new(lua_State *L) {
lupb_array *larray; lupb_array *larray;
upb_fieldtype_t type; upb_fieldtype_t type;
lupb_msgclass *lmsgclass = NULL; const lupb_msgclass *lmsgclass = NULL;
if (lua_type(L, 1) == LUA_TNUMBER) { if (lua_type(L, 1) == LUA_TNUMBER) {
type = lupb_checkfieldtype(L, 1); type = lupb_checkfieldtype(L, 1);
@ -691,7 +687,7 @@ static int lupb_map_new(lua_State *L) {
lupb_map *lmap; lupb_map *lmap;
upb_fieldtype_t key_type = lupb_checkfieldtype(L, 1); upb_fieldtype_t key_type = lupb_checkfieldtype(L, 1);
upb_fieldtype_t value_type; upb_fieldtype_t value_type;
lupb_msgclass *value_lmsgclass = NULL; const lupb_msgclass *value_lmsgclass = NULL;
if (lua_type(L, 2) == LUA_TNUMBER) { if (lua_type(L, 2) == LUA_TNUMBER) {
value_type = lupb_checkfieldtype(L, 2); value_type = lupb_checkfieldtype(L, 2);
@ -897,11 +893,11 @@ lupb_msg *lupb_msg_check(lua_State *L, int narg) {
return msg; return msg;
} }
static upb_msgval lupb_msg_typecheck(lua_State *L, int narg, const upb_msg *lupb_msg_checkmsg(lua_State *L, int narg,
const lupb_msgclass *lmsgclass) { const lupb_msgclass *lmsgclass) {
lupb_msg *msg = lupb_msg_check(L, narg); lupb_msg *lmsg = lupb_msg_check(L, narg);
lupb_msgclass_typecheck(L, lmsgclass, msg->lmsgclass); lupb_msgclass_typecheck(L, lmsgclass, lmsg->lmsgclass);
return upb_msgval_msg(msg); return lmsg->msg;
} }
const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) { 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) { 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); lupb_msg *lmsg = lupb_newuserdata(L, sizeof(lupb_msg), LUPB_MSG);
lmsg->lmsgclass = lmsgclass; lmsg->lmsgclass = lmsgclass;
@ -965,7 +961,7 @@ static int lupb_msg_gc(lua_State *L) {
* new_msg = MessageClass() * new_msg = MessageClass()
*/ */
static int lupb_msg_pushnew(lua_State *L, int narg) { 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); size_t size = sizeof(lupb_msg) + upb_msg_sizeof(lmsgclass->layout);
lupb_msg *lmsg = lupb_newuserdata(L, size, LUPB_MSG); lupb_msg *lmsg = lupb_newuserdata(L, size, LUPB_MSG);

@ -99,7 +99,6 @@ void lupb_checkstatus(lua_State *L, upb_status *s);
upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg); upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg);
void *lupb_refcounted_check(lua_State *L, int narg, const char *type); 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_msgdef *lupb_msgdef_check(lua_State *L, int narg);
const upb_enumdef *lupb_enumdef_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); 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); upb_arena *lupb_arena_check(lua_State *L, int narg);
int lupb_arena_new(lua_State *L); int lupb_arena_new(lua_State *L);
int lupb_msg_pushref(lua_State *L, int msgclass, void *msg); 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_msglayout *lupb_msgclass_getlayout(lua_State *L, int narg);
const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg); const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg);
void lupb_msg_registertypes(lua_State *L); void lupb_msg_registertypes(lua_State *L);

@ -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}
};

@ -7,6 +7,7 @@
#include "upb/bindings/lua/upb.h" #include "upb/bindings/lua/upb.h"
#include "upb/pb/decoder.h" #include "upb/pb/decoder.h"
#include "upb/pb/encoder.h"
#define LUPB_PBDECODERMETHOD "lupb.pb.decodermethod" #define LUPB_PBDECODERMETHOD "lupb.pb.decodermethod"
@ -32,13 +33,11 @@ static int lupb_pb_strtomessage(lua_State *L) {
upb_env_reporterrorsto(&env, &status); upb_env_reporterrorsto(&env, &status);
upb_sink_reset(&sink, handlers, msg); upb_sink_reset(&sink, handlers, msg);
decoder = upb_pbdecoder_create(&env, method, &sink); decoder = upb_pbdecoder_create(&env, method, &sink);
upb_bufsrc_putbuf(pb, len, upb_pbdecoder_input(decoder)); upb_bufsrc_putbuf(pb, len, upb_pbdecoder_input(decoder));
/* TODO: This won't get called in the error case, which longjmp's across us. /* Free resources before we potentially bail on error. */
* 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. */
upb_env_uninit(&env); upb_env_uninit(&env);
lupb_checkstatus(L, &status); lupb_checkstatus(L, &status);
/* References the arena at the top of the stack. */ /* References the arena at the top of the stack. */
@ -46,6 +45,39 @@ static int lupb_pb_strtomessage(lua_State *L) {
return 1; 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) { static int lupb_pb_makestrtomsgdecoder(lua_State *L) {
const upb_msglayout *layout = lupb_msgclass_getlayout(L, 1); const upb_msglayout *layout = lupb_msgclass_getlayout(L, 1);
const upb_handlers *handlers = lupb_msgclass_getmergehandlers(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. */ 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[] = { static const struct luaL_Reg decodermethod_mm[] = {
{"__gc", lupb_refcounted_gc}, {"__gc", lupb_refcounted_gc},
{NULL, NULL} {NULL, NULL}
@ -77,6 +135,7 @@ static const struct luaL_Reg decodermethod_mm[] = {
static const struct luaL_Reg toplevel_m[] = { static const struct luaL_Reg toplevel_m[] = {
{"MakeStringToMessageDecoder", lupb_pb_makestrtomsgdecoder}, {"MakeStringToMessageDecoder", lupb_pb_makestrtomsgdecoder},
{"MakeMessageToStringEncoder", lupb_pb_makemsgtostrencoder},
{NULL, NULL} {NULL, NULL}
}; };

@ -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); 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 CHARPTR_AT(msg, ofs) ((char*)msg + ofs)
#define ENCODE_MAX_NESTING 64
/** upb_msgval ****************************************************************/ /** upb_msgval ****************************************************************/
@ -148,6 +149,7 @@ static upb_msgval upb_msgval_fromdefault(const upb_fielddef *f) {
/** upb_msglayout *************************************************************/ /** upb_msglayout *************************************************************/
struct upb_msglayout { struct upb_msglayout {
upb_msgfactory *factory;
const upb_msgdef *msgdef; const upb_msgdef *msgdef;
size_t size; size_t size;
size_t extdict_offset; 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 ************************************************************/ /** upb_msgfactory ************************************************************/
@ -388,6 +394,7 @@ const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f,
upb_msglayout *l = upb_msglayout_new(m); upb_msglayout *l = upb_msglayout_new(m);
upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l)); upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l));
UPB_ASSERT(l); UPB_ASSERT(l);
l->factory = f;
return l; return l;
} }
} }
@ -472,6 +479,150 @@ const upb_handlers *upb_msgfactory_getmergehandlers(upb_msgfactory *f,
return ret; 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 *******************************************************************/ /** upb_msg *******************************************************************/

@ -57,6 +57,8 @@ UPB_DECLARE_TYPE(upb::VisitorPlan, upb_visitorplan)
UPB_BEGIN_EXTERN_C UPB_BEGIN_EXTERN_C
typedef void upb_msg;
/** upb_msglayout *************************************************************/ /** upb_msglayout *************************************************************/
@ -64,6 +66,9 @@ UPB_BEGIN_EXTERN_C
* instances of this from a upb_msgfactory, and the factory always owns the * instances of this from a upb_msgfactory, and the factory always owns the
* msglayout. */ * 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 /* Get the msglayout for a submessage. This requires that this field is a
* submessage, ie. upb_fielddef_issubmsg(upb_msglayout_msgdef(l)) == true. * 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_visitor *upb_visitor_create(upb_env *e, const upb_visitorplan *vp,
upb_sink *output); 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 ************************************************************/ /** upb_msgfactory ************************************************************/
@ -117,8 +122,8 @@ const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f,
const upb_msgdef *m); const upb_msgdef *m);
const upb_handlers *upb_msgfactory_getmergehandlers(upb_msgfactory *f, const upb_handlers *upb_msgfactory_getmergehandlers(upb_msgfactory *f,
const upb_msgdef *m); const upb_msgdef *m);
const upb_visitorplan *upb_msgfactory_getvisitorplan(const upb_msgfactory *f, const upb_visitorplan *upb_msgfactory_getvisitorplan(upb_msgfactory *f,
const upb_msgdef *m); const upb_handlers *h);
/** upb_msgval ****************************************************************/ /** 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 /* A union representing all possible protobuf values. Used for generic get/set
* operations. */ * operations. */
typedef void upb_msg;
typedef union { typedef union {
bool b; bool b;
float flt; float flt;
@ -180,6 +183,14 @@ UPB_INLINE upb_msgval upb_msgval_str(const char *ptr, size_t len) {
return ret; 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 *******************************************************************/ /** upb_msg *******************************************************************/

@ -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;
}

@ -28,6 +28,7 @@ class Sink;
} }
#endif #endif
UPB_DECLARE_TYPE(upb::BufferSink, upb_bufsink)
UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc) UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc)
UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink) UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink)
UPB_DECLARE_TYPE(upb::Sink, upb_sink) UPB_DECLARE_TYPE(upb::Sink, upb_sink)
@ -212,7 +213,12 @@ struct upb_bufsrc {
#endif #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. */ /* Inline definitions. */
@ -263,23 +269,7 @@ UPB_INLINE bool upb_bytessink_end(upb_bytessink *s) {
&s->handler->table[UPB_ENDSTR_SELECTOR].attr)); &s->handler->table[UPB_ENDSTR_SELECTOR].attr));
} }
UPB_INLINE bool upb_bufsrc_putbuf(const char *buf, size_t len, bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink *sink);
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;
}
#define PUTVAL(type, ctype) \ #define PUTVAL(type, ctype) \
UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \ UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \

Loading…
Cancel
Save