Protocol Buffers - Google's data interchange format (grpc依赖) https://developers.google.com/protocol-buffers/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

956 lines
26 KiB

/*
** lupb_msg -- Message/Array/Map objects in Lua/C that wrap upb/msg.h
*/
#include "upb/msg.h"
#include <float.h>
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "lauxlib.h"
#include "upb/bindings/lua/upb.h"
#include "upb/reflection.h"
#include "upb/text_encode.h"
#include "upb/port_def.inc"
/*
* Message/Map/Array objects. These objects form a directed graph: a message
* can contain submessages, arrays, and maps, which can then point to other
* messages. This graph can technically be cyclic, though this is an error and
* a cyclic graph cannot be serialized. So it's better to think of this as a
* tree of objects.
*
* The actual data exists at the upb level (upb_msg, upb_map, upb_array),
* independently of Lua. The upb objects contain all the canonical data and
* edges between objects. Lua wrapper objects expose the upb objects to Lua,
* but ultimately they are just wrappers. They pass through all reads and
* writes to the underlying upb objects.
*
* Each upb object lives in a upb arena. We have a Lua object to wrap the upb
* arena, but arenas are never exposed to the user. The Lua arena object just
* serves to own the upb arena and free it at the proper time, once the Lua GC
* has determined that there are no more references to anything that lives in
* that arena. All wrapper objects strongly reference the arena to which they
* belong.
*
* A global object cache stores a mapping of C pointer (upb_msg*, upb_array*,
* upb_map*) to a corresponding Lua wrapper. These references are weak so that
* the wrappers can be collected if they are no longer needed. A new wrapper
* object can always be recreated later.
*
* +-----+
* lupb_arena |cache|-weak-+
* | ^ +-----+ |
* | | V
* Lua level | +------------lupb_msg
* ----------------|-----------------|------------------------------------------
* upb level | |
* | +----V------------------------------+
* +->upb_arena | upb_msg ...(empty arena storage) |
* +-----------------------------------+
*
* If the user creates a reference between two objects that have different
* arenas, we need to fuse the two arenas together, so that the blocks will
* outlive both arenas.
*
* +-------------------------->(fused)<----------------+
* | |
* V +-----+ V
* lupb_arena +-weak-|cache|-weak-+ lupb_arena
* | ^ | +-----+ | ^ |
* | | V V | |
* Lua level | +------------lupb_msg lupb_msg----+ |
* ----------------|-----------------|-------------------------|---------|------
* upb level | | | |
* | +----V----+ +----V----+ V
* +->upb_arena | upb_msg | | upb_msg | upb_arena
* +------|--+ +--^------+
* +---------------------+
* Key invariants:
* 1. every wrapper references the arena that contains it.
* 2. every fused arena includes all arenas that own upb objects reachable
* from that arena. In other words, when a wrapper references an arena,
* this is sufficient to ensure that any upb object reachable from that
* wrapper will stay alive.
*
* Additionally, every message object contains a strong reference to the
* corresponding Descriptor object. Likewise, array/map objects reference a
* Descriptor object if they are typed to store message values.
*/
#define LUPB_ARENA "lupb.arena"
#define LUPB_ARRAY "lupb.array"
#define LUPB_MAP "lupb.map"
#define LUPB_MSG "lupb.msg"
#define LUPB_ARENA_INDEX 1
#define LUPB_MSGDEF_INDEX 2 /* For msg, and map/array that store msg */
static void lupb_msg_newmsgwrapper(lua_State *L, int narg, upb_msgval val);
static upb_msg *lupb_msg_check(lua_State *L, int narg);
static upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg) {
uint32_t n = lupb_checkuint32(L, narg);
bool ok = n >= UPB_TYPE_BOOL && n <= UPB_TYPE_BYTES;
luaL_argcheck(L, ok, narg, "invalid field type");
return n;
}
char cache_key;
/* lupb_cacheinit()
*
* Creates the global cache used by lupb_cacheget() and lupb_cacheset().
*/
static void lupb_cacheinit(lua_State *L) {
/* Create our object cache. */
lua_newtable(L);
/* Cache metatable gives the cache weak values */
lua_createtable(L, 0, 1);
lua_pushstring(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
/* Set cache in the registry. */
lua_rawsetp(L, LUA_REGISTRYINDEX, &cache_key);
}
/* lupb_cacheget()
*
* Pushes cache[key] and returns true if this key is present in the cache.
* Otherwise returns false and leaves nothing on the stack.
*/
static bool lupb_cacheget(lua_State *L, const void *key) {
if (key == NULL) {
lua_pushnil(L);
return true;
}
lua_rawgetp(L, LUA_REGISTRYINDEX, &cache_key);
lua_rawgetp(L, -1, key);
if (lua_isnil(L, -1)) {
lua_pop(L, 2); /* Pop table, nil. */
return false;
} else {
lua_replace(L, -2); /* Replace cache table. */
return true;
}
}
/* lupb_cacheset()
*
* Sets cache[key] = val, where "val" is the value at the top of the stack.
* Does not pop the value.
*/
static void lupb_cacheset(lua_State *L, const void *key) {
lua_rawgetp(L, LUA_REGISTRYINDEX, &cache_key);
lua_pushvalue(L, -2);
lua_rawsetp(L, -2, key);
lua_pop(L, 1); /* Pop table. */
}
/* lupb_arena *****************************************************************/
/* lupb_arena only exists to wrap a upb_arena. It is never exposed to users; it
* is an internal memory management detail. Other wrapper objects refer to this
* object from their userdata to keep the arena-owned data alive.
*/
typedef struct {
upb_arena *arena;
} lupb_arena;
static upb_arena *lupb_arena_check(lua_State *L, int narg) {
lupb_arena *a = luaL_checkudata(L, narg, LUPB_ARENA);
return a->arena;
}
upb_arena *lupb_arena_pushnew(lua_State *L) {
lupb_arena *a = lupb_newuserdata(L, sizeof(lupb_arena), 1, LUPB_ARENA);
a->arena = upb_arena_new();
return a->arena;
}
/**
* lupb_arena_fuse()
*
* Merges |from| into |to| so that there is a single arena group that contains
* both, and both arenas will point at this new table. */
static void lupb_arena_fuse(lua_State *L, int to, int from) {
upb_arena *to_arena = lupb_arena_check(L, to);
upb_arena *from_arena = lupb_arena_check(L, from);
upb_arena_fuse(to_arena, from_arena);
}
static int lupb_arena_gc(lua_State *L) {
upb_arena *a = lupb_arena_check(L, 1);
upb_arena_free(a);
return 0;
}
static const struct luaL_Reg lupb_arena_mm[] = {
{"__gc", lupb_arena_gc},
{NULL, NULL}
};
/* lupb_arenaget()
*
* Returns the arena from the given message, array, or map object.
*/
static upb_arena *lupb_arenaget(lua_State *L, int narg) {
upb_arena *arena;
lua_getiuservalue(L, narg, LUPB_ARENA_INDEX);
arena = lupb_arena_check(L, -1);
lua_pop(L, 1);
return arena;
}
/* upb <-> Lua type conversion ************************************************/
/* Whether string data should be copied into the containing arena. We can
* avoid a copy if the string data is only needed temporarily (like for a map
* lookup).
*/
typedef enum {
LUPB_COPY, /* Copy string data into the arena. */
LUPB_REF /* Reference the Lua copy of the string data. */
} lupb_copy_t;
/**
* lupb_tomsgval()
*
* Converts the given Lua value |narg| to a upb_msgval.
*/
static upb_msgval lupb_tomsgval(lua_State *L, upb_fieldtype_t type, int narg,
int container, lupb_copy_t copy) {
upb_msgval ret;
switch (type) {
case UPB_TYPE_INT32:
case UPB_TYPE_ENUM:
ret.int32_val = lupb_checkint32(L, narg);
break;
case UPB_TYPE_INT64:
ret.int64_val = lupb_checkint64(L, narg);
break;
case UPB_TYPE_UINT32:
ret.uint32_val = lupb_checkuint32(L, narg);
break;
case UPB_TYPE_UINT64:
ret.uint64_val = lupb_checkuint64(L, narg);
break;
case UPB_TYPE_DOUBLE:
ret.double_val = lupb_checkdouble(L, narg);
break;
case UPB_TYPE_FLOAT:
ret.float_val = lupb_checkfloat(L, narg);
break;
case UPB_TYPE_BOOL:
ret.bool_val = lupb_checkbool(L, narg);
break;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
size_t len;
const char *ptr = lupb_checkstring(L, narg, &len);
switch (copy) {
case LUPB_COPY: {
upb_arena *arena = lupb_arenaget(L, container);
char *data = upb_arena_malloc(arena, len);
memcpy(data, ptr, len);
ret.str_val = upb_strview_make(data, len);
break;
}
case LUPB_REF:
ret.str_val = upb_strview_make(ptr, len);
break;
}
break;
}
case UPB_TYPE_MESSAGE:
ret.msg_val = lupb_msg_check(L, narg);
/* Typecheck message. */
lua_getiuservalue(L, container, LUPB_MSGDEF_INDEX);
lua_getiuservalue(L, narg, LUPB_MSGDEF_INDEX);
luaL_argcheck(L, lua_rawequal(L, -1, -2), narg, "message type mismatch");
lua_pop(L, 2);
break;
}
return ret;
}
static void lupb_pushmsgval(lua_State *L, int container, upb_fieldtype_t type,
upb_msgval val) {
switch (type) {
case UPB_TYPE_INT32:
case UPB_TYPE_ENUM:
lupb_pushint32(L, val.int32_val);
return;
case UPB_TYPE_INT64:
lupb_pushint64(L, val.int64_val);
return;
case UPB_TYPE_UINT32:
lupb_pushuint32(L, val.uint32_val);
return;
case UPB_TYPE_UINT64:
lupb_pushuint64(L, val.uint64_val);
return;
case UPB_TYPE_DOUBLE:
lua_pushnumber(L, val.double_val);
return;
case UPB_TYPE_FLOAT:
lua_pushnumber(L, val.float_val);
return;
case UPB_TYPE_BOOL:
lua_pushboolean(L, val.bool_val);
return;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
lua_pushlstring(L, val.str_val.data, val.str_val.size);
return;
case UPB_TYPE_MESSAGE:
assert(container);
if (!lupb_cacheget(L, val.msg_val)) {
lupb_msg_newmsgwrapper(L, container, val);
}
return;
}
LUPB_UNREACHABLE();
}
/* lupb_array *****************************************************************/
typedef struct {
upb_array *arr;
upb_fieldtype_t type;
} lupb_array;
static lupb_array *lupb_array_check(lua_State *L, int narg) {
return luaL_checkudata(L, narg, LUPB_ARRAY);
}
/**
* lupb_array_checkindex()
*
* Checks the array index at Lua stack index |narg| to verify that it is an
* integer between 1 and |max|, inclusively. Also corrects it to be zero-based
* for C.
*/
static int lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
uint32_t n = lupb_checkuint32(L, narg);
luaL_argcheck(L, n != 0 && n <= max, narg, "invalid array index");
return n - 1; /* Lua uses 1-based indexing. */
}
/* lupb_array Public API */
/* lupb_array_new():
*
* Handles:
* Array(upb.TYPE_INT32)
* Array(message_type)
*/
static int lupb_array_new(lua_State *L) {
lupb_array *larray;
upb_arena *arena;
if (lua_type(L, 1) == LUA_TNUMBER) {
upb_fieldtype_t type = lupb_checkfieldtype(L, 1);
larray = lupb_newuserdata(L, sizeof(*larray), 1, LUPB_ARRAY);
larray->type = type;
} else {
lupb_msgdef_check(L, 1);
larray = lupb_newuserdata(L, sizeof(*larray), 2, LUPB_ARRAY);
larray->type = UPB_TYPE_MESSAGE;
lua_pushvalue(L, 1);
lua_setiuservalue(L, -2, LUPB_MSGDEF_INDEX);
}
arena = lupb_arena_pushnew(L);
lua_setiuservalue(L, -2, LUPB_ARENA_INDEX);
larray->arr = upb_array_new(arena, larray->type);
lupb_cacheset(L, larray->arr);
return 1;
}
/* lupb_array_newindex():
*
* Handles:
* array[idx] = val
*
* idx can be within the array or one past the end to extend.
*/
static int lupb_array_newindex(lua_State *L) {
lupb_array *larray = lupb_array_check(L, 1);
size_t size = upb_array_size(larray->arr);
uint32_t n = lupb_array_checkindex(L, 2, size + 1);
upb_msgval msgval = lupb_tomsgval(L, larray->type, 3, 1, LUPB_COPY);
if (n == size) {
upb_array_append(larray->arr, msgval, lupb_arenaget(L, 1));
} else {
upb_array_set(larray->arr, n, msgval);
}
return 0; /* 1 for chained assignments? */
}
/* lupb_array_index():
*
* Handles:
* array[idx] -> val
*
* idx must be within the array.
*/
static int lupb_array_index(lua_State *L) {
lupb_array *larray = lupb_array_check(L, 1);
size_t size = upb_array_size(larray->arr);
uint32_t n = lupb_array_checkindex(L, 2, size);
upb_msgval val = upb_array_get(larray->arr, n);
lupb_pushmsgval(L, 1, larray->type, val);
return 1;
}
/* lupb_array_len():
*
* Handles:
* #array -> len
*/
static int lupb_array_len(lua_State *L) {
lupb_array *larray = lupb_array_check(L, 1);
lua_pushnumber(L, upb_array_size(larray->arr));
return 1;
}
static const struct luaL_Reg lupb_array_mm[] = {
{"__index", lupb_array_index},
{"__len", lupb_array_len},
{"__newindex", lupb_array_newindex},
{NULL, NULL}
};
/* lupb_map *******************************************************************/
typedef struct {
upb_map *map;
upb_fieldtype_t key_type;
upb_fieldtype_t value_type;
} lupb_map;
#define MAP_MSGDEF_INDEX 1
static lupb_map *lupb_map_check(lua_State *L, int narg) {
return luaL_checkudata(L, narg, LUPB_MAP);
}
/* lupb_map Public API */
/**
* lupb_map_new
*
* Handles:
* new_map = upb.Map(key_type, value_type)
* new_map = upb.Map(key_type, value_msgdef)
*/
static int lupb_map_new(lua_State *L) {
upb_arena *arena;
lupb_map *lmap;
if (lua_type(L, 2) == LUA_TNUMBER) {
lmap = lupb_newuserdata(L, sizeof(*lmap), 1, LUPB_MAP);
lmap->value_type = lupb_checkfieldtype(L, 2);
} else {
lupb_msgdef_check(L, 2);
lmap = lupb_newuserdata(L, sizeof(*lmap), 2, LUPB_MAP);
lmap->value_type = UPB_TYPE_MESSAGE;
lua_pushvalue(L, 2);
lua_setiuservalue(L, -2, MAP_MSGDEF_INDEX);
}
arena = lupb_arena_pushnew(L);
lua_setiuservalue(L, -2, LUPB_ARENA_INDEX);
lmap->key_type = lupb_checkfieldtype(L, 1);
lmap->map = upb_map_new(arena, lmap->key_type, lmap->value_type);
lupb_cacheset(L, lmap->map);
return 1;
}
/**
* lupb_map_index
*
* Handles:
* map[key]
*/
static int lupb_map_index(lua_State *L) {
lupb_map *lmap = lupb_map_check(L, 1);
upb_msgval key = lupb_tomsgval(L, lmap->key_type, 2, 1, LUPB_REF);
upb_msgval val;
if (upb_map_get(lmap->map, key, &val)) {
lupb_pushmsgval(L, 1, lmap->value_type, val);
} else {
lua_pushnil(L);
}
return 1;
}
/**
* lupb_map_len
*
* Handles:
* map_len = #map
*/
static int lupb_map_len(lua_State *L) {
lupb_map *lmap = lupb_map_check(L, 1);
lua_pushnumber(L, upb_map_size(lmap->map));
return 1;
}
/**
* lupb_map_newindex
*
* Handles:
* map[key] = val
* map[key] = nil # to remove from map
*/
static int lupb_map_newindex(lua_State *L) {
lupb_map *lmap = lupb_map_check(L, 1);
upb_map *map = lmap->map;
upb_msgval key = lupb_tomsgval(L, lmap->key_type, 2, 1, LUPB_REF);
if (lua_isnil(L, 3)) {
upb_map_delete(map, key);
} else {
upb_msgval val = lupb_tomsgval(L, lmap->value_type, 3, 1, LUPB_COPY);
upb_map_set(map, key, val, lupb_arenaget(L, 1));
}
return 0;
}
static int lupb_mapiter_next(lua_State *L) {
int map = lua_upvalueindex(2);
size_t *iter = lua_touserdata(L, lua_upvalueindex(1));
lupb_map *lmap = lupb_map_check(L, map);
if (upb_mapiter_next(lmap->map, iter)) {
upb_msgval key = upb_mapiter_key(lmap->map, *iter);
upb_msgval val = upb_mapiter_value(lmap->map, *iter);
lupb_pushmsgval(L, map, lmap->key_type, key);
lupb_pushmsgval(L, map, lmap->value_type, val);
return 2;
} else {
return 0;
}
}
/**
* lupb_map_pairs()
*
* Handles:
* pairs(map)
*/
static int lupb_map_pairs(lua_State *L) {
lupb_map_check(L, 1);
size_t *iter = lua_newuserdata(L, sizeof(*iter));
*iter = UPB_MAP_BEGIN;
lua_pushvalue(L, 1);
/* Upvalues are [iter, lupb_map]. */
lua_pushcclosure(L, &lupb_mapiter_next, 2);
return 1;
}
/* upb_mapiter ]]] */
static const struct luaL_Reg lupb_map_mm[] = {
{"__index", lupb_map_index},
{"__len", lupb_map_len},
{"__newindex", lupb_map_newindex},
{"__pairs", lupb_map_pairs},
{NULL, NULL}
};
/* lupb_msg *******************************************************************/
typedef struct {
upb_msg *msg;
} lupb_msg;
/* lupb_msg helpers */
static upb_msg *lupb_msg_check(lua_State *L, int narg) {
lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
return msg->msg;
}
static const upb_fielddef *lupb_msg_checkfield(lua_State *L, int msg,
int field) {
size_t len;
const char *fieldname = luaL_checklstring(L, field, &len);
const upb_msgdef *m;
const upb_fielddef *f;
lua_getiuservalue(L, msg, LUPB_MSGDEF_INDEX);
m = lupb_msgdef_check(L, -1);
f = upb_msgdef_ntof(m, fieldname, len);
if (f == NULL) {
luaL_error(L, "no such field '%s'", fieldname);
}
lua_pop(L, 1);
return f;
}
/**
* lupb_msg_newmsgwrapper()
*
* Creates a new wrapper for a message, copying the arena and msgdef references
* from |narg| (which should be an array or map).
*/
static void lupb_msg_newmsgwrapper(lua_State *L, int narg, upb_msgval val) {
lupb_msg *lmsg = lupb_newuserdata(L, sizeof(*lmsg), 2, LUPB_MSG);
lmsg->msg = (upb_msg*)val.msg_val; /* XXX: cast isn't great. */
lupb_cacheset(L, lmsg->msg);
/* Copy both arena and msgdef into the wrapper. */
lua_getiuservalue(L, narg, LUPB_ARENA_INDEX);
lua_setiuservalue(L, -2, LUPB_ARENA_INDEX);
lua_getiuservalue(L, narg, LUPB_MSGDEF_INDEX);
lua_setiuservalue(L, -2, LUPB_MSGDEF_INDEX);
}
/**
* lupb_msg_newud()
*
* Creates the Lua userdata for a new wrapper object, adding a reference to
* the msgdef if necessary.
*/
static void *lupb_msg_newud(lua_State *L, int narg, size_t size,
const char *type, const upb_fielddef *f) {
if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) {
/* Wrapper needs a reference to the msgdef. */
void* ud = lupb_newuserdata(L, size, 2, type);
lua_getiuservalue(L, narg, LUPB_MSGDEF_INDEX);
lupb_msgdef_pushsubmsgdef(L, f);
lua_setiuservalue(L, -2, LUPB_MSGDEF_INDEX);
return ud;
} else {
return lupb_newuserdata(L, size, 1, type);
}
}
/**
* lupb_msg_newwrapper()
*
* Creates a new Lua wrapper object to wrap the given array, map, or message.
*/
static void lupb_msg_newwrapper(lua_State *L, int narg, const upb_fielddef *f,
upb_mutmsgval val) {
if (upb_fielddef_ismap(f)) {
const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
const upb_fielddef *key_f = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
const upb_fielddef *val_f = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
lupb_map *lmap = lupb_msg_newud(L, narg, sizeof(*lmap), LUPB_MAP, val_f);
lmap->key_type = upb_fielddef_type(key_f);
lmap->value_type = upb_fielddef_type(val_f);
lmap->map = val.map;
} else if (upb_fielddef_isseq(f)) {
lupb_array *larr = lupb_msg_newud(L, narg, sizeof(*larr), LUPB_ARRAY, f);
larr->type = upb_fielddef_type(f);
larr->arr = val.array;
} else {
lupb_msg *lmsg = lupb_msg_newud(L, narg, sizeof(*lmsg), LUPB_MSG, f);
lmsg->msg = val.msg;
}
/* Copy arena ref to new wrapper. This may be a different arena than the
* underlying data was originally constructed from, but if so both arenas
* must be in the same group. */
lua_getiuservalue(L, narg, LUPB_ARENA_INDEX);
lua_setiuservalue(L, -2, LUPB_ARENA_INDEX);
lupb_cacheset(L, val.msg);
}
/**
* lupb_msg_typechecksubmsg()
*
* Typechecks the given array, map, or msg against this upb_fielddef.
*/
static void lupb_msg_typechecksubmsg(lua_State *L, int narg, int msgarg,
const upb_fielddef *f) {
/* Typecheck this map's msgdef against this message field. */
lua_getiuservalue(L, narg, LUPB_MSGDEF_INDEX);
lua_getiuservalue(L, msgarg, LUPB_MSGDEF_INDEX);
lupb_msgdef_pushsubmsgdef(L, f);
luaL_argcheck(L, lua_rawequal(L, -1, -2), narg, "message type mismatch");
lua_pop(L, 2);
}
/* lupb_msg Public API */
/**
* lupb_msg_pushnew
*
* Handles:
* new_msg = MessageClass()
* new_msg = MessageClass{foo = "bar", baz = 3, quux = {foo = 3}}
*/
int lupb_msg_pushnew(lua_State *L) {
int argcount = lua_gettop(L);
const upb_msgdef *m = lupb_msgdef_check(L, 1);
lupb_msg *lmsg = lupb_newuserdata(L, sizeof(lupb_msg), 2, LUPB_MSG);
upb_arena *arena = lupb_arena_pushnew(L);
lua_setiuservalue(L, -2, LUPB_ARENA_INDEX);
lua_pushvalue(L, 1);
lua_setiuservalue(L, -2, LUPB_MSGDEF_INDEX);
lmsg->msg = upb_msg_new(m, arena);
lupb_cacheset(L, lmsg->msg);
if (argcount > 1) {
/* Set initial fields from table. */
int msg = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
lua_pushvalue(L, -2); /* now stack is key, val, key */
lua_insert(L, -3); /* now stack is key, key, val */
lua_settable(L, msg);
}
}
return 1;
}
/**
* lupb_msg_index
*
* Handles:
* msg.foo
* msg["foo"]
* msg[field_descriptor] # (for extensions) (TODO)
*/
static int lupb_msg_index(lua_State *L) {
upb_msg *msg = lupb_msg_check(L, 1);
const upb_fielddef *f = lupb_msg_checkfield(L, 1, 2);
if (upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f)) {
/* Wrapped type; get or create wrapper. */
upb_arena *arena = upb_fielddef_isseq(f) ? lupb_arenaget(L, 1) : NULL;
upb_mutmsgval val = upb_msg_mutable(msg, f, arena);
if (!lupb_cacheget(L, val.msg)) {
lupb_msg_newwrapper(L, 1, f, val);
}
} else {
/* Value type, just push value and return .*/
upb_msgval val = upb_msg_get(msg, f);
lupb_pushmsgval(L, 0, upb_fielddef_type(f), val);
}
return 1;
}
/**
* lupb_msg_newindex()
*
* Handles:
* msg.foo = bar
* msg["foo"] = bar
* msg[field_descriptor] = bar # (for extensions) (TODO)
*/
static int lupb_msg_newindex(lua_State *L) {
upb_msg *msg = lupb_msg_check(L, 1);
const upb_fielddef *f = lupb_msg_checkfield(L, 1, 2);
upb_msgval msgval;
bool merge_arenas = true;
if (upb_fielddef_ismap(f)) {
lupb_map *lmap = lupb_map_check(L, 3);
const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
const upb_fielddef *key_f = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
const upb_fielddef *val_f = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
upb_fieldtype_t key_type = upb_fielddef_type(key_f);
upb_fieldtype_t value_type = upb_fielddef_type(val_f);
luaL_argcheck(L, lmap->key_type == key_type, 3, "key type mismatch");
luaL_argcheck(L, lmap->value_type == value_type, 3, "value type mismatch");
if (value_type == UPB_TYPE_MESSAGE) {
lupb_msg_typechecksubmsg(L, 3, 1, val_f);
}
msgval.map_val = lmap->map;
} else if (upb_fielddef_isseq(f)) {
lupb_array *larr = lupb_array_check(L, 3);
upb_fieldtype_t type = upb_fielddef_type(f);
luaL_argcheck(L, larr->type == type, 3, "array type mismatch");
if (type == UPB_TYPE_MESSAGE) {
lupb_msg_typechecksubmsg(L, 3, 1, f);
}
msgval.array_val = larr->arr;
} else if (upb_fielddef_issubmsg(f)) {
upb_msg *msg = lupb_msg_check(L, 3);
lupb_msg_typechecksubmsg(L, 3, 1, f);
msgval.msg_val = msg;
} else {
msgval = lupb_tomsgval(L, upb_fielddef_type(f), 3, 1, LUPB_COPY);
merge_arenas = false;
}
if (merge_arenas) {
lua_getiuservalue(L, 1, LUPB_ARENA_INDEX);
lua_getiuservalue(L, 3, LUPB_ARENA_INDEX);
lupb_arena_fuse(L, lua_absindex(L, -2), lua_absindex(L, -1));
lua_pop(L, 2);
}
upb_msg_set(msg, f, msgval, lupb_arenaget(L, 1));
/* Return the new value for chained assignments. */
lua_pushvalue(L, 3);
return 1;
}
/**
* lupb_msg_tostring()
*
* Handles:
* tostring(msg)
* print(msg)
* etc.
*/
static int lupb_msg_tostring(lua_State *L) {
upb_msg *msg = lupb_msg_check(L, 1);
const upb_msgdef *m;
char buf[1024];
size_t size;
lua_getiuservalue(L, 1, LUPB_MSGDEF_INDEX);
m = lupb_msgdef_check(L, -1);
size = upb_text_encode(msg, m, NULL, 0, buf, sizeof(buf));
if (size < sizeof(buf)) {
lua_pushlstring(L, buf, size);
} else {
char *ptr = malloc(size + 1);
upb_text_encode(msg, m, NULL, 0, ptr, size + 1);
lua_pushlstring(L, ptr, size);
free(ptr);
}
return 1;
}
static const struct luaL_Reg lupb_msg_mm[] = {
{"__index", lupb_msg_index},
{"__newindex", lupb_msg_newindex},
{"__tostring", lupb_msg_tostring},
{NULL, NULL}
};
/* lupb_msg toplevel **********************************************************/
/**
* lupb_decode()
*
* Handles:
* msg = upb.decode(MessageClass, bin_string)
*/
static int lupb_decode(lua_State *L) {
size_t len;
const upb_msgdef *m = lupb_msgdef_check(L, 1);
const char *pb = lua_tolstring(L, 2, &len);
const upb_msglayout *layout = upb_msgdef_layout(m);
char *buf;
upb_msg *msg;
upb_arena *arena;
bool ok;
/* Create message. */
lua_pushcfunction(L, &lupb_msg_pushnew);
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
msg = lupb_msg_check(L, -1);
lua_getiuservalue(L, -1, LUPB_ARENA_INDEX);
arena = lupb_arena_check(L, -1);
lua_pop(L, 1);
/* Copy input data to arena, message will reference it. */
buf = upb_arena_malloc(arena, len);
memcpy(buf, pb, len);
ok = upb_decode(buf, len, msg, layout, arena);
if (!ok) {
lua_pushstring(L, "Error decoding protobuf.");
return lua_error(L);
}
return 1;
}
/**
* lupb_encode()
*
* Handles:
* bin_string = upb.encode(msg)
*/
static int lupb_encode(lua_State *L) {
const upb_msg *msg = lupb_msg_check(L, 1);
const upb_msglayout *layout;
upb_arena *arena = lupb_arena_pushnew(L);
size_t size;
char *result;
lua_getiuservalue(L, 1, LUPB_MSGDEF_INDEX);
layout = upb_msgdef_layout(lupb_msgdef_check(L, -1));
lua_pop(L, 1);
result = upb_encode(msg, (const void*)layout, arena, &size);
if (!result) {
lua_pushstring(L, "Error encoding protobuf.");
return lua_error(L);
}
lua_pushlstring(L, result, size);
return 1;
}
static const struct luaL_Reg lupb_msg_toplevel_m[] = {
{"Array", lupb_array_new},
{"Map", lupb_map_new},
{"decode", lupb_decode},
{"encode", lupb_encode},
{NULL, NULL}
};
void lupb_msg_registertypes(lua_State *L) {
lupb_setfuncs(L, lupb_msg_toplevel_m);
lupb_register_type(L, LUPB_ARENA, NULL, lupb_arena_mm);
lupb_register_type(L, LUPB_ARRAY, NULL, lupb_array_mm);
lupb_register_type(L, LUPB_MAP, NULL, lupb_map_mm);
lupb_register_type(L, LUPB_MSG, NULL, lupb_msg_mm);
lupb_cacheinit(L);
}