|
|
|
/*
|
|
|
|
* Copyright (c) 2009-2021, Google LLC
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Google LLC nor the
|
|
|
|
* names of its contributors may be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
|
|
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* lupb_Message -- 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/json_decode.h"
|
|
|
|
#include "upb/json_encode.h"
|
|
|
|
#include "upb/port_def.inc"
|
|
|
|
#include "upb/reflection.h"
|
|
|
|
#include "upb/text_encode.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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_Message, 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_Message*,
|
|
|
|
* 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_Message
|
|
|
|
* ----------------|-----------------|------------------------------------------
|
|
|
|
* upb level | |
|
|
|
|
* | +----V----------------------------------+
|
|
|
|
* +->upb_Arena | upb_Message ...(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_Message lupb_Message--+ |
|
|
|
|
* ----------------|-----------------|----------------------|-----------|------
|
|
|
|
* upb level | | | |
|
|
|
|
* | +----V--------+ +----V--------+ V
|
|
|
|
* +->upb_Arena | upb_Message | | upb_Message | 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_Message_Newmsgwrapper(lua_State* L, int narg,
|
|
|
|
upb_MessageValue val);
|
|
|
|
static upb_Message* lupb_msg_check(lua_State* L, int narg);
|
|
|
|
|
|
|
|
static upb_CType lupb_checkfieldtype(lua_State* L, int narg) {
|
|
|
|
uint32_t n = lupb_checkuint32(L, narg);
|
|
|
|
bool ok = n >= kUpb_CType_Bool && n <= kUpb_CType_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 void lupb_Arena_Fuseobjs(lua_State* L, int to, int from) {
|
|
|
|
lua_getiuservalue(L, to, LUPB_ARENA_INDEX);
|
|
|
|
lua_getiuservalue(L, from, LUPB_ARENA_INDEX);
|
|
|
|
lupb_Arena_Fuse(L, lua_absindex(L, -2), lua_absindex(L, -1));
|
|
|
|
lua_pop(L, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_MessageValue.
|
|
|
|
*/
|
|
|
|
static upb_MessageValue lupb_tomsgval(lua_State* L, upb_CType type, int narg,
|
|
|
|
int container, lupb_copy_t copy) {
|
|
|
|
upb_MessageValue ret;
|
|
|
|
switch (type) {
|
|
|
|
case kUpb_CType_Int32:
|
|
|
|
case kUpb_CType_Enum:
|
|
|
|
ret.int32_val = lupb_checkint32(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_Int64:
|
|
|
|
ret.int64_val = lupb_checkint64(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_UInt32:
|
|
|
|
ret.uint32_val = lupb_checkuint32(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_UInt64:
|
|
|
|
ret.uint64_val = lupb_checkuint64(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_Double:
|
|
|
|
ret.double_val = lupb_checkdouble(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_Float:
|
|
|
|
ret.float_val = lupb_checkfloat(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_Bool:
|
|
|
|
ret.bool_val = lupb_checkbool(L, narg);
|
|
|
|
break;
|
|
|
|
case kUpb_CType_String:
|
|
|
|
case kUpb_CType_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_StringView_FromDataAndSize(data, len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LUPB_REF:
|
|
|
|
ret.str_val = upb_StringView_FromDataAndSize(ptr, len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case kUpb_CType_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lupb_pushmsgval(lua_State* L, int container, upb_CType type,
|
|
|
|
upb_MessageValue val) {
|
|
|
|
switch (type) {
|
|
|
|
case kUpb_CType_Int32:
|
|
|
|
case kUpb_CType_Enum:
|
|
|
|
lupb_pushint32(L, val.int32_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_Int64:
|
|
|
|
lupb_pushint64(L, val.int64_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_UInt32:
|
|
|
|
lupb_pushuint32(L, val.uint32_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_UInt64:
|
|
|
|
lupb_pushuint64(L, val.uint64_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_Double:
|
|
|
|
lua_pushnumber(L, val.double_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_Float:
|
|
|
|
lua_pushnumber(L, val.float_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_Bool:
|
|
|
|
lua_pushboolean(L, val.bool_val);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_String:
|
|
|
|
case kUpb_CType_Bytes:
|
|
|
|
lua_pushlstring(L, val.str_val.data, val.str_val.size);
|
|
|
|
return;
|
|
|
|
case kUpb_CType_Message:
|
|
|
|
assert(container);
|
|
|
|
if (!lupb_cacheget(L, val.msg_val)) {
|
|
|
|
lupb_Message_Newmsgwrapper(L, container, val);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LUPB_UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lupb_array *****************************************************************/
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
upb_Array* arr;
|
|
|
|
upb_CType 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) {
|
|
|
|
int arg_count = lua_gettop(L);
|
|
|
|
lupb_array* larray;
|
|
|
|
upb_Arena* arena;
|
|
|
|
|
|
|
|
if (lua_type(L, 1) == LUA_TNUMBER) {
|
|
|
|
upb_CType type = lupb_checkfieldtype(L, 1);
|
|
|
|
larray = lupb_newuserdata(L, sizeof(*larray), 1, LUPB_ARRAY);
|
|
|
|
larray->type = type;
|
|
|
|
} else {
|
|
|
|
lupb_MessageDef_check(L, 1);
|
|
|
|
larray = lupb_newuserdata(L, sizeof(*larray), 2, LUPB_ARRAY);
|
|
|
|
larray->type = kUpb_CType_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);
|
|
|
|
|
|
|
|
if (arg_count > 1) {
|
|
|
|
/* Set initial fields from table. */
|
|
|
|
int msg = arg_count + 1;
|
|
|
|
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_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_MessageValue 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (larray->type == kUpb_CType_Message) {
|
|
|
|
lupb_Arena_Fuseobjs(L, 1, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_MessageValue 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_CType key_type;
|
|
|
|
upb_CType 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_MessageDef_check(L, 2);
|
|
|
|
lmap = lupb_newuserdata(L, sizeof(*lmap), 2, LUPB_MAP);
|
|
|
|
lmap->value_type = kUpb_CType_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_MessageValue key = lupb_tomsgval(L, lmap->key_type, 2, 1, LUPB_REF);
|
|
|
|
upb_MessageValue 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_MessageValue key = lupb_tomsgval(L, lmap->key_type, 2, 1, LUPB_REF);
|
|
|
|
|
|
|
|
if (lua_isnil(L, 3)) {
|
|
|
|
upb_Map_Delete(map, key);
|
|
|
|
} else {
|
|
|
|
upb_MessageValue val = lupb_tomsgval(L, lmap->value_type, 3, 1, LUPB_COPY);
|
|
|
|
upb_Map_Set(map, key, val, lupb_Arenaget(L, 1));
|
|
|
|
if (lmap->value_type == kUpb_CType_Message) {
|
|
|
|
lupb_Arena_Fuseobjs(L, 1, 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lupb_MapIterator_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_MapIterator_Next(lmap->map, iter)) {
|
|
|
|
upb_MessageValue key = upb_MapIterator_Key(lmap->map, *iter);
|
|
|
|
upb_MessageValue val = upb_MapIterator_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) {
|
|
|
|
size_t* iter = lua_newuserdata(L, sizeof(*iter));
|
|
|
|
lupb_map_check(L, 1);
|
|
|
|
|
|
|
|
*iter = kUpb_Map_Begin;
|
|
|
|
lua_pushvalue(L, 1);
|
|
|
|
|
|
|
|
/* Upvalues are [iter, lupb_map]. */
|
|
|
|
lua_pushcclosure(L, &lupb_MapIterator_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_Message
|
|
|
|
* *******************************************************************/
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
upb_Message* msg;
|
|
|
|
} lupb_Message;
|
|
|
|
|
|
|
|
/* lupb_Message helpers */
|
|
|
|
|
|
|
|
static upb_Message* lupb_msg_check(lua_State* L, int narg) {
|
|
|
|
lupb_Message* msg = luaL_checkudata(L, narg, LUPB_MSG);
|
|
|
|
return msg->msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const upb_MessageDef* lupb_Message_Getmsgdef(lua_State* L, int msg) {
|
|
|
|
lua_getiuservalue(L, msg, LUPB_MSGDEF_INDEX);
|
|
|
|
const upb_MessageDef* m = lupb_MessageDef_check(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const upb_FieldDef* lupb_msg_tofield(lua_State* L, int msg, int field) {
|
|
|
|
size_t len;
|
|
|
|
const char* fieldname = luaL_checklstring(L, field, &len);
|
|
|
|
const upb_MessageDef* m = lupb_Message_Getmsgdef(L, msg);
|
|
|
|
return upb_MessageDef_FindFieldByNameWithSize(m, fieldname, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const upb_FieldDef* lupb_msg_checkfield(lua_State* L, int msg,
|
|
|
|
int field) {
|
|
|
|
const upb_FieldDef* f = lupb_msg_tofield(L, msg, field);
|
|
|
|
if (f == NULL) {
|
|
|
|
luaL_error(L, "no such field '%s'", lua_tostring(L, field));
|
|
|
|
}
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_Message* lupb_msg_pushnew(lua_State* L, int narg) {
|
|
|
|
const upb_MessageDef* m = lupb_MessageDef_check(L, narg);
|
|
|
|
lupb_Message* lmsg = lupb_newuserdata(L, sizeof(lupb_Message), 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_Message_New(m, arena);
|
|
|
|
lupb_cacheset(L, lmsg->msg);
|
|
|
|
return lmsg->msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_Message_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_Message_Newmsgwrapper(lua_State* L, int narg,
|
|
|
|
upb_MessageValue val) {
|
|
|
|
lupb_Message* lmsg = lupb_newuserdata(L, sizeof(*lmsg), 2, LUPB_MSG);
|
|
|
|
lmsg->msg = (upb_Message*)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_Message_Newud()
|
|
|
|
*
|
|
|
|
* Creates the Lua userdata for a new wrapper object, adding a reference to
|
|
|
|
* the msgdef if necessary.
|
|
|
|
*/
|
|
|
|
static void* lupb_Message_Newud(lua_State* L, int narg, size_t size,
|
|
|
|
const char* type, const upb_FieldDef* f) {
|
|
|
|
if (upb_FieldDef_CType(f) == kUpb_CType_Message) {
|
|
|
|
/* Wrapper needs a reference to the msgdef. */
|
|
|
|
void* ud = lupb_newuserdata(L, size, 2, type);
|
|
|
|
lua_getiuservalue(L, narg, LUPB_MSGDEF_INDEX);
|
|
|
|
lupb_MessageDef_pushsubmsgdef(L, f);
|
|
|
|
lua_setiuservalue(L, -2, LUPB_MSGDEF_INDEX);
|
|
|
|
return ud;
|
|
|
|
} else {
|
|
|
|
return lupb_newuserdata(L, size, 1, type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_Message_Newwrapper()
|
|
|
|
*
|
|
|
|
* Creates a new Lua wrapper object to wrap the given array, map, or message.
|
|
|
|
*/
|
|
|
|
static void lupb_Message_Newwrapper(lua_State* L, int narg,
|
|
|
|
const upb_FieldDef* f,
|
|
|
|
upb_MutableMessageValue val) {
|
|
|
|
if (upb_FieldDef_IsMap(f)) {
|
|
|
|
const upb_MessageDef* entry = upb_FieldDef_MessageSubDef(f);
|
|
|
|
const upb_FieldDef* key_f =
|
|
|
|
upb_MessageDef_FindFieldByNumber(entry, kUpb_MapEntry_KeyFieldNumber);
|
|
|
|
const upb_FieldDef* val_f =
|
|
|
|
upb_MessageDef_FindFieldByNumber(entry, kUpb_MapEntry_ValueFieldNumber);
|
|
|
|
lupb_map* lmap =
|
|
|
|
lupb_Message_Newud(L, narg, sizeof(*lmap), LUPB_MAP, val_f);
|
|
|
|
lmap->key_type = upb_FieldDef_CType(key_f);
|
|
|
|
lmap->value_type = upb_FieldDef_CType(val_f);
|
|
|
|
lmap->map = val.map;
|
|
|
|
} else if (upb_FieldDef_IsRepeated(f)) {
|
|
|
|
lupb_array* larr =
|
|
|
|
lupb_Message_Newud(L, narg, sizeof(*larr), LUPB_ARRAY, f);
|
|
|
|
larr->type = upb_FieldDef_CType(f);
|
|
|
|
larr->arr = val.array;
|
|
|
|
} else {
|
|
|
|
lupb_Message* lmsg =
|
|
|
|
lupb_Message_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_MessageDef_pushsubmsgdef(L, f);
|
|
|
|
luaL_argcheck(L, lua_rawequal(L, -1, -2), narg, "message type mismatch");
|
|
|
|
lua_pop(L, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lupb_Message Public API */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_MessageDef_call
|
|
|
|
*
|
|
|
|
* Handles:
|
|
|
|
* new_msg = MessageClass()
|
|
|
|
* new_msg = MessageClass{foo = "bar", baz = 3, quux = {foo = 3}}
|
|
|
|
*/
|
|
|
|
int lupb_MessageDef_call(lua_State* L) {
|
|
|
|
int arg_count = lua_gettop(L);
|
|
|
|
lupb_msg_pushnew(L, 1);
|
|
|
|
|
|
|
|
if (arg_count > 1) {
|
|
|
|
/* Set initial fields from table. */
|
|
|
|
int msg = arg_count + 1;
|
|
|
|
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_Message* msg = lupb_msg_check(L, 1);
|
|
|
|
const upb_FieldDef* f = lupb_msg_checkfield(L, 1, 2);
|
|
|
|
|
|
|
|
if (upb_FieldDef_IsRepeated(f) || upb_FieldDef_IsSubMessage(f)) {
|
|
|
|
/* Wrapped type; get or create wrapper. */
|
|
|
|
upb_Arena* arena = upb_FieldDef_IsRepeated(f) ? lupb_Arenaget(L, 1) : NULL;
|
|
|
|
upb_MutableMessageValue val = upb_Message_Mutable(msg, f, arena);
|
|
|
|
if (!lupb_cacheget(L, val.msg)) {
|
|
|
|
lupb_Message_Newwrapper(L, 1, f, val);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Value type, just push value and return .*/
|
|
|
|
upb_MessageValue val = upb_Message_Get(msg, f);
|
|
|
|
lupb_pushmsgval(L, 0, upb_FieldDef_CType(f), val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_Message_Newindex()
|
|
|
|
*
|
|
|
|
* Handles:
|
|
|
|
* msg.foo = bar
|
|
|
|
* msg["foo"] = bar
|
|
|
|
* msg[field_descriptor] = bar # (for extensions) (TODO)
|
|
|
|
*/
|
|
|
|
static int lupb_Message_Newindex(lua_State* L) {
|
|
|
|
upb_Message* msg = lupb_msg_check(L, 1);
|
|
|
|
const upb_FieldDef* f = lupb_msg_checkfield(L, 1, 2);
|
|
|
|
upb_MessageValue msgval;
|
|
|
|
bool merge_arenas = true;
|
|
|
|
|
|
|
|
if (upb_FieldDef_IsMap(f)) {
|
|
|
|
lupb_map* lmap = lupb_map_check(L, 3);
|
|
|
|
const upb_MessageDef* entry = upb_FieldDef_MessageSubDef(f);
|
|
|
|
const upb_FieldDef* key_f =
|
|
|
|
upb_MessageDef_FindFieldByNumber(entry, kUpb_MapEntry_KeyFieldNumber);
|
|
|
|
const upb_FieldDef* val_f =
|
|
|
|
upb_MessageDef_FindFieldByNumber(entry, kUpb_MapEntry_ValueFieldNumber);
|
|
|
|
upb_CType key_type = upb_FieldDef_CType(key_f);
|
|
|
|
upb_CType value_type = upb_FieldDef_CType(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 == kUpb_CType_Message) {
|
|
|
|
lupb_msg_typechecksubmsg(L, 3, 1, val_f);
|
|
|
|
}
|
|
|
|
msgval.map_val = lmap->map;
|
|
|
|
} else if (upb_FieldDef_IsRepeated(f)) {
|
|
|
|
lupb_array* larr = lupb_array_check(L, 3);
|
|
|
|
upb_CType type = upb_FieldDef_CType(f);
|
|
|
|
luaL_argcheck(L, larr->type == type, 3, "array type mismatch");
|
|
|
|
if (type == kUpb_CType_Message) {
|
|
|
|
lupb_msg_typechecksubmsg(L, 3, 1, f);
|
|
|
|
}
|
|
|
|
msgval.array_val = larr->arr;
|
|
|
|
} else if (upb_FieldDef_IsSubMessage(f)) {
|
|
|
|
upb_Message* msg = lupb_msg_check(L, 3);
|
|
|
|
lupb_msg_typechecksubmsg(L, 3, 1, f);
|
|
|
|
msgval.msg_val = msg;
|
|
|
|
} else {
|
|
|
|
msgval = lupb_tomsgval(L, upb_FieldDef_CType(f), 3, 1, LUPB_COPY);
|
|
|
|
merge_arenas = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (merge_arenas) {
|
|
|
|
lupb_Arena_Fuseobjs(L, 1, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_Message_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_Message* msg = lupb_msg_check(L, 1);
|
|
|
|
const upb_MessageDef* m;
|
|
|
|
char buf[1024];
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
lua_getiuservalue(L, 1, LUPB_MSGDEF_INDEX);
|
|
|
|
m = lupb_MessageDef_check(L, -1);
|
|
|
|
|
|
|
|
size = upb_TextEncode(msg, m, NULL, 0, buf, sizeof(buf));
|
|
|
|
|
|
|
|
if (size < sizeof(buf)) {
|
|
|
|
lua_pushlstring(L, buf, size);
|
|
|
|
} else {
|
|
|
|
char* ptr = malloc(size + 1);
|
|
|
|
upb_TextEncode(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_Message_Newindex},
|
|
|
|
{"__tostring", lupb_msg_tostring},
|
|
|
|
{NULL, NULL}};
|
|
|
|
|
|
|
|
/* lupb_Message toplevel
|
|
|
|
* **********************************************************/
|
|
|
|
|
|
|
|
static int lupb_getoptions(lua_State* L, int narg) {
|
|
|
|
int options = 0;
|
|
|
|
if (lua_gettop(L) >= narg) {
|
|
|
|
size_t len = lua_rawlen(L, narg);
|
|
|
|
for (size_t i = 1; i <= len; i++) {
|
|
|
|
lua_rawgeti(L, narg, i);
|
|
|
|
options |= lupb_checkuint32(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_decode()
|
|
|
|
*
|
|
|
|
* Handles:
|
|
|
|
* msg = upb.decode(MessageClass, bin_string)
|
|
|
|
*/
|
|
|
|
static int lupb_decode(lua_State* L) {
|
|
|
|
size_t len;
|
|
|
|
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
|
|
|
|
const char* pb = lua_tolstring(L, 2, &len);
|
|
|
|
const upb_MiniTable* layout = upb_MessageDef_MiniTable(m);
|
|
|
|
upb_Message* msg = lupb_msg_pushnew(L, 1);
|
|
|
|
upb_Arena* arena = lupb_Arenaget(L, -1);
|
|
|
|
char* buf;
|
|
|
|
|
|
|
|
/* Copy input data to arena, message will reference it. */
|
|
|
|
buf = upb_Arena_Malloc(arena, len);
|
|
|
|
memcpy(buf, pb, len);
|
|
|
|
|
|
|
|
upb_DecodeStatus status = upb_Decode(buf, len, msg, layout, NULL,
|
|
|
|
kUpb_DecodeOption_AliasString, arena);
|
|
|
|
|
|
|
|
if (status != kUpb_DecodeStatus_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_Message* msg = lupb_msg_check(L, 1);
|
|
|
|
const upb_MessageDef* m = lupb_Message_Getmsgdef(L, 1);
|
|
|
|
const upb_MiniTable* layout = upb_MessageDef_MiniTable(m);
|
|
|
|
int options = lupb_getoptions(L, 2);
|
|
|
|
upb_Arena* arena = lupb_Arena_pushnew(L);
|
|
|
|
char* buf;
|
|
|
|
size_t size;
|
|
|
|
upb_EncodeStatus status =
|
|
|
|
upb_Encode(msg, (const void*)layout, options, arena, &buf, &size);
|
|
|
|
if (status != kUpb_EncodeStatus_Ok) {
|
|
|
|
lua_pushstring(L, "Error encoding protobuf.");
|
|
|
|
return lua_error(L);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushlstring(L, buf, size);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_jsondecode()
|
|
|
|
*
|
|
|
|
* Handles:
|
|
|
|
* text_string = upb.json_decode(MessageClass, json_str,
|
|
|
|
* {upb.JSONDEC_IGNOREUNKNOWN})
|
|
|
|
*/
|
|
|
|
static int lupb_jsondecode(lua_State* L) {
|
|
|
|
size_t len;
|
|
|
|
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
|
|
|
|
const char* json = lua_tolstring(L, 2, &len);
|
|
|
|
int options = lupb_getoptions(L, 3);
|
|
|
|
upb_Message* msg;
|
|
|
|
upb_Arena* arena;
|
|
|
|
upb_Status status;
|
|
|
|
|
|
|
|
msg = lupb_msg_pushnew(L, 1);
|
|
|
|
arena = lupb_Arenaget(L, -1);
|
|
|
|
upb_Status_Clear(&status);
|
|
|
|
upb_JsonDecode(json, len, msg, m, NULL, options, arena, &status);
|
|
|
|
lupb_checkstatus(L, &status);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_jsonencode()
|
|
|
|
*
|
|
|
|
* Handles:
|
|
|
|
* text_string = upb.json_encode(msg, {upb.JSONENC_EMITDEFAULTS})
|
|
|
|
*/
|
|
|
|
static int lupb_jsonencode(lua_State* L) {
|
|
|
|
upb_Message* msg = lupb_msg_check(L, 1);
|
|
|
|
const upb_MessageDef* m = lupb_Message_Getmsgdef(L, 1);
|
|
|
|
int options = lupb_getoptions(L, 2);
|
|
|
|
char buf[1024];
|
|
|
|
size_t size;
|
|
|
|
upb_Status status;
|
|
|
|
|
|
|
|
upb_Status_Clear(&status);
|
|
|
|
size = upb_JsonEncode(msg, m, NULL, options, buf, sizeof(buf), &status);
|
|
|
|
lupb_checkstatus(L, &status);
|
|
|
|
|
|
|
|
if (size < sizeof(buf)) {
|
|
|
|
lua_pushlstring(L, buf, size);
|
|
|
|
} else {
|
|
|
|
char* ptr = malloc(size + 1);
|
|
|
|
upb_JsonEncode(msg, m, NULL, options, ptr, size + 1, &status);
|
|
|
|
lupb_checkstatus(L, &status);
|
|
|
|
lua_pushlstring(L, ptr, size);
|
|
|
|
free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lupb_textencode()
|
|
|
|
*
|
|
|
|
* Handles:
|
|
|
|
* text_string = upb.text_encode(msg, {upb.TXTENC_SINGLELINE})
|
|
|
|
*/
|
|
|
|
static int lupb_textencode(lua_State* L) {
|
|
|
|
upb_Message* msg = lupb_msg_check(L, 1);
|
|
|
|
const upb_MessageDef* m = lupb_Message_Getmsgdef(L, 1);
|
|
|
|
int options = lupb_getoptions(L, 2);
|
|
|
|
char buf[1024];
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = upb_TextEncode(msg, m, NULL, options, buf, sizeof(buf));
|
|
|
|
|
|
|
|
if (size < sizeof(buf)) {
|
|
|
|
lua_pushlstring(L, buf, size);
|
|
|
|
} else {
|
|
|
|
char* ptr = malloc(size + 1);
|
|
|
|
upb_TextEncode(msg, m, NULL, options, ptr, size + 1);
|
|
|
|
lua_pushlstring(L, ptr, size);
|
|
|
|
free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lupb_setfieldi(lua_State* L, const char* field, int i) {
|
|
|
|
lua_pushinteger(L, i);
|
|
|
|
lua_setfield(L, -2, field);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct luaL_Reg lupb_msg_toplevel_m[] = {
|
|
|
|
{"Array", lupb_Array_New}, {"Map", lupb_Map_New},
|
|
|
|
{"decode", lupb_decode}, {"encode", lupb_Encode},
|
|
|
|
{"json_decode", lupb_jsondecode}, {"json_encode", lupb_jsonencode},
|
|
|
|
{"text_encode", lupb_textencode}, {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_setfieldi(L, "TXTENC_SINGLELINE", UPB_TXTENC_SINGLELINE);
|
|
|
|
lupb_setfieldi(L, "TXTENC_SKIPUNKNOWN", UPB_TXTENC_SKIPUNKNOWN);
|
|
|
|
lupb_setfieldi(L, "TXTENC_NOSORT", UPB_TXTENC_NOSORT);
|
|
|
|
|
|
|
|
lupb_setfieldi(L, "ENCODE_DETERMINISTIC", kUpb_EncodeOption_Deterministic);
|
|
|
|
lupb_setfieldi(L, "ENCODE_SKIPUNKNOWN", kUpb_EncodeOption_SkipUnknown);
|
|
|
|
|
|
|
|
lupb_setfieldi(L, "JSONENC_EMITDEFAULTS", upb_JsonEncode_EmitDefaults);
|
|
|
|
lupb_setfieldi(L, "JSONENC_PROTONAMES", upb_JsonEncode_UseProtoNames);
|
|
|
|
|
|
|
|
lupb_setfieldi(L, "JSONDEC_IGNOREUNKNOWN", upb_JsonDecode_IgnoreUnknown);
|
|
|
|
|
|
|
|
lupb_cacheinit(L);
|
|
|
|
}
|