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.
774 lines
24 KiB
774 lines
24 KiB
/******************************************************************************
|
|
|
|
版权所有 (C), 2018-2099, Radkil
|
|
|
|
******************************************************************************
|
|
文 件 名 : pb_lua.c
|
|
版 本 号 : 初稿
|
|
功 能 描 述 : nanopb 消息的 Lua 桥接:table <-> C struct <-> protobuf 字节流
|
|
Lua 侧约定:
|
|
- 二进制一律 Lua string(与 COM:read/write 直通)
|
|
- 纯 table 表示消息;t.Value = 999 即改成员
|
|
- nil = 字段缺省(OPTIONAL 不出现在线上)
|
|
- 枚举字段为整数值(与 .proto 枚举编号一致,无名字表)
|
|
|
|
******************************************************************************/
|
|
#ifdef BUILD_PROTOBUF
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "lua_base.h"
|
|
#include "pb_lua.h"
|
|
#include "pb_lua_desc.h"
|
|
#include "pb.h"
|
|
#include "pb_encode.h"
|
|
#include "pb_decode.h"
|
|
#include "bsp_CV.pb.h"
|
|
|
|
/* 结构体/编码缓冲上限(当前最大消息 ~130B 结构 / 118B 编码,留足余量) */
|
|
#define PB_LUA_MAX_STRUCT 256
|
|
#define PB_LUA_MAX_ENCODED 256
|
|
|
|
/*======================================================================
|
|
* 消息注册表: 由 tools/gen_proto_ui.py --pb_lua_registry 生成
|
|
* pb_lua_registry.c 提供 g_pb_msgs / g_pb_msgs_count 强符号。
|
|
* 字段表内容仍走 Type_FIELDLIST X-macro(见 pb_lua_desc.h)。
|
|
*====================================================================*/
|
|
extern CV_struct_define g_stCV;
|
|
|
|
/*======================================================================
|
|
* 命名全局绑定注册表(pb_ref 用)
|
|
*====================================================================*/
|
|
|
|
#define PB_LUA_MAX_BINDINGS 8
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
void *ptr;
|
|
const pb_lua_msg_t *msg;
|
|
} pb_lua_binding_t;
|
|
|
|
static pb_lua_binding_t g_pb_bindings[PB_LUA_MAX_BINDINGS];
|
|
static size_t g_pb_nbindings;
|
|
|
|
/*======================================================================
|
|
* 查找辅助
|
|
*====================================================================*/
|
|
|
|
static size_t pb_lua_strnlen(const char *s, size_t max)
|
|
{
|
|
size_t n = 0;
|
|
while (n < max && s[n] != '\0')
|
|
n++;
|
|
return n;
|
|
}
|
|
|
|
static const pb_lua_msg_t *pb_lua_find_msg_or_null(const char *name)
|
|
{
|
|
for (int i = 0; i < g_pb_msgs_count; i++) {
|
|
if (strcmp(g_pb_msgs[i]->name, name) == 0)
|
|
return g_pb_msgs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const pb_lua_msg_t *pb_lua_find_msg(lua_State *L, const char *name)
|
|
{
|
|
const pb_lua_msg_t *m = pb_lua_find_msg_or_null(name);
|
|
if (m != NULL)
|
|
return m;
|
|
/* known 列表从注册表动态拼接(生成 registry 后零手改) */
|
|
char known[256];
|
|
size_t off = 0;
|
|
known[0] = '\0';
|
|
for (int i = 0; i < g_pb_msgs_count && off + 32 < sizeof(known); i++) {
|
|
int w = snprintf(known + off, sizeof(known) - off, "%s%s",
|
|
(i ? ", " : ""), g_pb_msgs[i]->name);
|
|
if (w <= 0) break;
|
|
off += (size_t)w;
|
|
}
|
|
luaL_error(L, "unknown message type '%s' (known: %s)", name, known);
|
|
return NULL;
|
|
}
|
|
|
|
int pb_lua_bind(const char *name, void *ptr, const char *msgtype)
|
|
{
|
|
const pb_lua_msg_t *m;
|
|
|
|
if (name == NULL || ptr == NULL)
|
|
return -1;
|
|
m = pb_lua_find_msg_or_null(msgtype);
|
|
if (m == NULL)
|
|
return -1;
|
|
|
|
for (size_t i = 0; i < g_pb_nbindings; i++) {
|
|
if (strcmp(g_pb_bindings[i].name, name) == 0) {
|
|
g_pb_bindings[i].ptr = ptr;
|
|
g_pb_bindings[i].msg = m;
|
|
return 0;
|
|
}
|
|
}
|
|
if (g_pb_nbindings >= PB_LUA_MAX_BINDINGS)
|
|
return -2;
|
|
g_pb_bindings[g_pb_nbindings].name = name;
|
|
g_pb_bindings[g_pb_nbindings].ptr = ptr;
|
|
g_pb_bindings[g_pb_nbindings].msg = m;
|
|
g_pb_nbindings++;
|
|
return 0;
|
|
}
|
|
|
|
static const pb_lua_field_t *pb_lua_find_field(const pb_lua_msg_t *m, const char *name)
|
|
{
|
|
for (size_t i = 0; i < m->nfields; i++) {
|
|
if (strcmp(m->fields[i].name, name) == 0)
|
|
return &m->fields[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*======================================================================
|
|
* table → C struct(encode 方向)
|
|
*====================================================================*/
|
|
|
|
static void pb_lua_table_to_msg(lua_State *L, int tidx, const pb_lua_msg_t *m, void *msg);
|
|
|
|
static void pb_lua_set_one(lua_State *L, int vidx, const pb_lua_field_t *f, void *msg)
|
|
{
|
|
void *p = (char *)msg + f->offset;
|
|
|
|
switch (f->ltype) {
|
|
case PB_LUA_T_BOOL: {
|
|
bool v;
|
|
if (!lua_isboolean(L, vidx))
|
|
luaL_error(L, "field '%s': expected boolean", f->name);
|
|
v = lua_toboolean(L, vidx) ? true : false;
|
|
memcpy(p, &v, sizeof(v));
|
|
break;
|
|
}
|
|
case PB_LUA_T_INT32: {
|
|
int32_t v;
|
|
if (!lua_isinteger(L, vidx))
|
|
luaL_error(L, "field '%s': expected integer", f->name);
|
|
lua_Integer li = lua_tointeger(L, vidx);
|
|
if (li < (lua_Integer)INT32_MIN || li > (lua_Integer)INT32_MAX)
|
|
luaL_error(L, "field '%s': integer out of int32 range", f->name);
|
|
v = (int32_t)li;
|
|
memcpy(p, &v, sizeof(v));
|
|
break;
|
|
}
|
|
case PB_LUA_T_INT64: {
|
|
int64_t v;
|
|
if (!lua_isinteger(L, vidx))
|
|
luaL_error(L, "field '%s': expected integer", f->name);
|
|
v = (int64_t)lua_tointeger(L, vidx);
|
|
memcpy(p, &v, sizeof(v));
|
|
break;
|
|
}
|
|
case PB_LUA_T_DOUBLE: {
|
|
double v;
|
|
if (!lua_isnumber(L, vidx))
|
|
luaL_error(L, "field '%s': expected number", f->name);
|
|
v = (double)lua_tonumber(L, vidx);
|
|
memcpy(p, &v, sizeof(v));
|
|
break;
|
|
}
|
|
case PB_LUA_T_STRING: {
|
|
size_t len;
|
|
const char *s;
|
|
if (lua_type(L, vidx) != LUA_TSTRING)
|
|
luaL_error(L, "field '%s': expected string", f->name);
|
|
s = lua_tolstring(L, vidx, &len);
|
|
if (memchr(s, 0, len) != NULL)
|
|
luaL_error(L, "field '%s': string contains embedded NUL", f->name);
|
|
if (len >= f->size)
|
|
luaL_error(L, "field '%s': string too long (%u >= %u)",
|
|
f->name, (unsigned)len, (unsigned)f->size);
|
|
memset(p, 0, f->size);
|
|
memcpy(p, s, len);
|
|
break;
|
|
}
|
|
case PB_LUA_T_MESSAGE: {
|
|
if (lua_type(L, vidx) != LUA_TTABLE)
|
|
luaL_error(L, "field '%s': expected table", f->name);
|
|
memset(p, 0, f->size); /* 赋值 = 整体替换子消息(live 路径无预清零) */
|
|
pb_lua_table_to_msg(L, vidx, (const pb_lua_msg_t *)f->aux, p);
|
|
break;
|
|
}
|
|
default:
|
|
luaL_error(L, "field '%s': unsupported protobuf type", f->name);
|
|
break;
|
|
}
|
|
|
|
if (f->has_offset >= 0) {
|
|
bool has = true;
|
|
memcpy((char *)msg + f->has_offset, &has, sizeof(has));
|
|
}
|
|
}
|
|
|
|
static void pb_lua_table_to_msg(lua_State *L, int tidx, const pb_lua_msg_t *m, void *msg)
|
|
{
|
|
tidx = lua_absindex(L, tidx);
|
|
|
|
lua_pushnil(L);
|
|
while (lua_next(L, tidx) != 0) {
|
|
if (lua_type(L, -2) != LUA_TSTRING) {
|
|
luaL_error(L, "%s: field name must be a string", m->name);
|
|
return;
|
|
}
|
|
const char *key = lua_tostring(L, -2);
|
|
const pb_lua_field_t *f = pb_lua_find_field(m, key);
|
|
if (f == NULL) {
|
|
luaL_error(L, "unknown field '%s' in %s", key, m->name);
|
|
return;
|
|
}
|
|
if (lua_isnil(L, -1)) {
|
|
/* 理论上 lua_next 不返回 nil 值,防御处理:视为缺省 */
|
|
lua_pop(L, 1);
|
|
continue;
|
|
}
|
|
pb_lua_set_one(L, -1, f, msg);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
/*======================================================================
|
|
* C struct → table(decode 方向)
|
|
*====================================================================*/
|
|
|
|
static void pb_lua_msg_to_table(lua_State *L, const void *msg, const pb_lua_msg_t *m);
|
|
|
|
static void pb_lua_get_one(lua_State *L, const void *msg, const pb_lua_field_t *f)
|
|
{
|
|
const void *p = (const char *)msg + f->offset;
|
|
|
|
switch (f->ltype) {
|
|
case PB_LUA_T_BOOL: {
|
|
bool v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_pushboolean(L, v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_INT32: {
|
|
int32_t v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_pushinteger(L, (lua_Integer)v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_INT64: {
|
|
int64_t v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_pushinteger(L, (lua_Integer)v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_DOUBLE: {
|
|
double v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_pushnumber(L, (lua_Number)v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_STRING: {
|
|
const char *s = (const char *)p;
|
|
lua_pushlstring(L, s, pb_lua_strnlen(s, f->size > 0 ? f->size - 1 : 0));
|
|
break;
|
|
}
|
|
case PB_LUA_T_MESSAGE: {
|
|
pb_lua_msg_to_table(L, p, (const pb_lua_msg_t *)f->aux);
|
|
break;
|
|
}
|
|
default:
|
|
lua_pushnil(L);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* OPTIONAL 且 has==false 的字段不进 table(nil = 缺省);SINGULAR 恒进 */
|
|
static int pb_lua_field_present(const void *msg, const pb_lua_field_t *f)
|
|
{
|
|
if (f->has_offset < 0)
|
|
return 1;
|
|
bool has;
|
|
memcpy(&has, (const char *)msg + f->has_offset, sizeof(has));
|
|
return has ? 1 : 0;
|
|
}
|
|
|
|
static void pb_lua_msg_to_table(lua_State *L, const void *msg, const pb_lua_msg_t *m)
|
|
{
|
|
lua_createtable(L, (int)m->nfields, 0);
|
|
for (size_t i = 0; i < m->nfields; i++) {
|
|
const pb_lua_field_t *f = &m->fields[i];
|
|
if (!pb_lua_field_present(msg, f))
|
|
continue;
|
|
pb_lua_get_one(L, msg, f);
|
|
lua_setfield(L, -2, f->name);
|
|
}
|
|
}
|
|
|
|
/*======================================================================
|
|
* dump:解析显示
|
|
*====================================================================*/
|
|
|
|
static void pb_lua_dump_indent(int depth)
|
|
{
|
|
for (int i = 0; i < depth; i++)
|
|
lua_print(" ");
|
|
}
|
|
|
|
static void pb_lua_dump_value(lua_State *L, int vidx, const pb_lua_field_t *f, int depth);
|
|
|
|
/* 递归打印 C 结构字段(不含外层花括号头) */
|
|
static void pb_lua_dump_msg_fields(const void *msg, const pb_lua_msg_t *m, int depth)
|
|
{
|
|
for (size_t i = 0; i < m->nfields; i++) {
|
|
const pb_lua_field_t *f = &m->fields[i];
|
|
if (!pb_lua_field_present(msg, f))
|
|
continue;
|
|
pb_lua_dump_indent(depth);
|
|
if (f->ltype == PB_LUA_T_MESSAGE) {
|
|
lua_print("%s {\n", f->name);
|
|
pb_lua_dump_msg_fields((const char *)msg + f->offset,
|
|
(const pb_lua_msg_t *)f->aux, depth + 1);
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("}\n");
|
|
}
|
|
else {
|
|
lua_print("%s = ", f->name);
|
|
const void *p = (const char *)msg + f->offset;
|
|
switch (f->ltype) {
|
|
case PB_LUA_T_BOOL: {
|
|
bool v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_print("%s", v ? "true" : "false");
|
|
break;
|
|
}
|
|
case PB_LUA_T_INT32: {
|
|
int32_t v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_print("%d", v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_INT64: {
|
|
int64_t v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_print("%lld", (long long)v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_DOUBLE: {
|
|
double v;
|
|
memcpy(&v, p, sizeof(v));
|
|
lua_print("%g", v);
|
|
break;
|
|
}
|
|
case PB_LUA_T_STRING: {
|
|
const char *s = (const char *)p;
|
|
lua_print("\"%s\"", s);
|
|
break;
|
|
}
|
|
default:
|
|
lua_print("?");
|
|
break;
|
|
}
|
|
lua_print("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pb_lua_dump_msg(const void *msg, const pb_lua_msg_t *m, int depth)
|
|
{
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("%s {\n", m->name);
|
|
pb_lua_dump_msg_fields(msg, m, depth + 1);
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("}\n");
|
|
}
|
|
|
|
/* 按 table 打印字段(不含外层花括号头,多余键标 [unknown] 置后) */
|
|
static void pb_lua_dump_table_fields(lua_State *L, int tidx, const pb_lua_msg_t *m, int depth)
|
|
{
|
|
tidx = lua_absindex(L, tidx);
|
|
|
|
for (size_t i = 0; i < m->nfields; i++) {
|
|
const pb_lua_field_t *f = &m->fields[i];
|
|
lua_getfield(L, tidx, f->name);
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
continue;
|
|
}
|
|
pb_lua_dump_indent(depth);
|
|
if (f->ltype == PB_LUA_T_MESSAGE) {
|
|
lua_print("%s {\n", f->name);
|
|
pb_lua_dump_table_fields(L, -1, (const pb_lua_msg_t *)f->aux, depth + 1);
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("}\n");
|
|
lua_pop(L, 1);
|
|
}
|
|
else {
|
|
lua_print("%s = ", f->name);
|
|
pb_lua_dump_value(L, -1, f, depth + 1);
|
|
lua_print("\n");
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
/* 多余键 */
|
|
lua_pushnil(L);
|
|
while (lua_next(L, tidx) != 0) {
|
|
if (lua_type(L, -2) == LUA_TSTRING) {
|
|
const char *key = lua_tostring(L, -2);
|
|
if (pb_lua_find_field(m, key) == NULL) {
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("%s = ", key);
|
|
if (lua_type(L, -1) == LUA_TSTRING)
|
|
lua_print("\"%s\"", lua_tostring(L, -1));
|
|
else if (lua_isinteger(L, -1))
|
|
lua_print("%d", lua_tointeger(L, -1));
|
|
else if (lua_isnumber(L, -1))
|
|
lua_print("%g", (double)lua_tonumber(L, -1));
|
|
else if (lua_isboolean(L, -1))
|
|
lua_print("%s", lua_toboolean(L, -1) ? "true" : "false");
|
|
else
|
|
lua_print("%s", lua_typename(L, lua_type(L, -1)));
|
|
lua_print(" [unknown]\n");
|
|
}
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
static void pb_lua_dump_table(lua_State *L, int tidx, const pb_lua_msg_t *m, int depth)
|
|
{
|
|
tidx = lua_absindex(L, tidx);
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("%s {\n", m->name);
|
|
pb_lua_dump_table_fields(L, tidx, m, depth + 1);
|
|
pb_lua_dump_indent(depth);
|
|
lua_print("}\n");
|
|
}
|
|
|
|
static void pb_lua_dump_value(lua_State *L, int vidx, const pb_lua_field_t *f, int depth)
|
|
{
|
|
(void)depth;
|
|
switch (f->ltype) {
|
|
case PB_LUA_T_BOOL:
|
|
lua_print("%s", lua_toboolean(L, vidx) ? "true" : "false");
|
|
break;
|
|
case PB_LUA_T_INT32:
|
|
case PB_LUA_T_INT64:
|
|
if (lua_isinteger(L, vidx))
|
|
lua_print("%d", lua_tointeger(L, vidx));
|
|
else
|
|
lua_print("%g", (double)lua_tonumber(L, vidx));
|
|
break;
|
|
case PB_LUA_T_DOUBLE:
|
|
lua_print("%g", (double)lua_tonumber(L, vidx));
|
|
break;
|
|
case PB_LUA_T_STRING:
|
|
lua_print("\"%s\"", lua_tostring(L, vidx));
|
|
break;
|
|
default:
|
|
lua_print("%s", lua_typename(L, lua_type(L, vidx)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void pb_lua_dump_hex(const pb_byte_t *buf, size_t len)
|
|
{
|
|
size_t n = len > 64 ? 64 : len;
|
|
lua_print("hex:");
|
|
for (size_t i = 0; i < n; i++)
|
|
lua_print(" %02X", (unsigned)buf[i]);
|
|
if (len > n)
|
|
lua_print(" ...");
|
|
lua_print("\n");
|
|
}
|
|
|
|
/*======================================================================
|
|
* live userdata(PB_LIVE):C 全局/结构体指针的可写视图
|
|
*====================================================================*/
|
|
|
|
typedef struct {
|
|
void *ptr; /* 借用指针,不拥有内存(无 __gc) */
|
|
const pb_lua_msg_t *msg;
|
|
} pb_lua_live_t;
|
|
|
|
#define PB_LIVE_MT "PB_LIVE"
|
|
|
|
static int l_live_index(lua_State *L);
|
|
static int l_live_newindex(lua_State *L);
|
|
static int l_live_tostring(lua_State *L);
|
|
static int l_live_dump(lua_State *L);
|
|
static int l_live_encode(lua_State *L);
|
|
static int l_live_totable(lua_State *L);
|
|
|
|
static void pb_lua_live_open(lua_State *L)
|
|
{
|
|
if (!luaL_newmetatable(L, PB_LIVE_MT)) {
|
|
lua_pop(L, 1);
|
|
return;
|
|
}
|
|
lua_pushcfunction(L, l_live_index); lua_setfield(L, -2, "__index");
|
|
lua_pushcfunction(L, l_live_newindex); lua_setfield(L, -2, "__newindex");
|
|
lua_pushcfunction(L, l_live_tostring); lua_setfield(L, -2, "__tostring");
|
|
lua_pushcfunction(L, l_live_dump); lua_setfield(L, -2, "dump");
|
|
lua_pushcfunction(L, l_live_encode); lua_setfield(L, -2, "encode");
|
|
lua_pushcfunction(L, l_live_totable); lua_setfield(L, -2, "totable");
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static void pb_lua_push_live(lua_State *L, void *ptr, const pb_lua_msg_t *m)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)lua_newuserdata(L, sizeof *u);
|
|
u->ptr = ptr;
|
|
u->msg = m;
|
|
pb_lua_live_open(L); /* 确保 metatable 已注册(会 pop 掉) */
|
|
luaL_getmetatable(L, PB_LIVE_MT); /* 重新从 registry 取出 */
|
|
lua_setmetatable(L, -2);
|
|
}
|
|
|
|
/* 字段优先,方法 fallback;未知键报错(与 table 路径同文案)
|
|
* MESSAGE 字段: Mutable 语义 — 始终返回嵌套 live 视图, 首次进入置 has_
|
|
* (否则 p.Sub.Field = x 在零初始化上必炸; 与 C++ MutableMessage 对齐)。 */
|
|
static int l_live_index(lua_State *L)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT);
|
|
const char *key = luaL_checkstring(L, 2);
|
|
const pb_lua_field_t *f = pb_lua_find_field(u->msg, key);
|
|
|
|
if (f != NULL) {
|
|
if (f->ltype == PB_LUA_T_MESSAGE) {
|
|
/* 嵌套消息可导航; 首次进入置 has_, encode 才会带上 */
|
|
if (f->has_offset >= 0) {
|
|
bool has;
|
|
memcpy(&has, (char *)u->ptr + f->has_offset, sizeof(has));
|
|
if (!has) {
|
|
has = true;
|
|
memcpy((char *)u->ptr + f->has_offset, &has, sizeof(has));
|
|
}
|
|
}
|
|
pb_lua_push_live(L, (char *)u->ptr + f->offset,
|
|
(const pb_lua_msg_t *)f->aux);
|
|
return 1;
|
|
}
|
|
if (!pb_lua_field_present(u->ptr, f)) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
pb_lua_get_one(L, u->ptr, f);
|
|
return 1;
|
|
}
|
|
|
|
lua_getmetatable(L, 1);
|
|
lua_getfield(L, -1, key);
|
|
if (!lua_isnil(L, -1))
|
|
return 1;
|
|
return luaL_error(L, "unknown field '%s' in %s", key, u->msg->name);
|
|
}
|
|
|
|
/* nil → 清 OPTIONAL 的 has_(SINGULAR 报错);其余走 set_one(自动置 has_=true) */
|
|
static int l_live_newindex(lua_State *L)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT);
|
|
const char *key = luaL_checkstring(L, 2);
|
|
const pb_lua_field_t *f = pb_lua_find_field(u->msg, key);
|
|
|
|
if (f == NULL)
|
|
return luaL_error(L, "unknown field '%s' in %s", key, u->msg->name);
|
|
|
|
if (lua_isnil(L, 3)) {
|
|
bool has = false;
|
|
if (f->has_offset < 0)
|
|
return luaL_error(L, "field '%s' in %s: SINGULAR field cannot be cleared",
|
|
key, u->msg->name);
|
|
memcpy((char *)u->ptr + f->has_offset, &has, sizeof(has));
|
|
return 0;
|
|
}
|
|
|
|
pb_lua_set_one(L, 3, f, u->ptr);
|
|
return 0;
|
|
}
|
|
|
|
static int l_live_tostring(lua_State *L)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT);
|
|
lua_pushfstring(L, "PB_LIVE(%s): %p", u->msg->name, u->ptr);
|
|
return 1;
|
|
}
|
|
|
|
static int l_live_dump(lua_State *L)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT);
|
|
pb_lua_dump_msg(u->ptr, u->msg, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* 直接从 C 内存编码,不经 table 往返 */
|
|
static int l_live_encode(lua_State *L)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT);
|
|
|
|
if (u->msg->c_size > PB_LUA_MAX_STRUCT || u->msg->max_encoded > PB_LUA_MAX_ENCODED)
|
|
return luaL_error(L, "%s: message too large for static buffer", u->msg->name);
|
|
|
|
pb_byte_t buf[PB_LUA_MAX_ENCODED];
|
|
pb_ostream_t os = pb_ostream_from_buffer(buf, u->msg->max_encoded);
|
|
if (!pb_encode(&os, u->msg->msg, u->ptr))
|
|
return luaL_error(L, "%s encode failed: %s", u->msg->name, PB_GET_ERROR(&os));
|
|
|
|
lua_pushlstring(L, (const char *)buf, os.bytes_written);
|
|
return 1;
|
|
}
|
|
|
|
static int l_live_totable(lua_State *L)
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT);
|
|
pb_lua_msg_to_table(L, u->ptr, u->msg);
|
|
return 1;
|
|
}
|
|
|
|
int l_pb_ref(lua_State *L)
|
|
{
|
|
const char *name = luaL_checkstring(L, 1);
|
|
for (size_t i = 0; i < g_pb_nbindings; i++) {
|
|
if (strcmp(g_pb_bindings[i].name, name) == 0) {
|
|
pb_lua_push_live(L, g_pb_bindings[i].ptr, g_pb_bindings[i].msg);
|
|
return 1;
|
|
}
|
|
}
|
|
return luaL_error(L, "unknown pb global '%s'", name);
|
|
}
|
|
|
|
void pb_lua_open(lua_State *L)
|
|
{
|
|
pb_lua_live_open(L);
|
|
if (pb_lua_bind("CV", &g_stCV, "CV_struct_define") != 0)
|
|
luaL_error(L, "pb_lua_open: bind failed");
|
|
}
|
|
|
|
/*======================================================================
|
|
* Lua 全局函数
|
|
*====================================================================*/
|
|
|
|
int l_pb_encode(lua_State *L)
|
|
{
|
|
const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1));
|
|
luaL_checktype(L, 2, LUA_TTABLE);
|
|
|
|
if (m->c_size > PB_LUA_MAX_STRUCT || m->max_encoded > PB_LUA_MAX_ENCODED)
|
|
return luaL_error(L, "%s: message too large for static buffer", m->name);
|
|
|
|
char msg[PB_LUA_MAX_STRUCT];
|
|
memset(msg, 0, m->c_size);
|
|
pb_lua_table_to_msg(L, 2, m, msg);
|
|
|
|
pb_byte_t buf[PB_LUA_MAX_ENCODED];
|
|
pb_ostream_t os = pb_ostream_from_buffer(buf, m->max_encoded);
|
|
if (!pb_encode(&os, m->msg, msg))
|
|
return luaL_error(L, "%s encode failed: %s", m->name, PB_GET_ERROR(&os));
|
|
|
|
lua_pushlstring(L, (const char *)buf, os.bytes_written);
|
|
return 1;
|
|
}
|
|
|
|
int l_pb_decode(lua_State *L)
|
|
{
|
|
const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1));
|
|
size_t len;
|
|
const pb_byte_t *data = (const pb_byte_t *)luaL_checklstring(L, 2, &len);
|
|
|
|
if (m->c_size > PB_LUA_MAX_STRUCT)
|
|
return luaL_error(L, "%s: message too large for static buffer", m->name);
|
|
|
|
char msg[PB_LUA_MAX_STRUCT];
|
|
memset(msg, 0, m->c_size);
|
|
|
|
pb_istream_t is = pb_istream_from_buffer(data, len);
|
|
if (!pb_decode(&is, m->msg, msg))
|
|
return luaL_error(L, "%s decode failed: %s", m->name, PB_GET_ERROR(&is));
|
|
|
|
pb_lua_msg_to_table(L, msg, m);
|
|
return 1;
|
|
}
|
|
|
|
int l_pb_dump(lua_State *L)
|
|
{
|
|
const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1));
|
|
|
|
{
|
|
pb_lua_live_t *u = (pb_lua_live_t *)luaL_testudata(L, 2, PB_LIVE_MT);
|
|
if (u != NULL) {
|
|
if (u->msg != m)
|
|
return luaL_error(L, "pb_dump: type mismatch: %s vs %s",
|
|
m->name, u->msg->name);
|
|
pb_lua_dump_msg(u->ptr, u->msg, 0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (lua_type(L, 2) == LUA_TSTRING) {
|
|
size_t len;
|
|
const pb_byte_t *data = (const pb_byte_t *)lua_tolstring(L, 2, &len);
|
|
|
|
if (m->c_size > PB_LUA_MAX_STRUCT) {
|
|
lua_print("pb_dump: %s too large\n", m->name);
|
|
return 0;
|
|
}
|
|
char msg[PB_LUA_MAX_STRUCT];
|
|
memset(msg, 0, m->c_size);
|
|
|
|
pb_istream_t is = pb_istream_from_buffer(data, len);
|
|
if (!pb_decode(&is, m->msg, msg)) {
|
|
lua_print("pb_dump: %s decode failed: %s\n", m->name, PB_GET_ERROR(&is));
|
|
pb_lua_dump_hex(data, len);
|
|
return 0;
|
|
}
|
|
pb_lua_dump_msg(msg, m, 0);
|
|
}
|
|
else if (lua_type(L, 2) == LUA_TTABLE) {
|
|
pb_lua_dump_table(L, 2, m, 0);
|
|
}
|
|
else {
|
|
return luaL_error(L, "pb_dump: expected binary string, table, or pb_ref userdata");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* 全零模板:所有字段都置初值(嵌套消息递归展开),方便直接填值编码发送。
|
|
* 字段留在表里即视为 present;不想发到线上的字段设为 nil 即可。 */
|
|
static void pb_lua_new_table(lua_State *L, const pb_lua_msg_t *m)
|
|
{
|
|
lua_createtable(L, (int)m->nfields, 0);
|
|
for (size_t i = 0; i < m->nfields; i++) {
|
|
const pb_lua_field_t *f = &m->fields[i];
|
|
switch (f->ltype) {
|
|
case PB_LUA_T_BOOL:
|
|
lua_pushboolean(L, 0);
|
|
break;
|
|
case PB_LUA_T_INT32:
|
|
case PB_LUA_T_INT64:
|
|
lua_pushinteger(L, 0);
|
|
break;
|
|
case PB_LUA_T_DOUBLE:
|
|
lua_pushnumber(L, 0);
|
|
break;
|
|
case PB_LUA_T_STRING:
|
|
lua_pushstring(L, "");
|
|
break;
|
|
case PB_LUA_T_MESSAGE:
|
|
pb_lua_new_table(L, (const pb_lua_msg_t *)f->aux);
|
|
break;
|
|
default:
|
|
lua_pushnil(L);
|
|
break;
|
|
}
|
|
lua_setfield(L, -2, f->name);
|
|
}
|
|
}
|
|
|
|
int l_pb_new(lua_State *L)
|
|
{
|
|
const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1));
|
|
pb_lua_new_table(L, m);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|