#include <float.h>
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "lauxlib.h"
#include "lua/upb.h"
#include "upb/json/decode.h"
#include "upb/json/encode.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/port/def.inc"
#include "upb/reflection/message.h"
#include "upb/text/encode.h"
#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
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;
static void lupb_cacheinit(lua_State* L) {
lua_newtable(L);
lua_createtable(L, 0, 1);
lua_pushstring(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
lua_rawsetp(L, LUA_REGISTRYINDEX, &cache_key);
}
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);
return false;
} else {
lua_replace(L, -2);
return true;
}
}
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);
}
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;
}
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}};
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;
}
typedef enum {
LUPB_COPY,
LUPB_REF
} lupb_copy_t;
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);
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();
}
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);
}
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;
}
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) {
int msg = arg_count + 1;
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
lua_pushvalue(L, -2);
lua_insert(L, -3);
lua_settable(L, msg);
}
}
return 1;
}
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;
}
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;
}
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}};
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);
}
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;
}
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;
}
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;
}
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, NULL);
} 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);
upb_MessageValue key, val;
if (upb_Map_Next(lmap->map, &key, &val, iter)) {
lupb_pushmsgval(L, map, lmap->key_type, key);
lupb_pushmsgval(L, map, lmap->value_type, val);
return 2;
} else {
return 0;
}
}
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);
lua_pushcclosure(L, &lupb_MapIterator_Next, 2);
return 1;
}
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}};
typedef struct {
upb_Message* msg;
} lupb_Message;
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(upb_MessageDef_MiniTable(m), arena);
lupb_cacheset(L, lmsg->msg);
return lmsg->msg;
}
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;
lupb_cacheset(L, lmsg->msg);
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);
}
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) {
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);
}
}
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;
}
lua_getiuservalue(L, narg, LUPB_ARENA_INDEX);
lua_setiuservalue(L, -2, LUPB_ARENA_INDEX);
lupb_cacheset(L, val.msg);
}
static void lupb_msg_typechecksubmsg(lua_State* L, int narg, int msgarg,
const upb_FieldDef* f) {
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);
}
int lupb_MessageDef_call(lua_State* L) {
int arg_count = lua_gettop(L);
lupb_msg_pushnew(L, 1);
if (arg_count > 1) {
int msg = arg_count + 1;
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
lua_pushvalue(L, -2);
lua_insert(L, -3);
lua_settable(L, msg);
}
}
return 1;
}
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)) {
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 {
upb_MessageValue val = upb_Message_GetFieldByDef(msg, f);
lupb_pushmsgval(L, 0, upb_FieldDef_CType(f), val);
}
return 1;
}
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_SetFieldByDef(msg, f, msgval, lupb_Arenaget(L, 1));
lua_pushvalue(L, 3);
return 1;
}
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}};
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;
}
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;
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;
}
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;
}
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;
}
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;
}
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);
}