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.

775 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));
print_number_fixed3(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))
print_number_fixed3((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
print_number_fixed3((double)lua_tonumber(L, vidx));
break;
case PB_LUA_T_DOUBLE:
print_number_fixed3((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