wifi-tally_Oostendam/nodemcu-firmware/app/modules/sjson.c

1044 lines
27 KiB
C
Raw Permalink Normal View History

2021-09-27 19:52:27 +00:00
#define LUA_LIB
#include "lauxlib.h"
#ifndef LOCAL_LUA
#include "module.h"
#include <string.h>
#include <math.h>
#include <limits.h>
#endif
#include "sjson/json_config.h"
#include "sjson/jsonsl.h"
#define LUA_SJSONLIBNAME "sjson"
#define DEFAULT_DEPTH 20
#define DBG_PRINTF(...)
#define SJSON_FLOAT_FMT ((sizeof(lua_Float) == 8) ? "%.19g" : "%.9g")
typedef struct {
jsonsl_t jsn;
int result_ref;
int hkey_ref;
int null_ref;
int metatable;
int pos_ref;
uint8_t complete;
const char *error;
lua_State *L;
size_t min_needed;
size_t min_available;
size_t buffer_len;
const char *buffer; // Points into buffer_ref
int buffer_ref;
} JSN_DATA;
#define get_parent_object_ref() ((state->level == 1) ? data->result_ref : state[-1].lua_object_ref)
#define get_parent_object_used_count_pre_inc() ((state->level == 1) ? 1 : ++state[-1].used_count)
static const char* get_state_buffer(JSN_DATA *ctx, struct jsonsl_state_st *state)
{
size_t offset = state->pos_begin - ctx->min_available;
return ctx->buffer + offset;
}
// The elem data is a ref
static int error_callback(jsonsl_t jsn,
jsonsl_error_t err,
struct jsonsl_state_st *state,
char *at)
{
JSN_DATA *data = (JSN_DATA *) jsn->data;
if (!data->complete) {
data->error = jsonsl_strerror(err);
}
//fprintf(stderr, "Got error at pos %lu: %s\n", jsn->pos, jsonsl_strerror(err));
return 0;
}
static void
create_table(JSN_DATA *data) {
lua_newtable(data->L);
if (data->metatable != LUA_NOREF) {
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->metatable);
lua_setmetatable(data->L, -2);
}
}
static void
create_new_element(jsonsl_t jsn,
jsonsl_action_t action,
struct jsonsl_state_st *state,
const char *buf)
{
JSN_DATA *data = jsn->data;
DBG_PRINTF("L%d: new action %d @ %d state->type %s\n", state->level, action, state->pos_begin, jsonsl_strtype(state->type));
DBG_PRINTF("buf: '%s' ('%.10s')\n", buf, get_state_buffer(data, state));
state->lua_object_ref = LUA_NOREF;
switch(state->type) {
case JSONSL_T_SPECIAL:
case JSONSL_T_STRING:
case JSONSL_T_HKEY:
break;
case JSONSL_T_LIST:
case JSONSL_T_OBJECT:
create_table(data);
state->lua_object_ref = luaL_ref(data->L, LUA_REGISTRYINDEX);
state->used_count = 0;
lua_rawgeti(data->L, LUA_REGISTRYINDEX, get_parent_object_ref());
if (data->hkey_ref == LUA_NOREF) {
// list, so append
lua_pushinteger(data->L, get_parent_object_used_count_pre_inc());
DBG_PRINTF("Adding array element\n");
} else {
// object, so
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->hkey_ref);
luaL_unref(data->L, LUA_REGISTRYINDEX, data->hkey_ref);
data->hkey_ref = LUA_NOREF;
DBG_PRINTF("Adding hash element\n");
}
if (data->pos_ref != LUA_NOREF && state->level > 1) {
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->pos_ref);
lua_pushinteger(data->L, state->level - 1);
lua_pushvalue(data->L, -3); // get the key
lua_settable(data->L, -3);
lua_pop(data->L, 1);
}
// At this point, the stack:
// top: index/hash key
// : table
int want_value = 1;
// Invoke the checkpath method if possible
if (data->pos_ref != LUA_NOREF) {
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->metatable);
lua_getfield(data->L, -1, "checkpath");
if (!lua_isnil(data->L, -1)) {
// Call with the new table and the path as arguments
lua_rawgeti(data->L, LUA_REGISTRYINDEX, state->lua_object_ref);
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->pos_ref);
lua_call(data->L, 2, 1);
want_value = lua_toboolean(data->L, -1);
}
lua_pop(data->L, 2); // Discard the metatable and either the getfield result or retval
}
if (want_value) {
lua_rawgeti(data->L, LUA_REGISTRYINDEX, state->lua_object_ref);
lua_settable(data->L, -3);
lua_pop(data->L, 1); // the table
} else {
lua_pop(data->L, 2); // the index and table
}
break;
default:
DBG_PRINTF("Unhandled type %c\n", state->type);
luaL_error(data->L, "Unhandled type");
break;
}
data->min_needed = state->pos_begin;
}
static void push_number(JSN_DATA *data, struct jsonsl_state_st *state) {
lua_pushlstring(data->L, get_state_buffer(data, state), state->pos_cur - state->pos_begin);
#if LUA_VERSION_NUM == 501
lua_pushnumber(data->L, lua_tonumber(data->L, -1));
#else
if (!lua_stringtonumber(data->L, lua_tostring(data->L, -1))) {
// In this case stringtonumber does not push a value
luaL_error(data->L, "Invalid number");
}
#endif
lua_remove(data->L, -2);
}
static int fromhex(char c) {
if (c <= '9') {
return c & 0xf;
}
return ((c - 'A' + 10) & 0xf);
}
static void output_utf8(luaL_Buffer *buf, int c) {
char space[4];
char *b = space;
if (c<0x80) *b++=c;
else if (c<0x800) *b++=192+c/64, *b++=128+c%64;
else if (c-0xd800u<0x800) *b++ = '?';
else if (c<0x10000) *b++=224+c/4096, *b++=128+c/64%64, *b++=128+c%64;
else if (c<0x110000) *b++=240+c/262144, *b++=128+c/4096%64, *b++=128+c/64%64, *b++=128+c%64;
else *b++ = '?';
luaL_addlstring(buf, space, b - space);
}
static void push_string(JSN_DATA *data, struct jsonsl_state_st *state) {
luaL_Buffer b;
luaL_buffinit(data->L, &b);
int i;
const char *c = get_state_buffer(data, state) + 1;
for (i = 0; i < state->pos_cur - state->pos_begin - 1; i++) {
int nc = c[i];
if (nc == '\\') {
i++;
nc = c[i] & 255;
switch (c[i]) {
case 'b':
nc = '\b';
break;
case 'f':
nc = '\f';
break;
case 'n':
nc = '\n';
break;
case 'r':
nc = '\r';
break;
case 't':
nc = '\t';
break;
case 'u':
nc = fromhex(c[++i]) << 12;
nc += fromhex(c[++i]) << 8;
nc += fromhex(c[++i]) << 4;
nc += fromhex(c[++i]) ;
output_utf8(&b, nc);
continue;
}
}
luaL_addchar(&b, nc);
}
luaL_pushresult(&b);
}
static void
cleanup_closing_element(jsonsl_t jsn,
jsonsl_action_t action,
struct jsonsl_state_st *state,
const char *at)
{
JSN_DATA *data = (JSN_DATA *) jsn->data;
DBG_PRINTF( "L%d: cc action %d state->type %s\n", state->level, action, jsonsl_strtype(state->type));
DBG_PRINTF( "buf (%d - %d): '%.*s'\n", state->pos_begin, state->pos_cur, state->pos_cur - state->pos_begin, get_state_buffer(data, state));
DBG_PRINTF( "at: '%s'\n", at);
switch (state->type) {
case JSONSL_T_HKEY:
push_string(data, state);
data->hkey_ref = luaL_ref(data->L, LUA_REGISTRYINDEX);
break;
case JSONSL_T_STRING:
lua_rawgeti(data->L, LUA_REGISTRYINDEX, get_parent_object_ref());
if (data->hkey_ref == LUA_NOREF) {
// list, so append
lua_pushinteger(data->L, get_parent_object_used_count_pre_inc());
} else {
// object, so
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->hkey_ref);
luaL_unref(data->L, LUA_REGISTRYINDEX, data->hkey_ref);
data->hkey_ref = LUA_NOREF;
}
push_string(data, state);
lua_settable(data->L, -3);
lua_pop(data->L, 1);
break;
case JSONSL_T_SPECIAL:
DBG_PRINTF("Special flags = 0x%x\n", state->special_flags);
// need to deal with true/false/null
if (state->special_flags & (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE|JSONSL_SPECIALf_NUMERIC|JSONSL_SPECIALf_NULL)) {
if (state->special_flags & JSONSL_SPECIALf_TRUE) {
lua_pushboolean(data->L, 1);
} else if (state->special_flags & JSONSL_SPECIALf_FALSE) {
lua_pushboolean(data->L, 0);
} else if (state->special_flags & JSONSL_SPECIALf_NULL) {
DBG_PRINTF("Outputting null\n");
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->null_ref);
} else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) {
push_number(data, state);
}
lua_rawgeti(data->L, LUA_REGISTRYINDEX, get_parent_object_ref());
if (data->hkey_ref == LUA_NOREF) {
// list, so append
lua_pushinteger(data->L, get_parent_object_used_count_pre_inc());
} else {
// object, so
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->hkey_ref);
luaL_unref(data->L, LUA_REGISTRYINDEX, data->hkey_ref);
data->hkey_ref = LUA_NOREF;
}
lua_pushvalue(data->L, -3);
lua_remove(data->L, -4);
lua_settable(data->L, -3);
lua_pop(data->L, 1);
}
break;
case JSONSL_T_OBJECT:
case JSONSL_T_LIST:
luaL_unref(data->L, LUA_REGISTRYINDEX, state->lua_object_ref);
state->lua_object_ref = LUA_NOREF;
if (data->pos_ref != LUA_NOREF) {
lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->pos_ref);
lua_pushinteger(data->L, state->level);
lua_pushnil(data->L);
lua_settable(data->L, -3);
lua_pop(data->L, 1);
}
if (state->level == 1) {
data->complete = 1;
}
break;
}
}
static int sjson_decoder_int(lua_State *L, int argno) {
int nlevels = DEFAULT_DEPTH;
if (lua_type(L, argno) == LUA_TTABLE) {
lua_getfield(L, argno, "depth");
nlevels = lua_tointeger(L, argno);
if (nlevels == 0) {
nlevels = DEFAULT_DEPTH;
}
if (nlevels < 4) {
nlevels = 4;
}
if (nlevels > 1000) {
nlevels = 1000;
}
lua_pop(L, 1);
}
JSN_DATA *data = (JSN_DATA *) lua_newuserdata(L, sizeof(JSN_DATA) + jsonsl_get_size(nlevels));
//
// Associate its metatable
luaL_getmetatable(L, "sjson.decoder");
lua_setmetatable(L, -2);
jsonsl_t jsn = jsonsl_init((jsonsl_t) (data + 1), nlevels);
int i;
for (i = 0; i < jsn->levels_max; i++) {
jsn->stack[i].lua_object_ref = LUA_NOREF;
}
data->jsn = jsn;
data->result_ref = LUA_NOREF;
data->null_ref = LUA_REFNIL;
data->metatable = LUA_NOREF;
data->hkey_ref = LUA_NOREF;
data->pos_ref = LUA_NOREF;
data->buffer_ref = LUA_NOREF;
data->complete = 0;
data->error = NULL;
data->L = L;
data->buffer_len = 0;
data->min_needed = data->min_available = jsn->pos;
lua_pushlightuserdata(L, 0);
data->null_ref = luaL_ref(L, LUA_REGISTRYINDEX);
// This may throw...
lua_newtable(L);
data->result_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (lua_type(L, argno) == LUA_TTABLE) {
luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref);
data->null_ref = LUA_NOREF;
lua_getfield(L, argno, "null");
data->null_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_getfield(L, argno, "metatable");
lua_pushvalue(L, -1);
data->metatable = luaL_ref(L, LUA_REGISTRYINDEX);
if (!lua_isnil(L, -1)) {
lua_getfield(L, -1, "checkpath");
if (!lua_isnil(L, -1)) {
lua_newtable(L);
data->pos_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
lua_pop(L, 1); // Throw away the checkpath value
}
lua_pop(L, 1); // Throw away the metatable
}
jsonsl_enable_all_callbacks(data->jsn);
jsn->action_callback = NULL;
jsn->action_callback_PUSH = create_new_element;
jsn->action_callback_POP = cleanup_closing_element;
jsn->error_callback = error_callback;
jsn->data = data;
jsn->max_callback_level = nlevels;
return 1;
}
static int sjson_decoder(lua_State *L) {
return sjson_decoder_int(L, 1);
}
static int sjson_decoder_result_int(lua_State *L, JSN_DATA *data) {
if (!data->complete) {
luaL_error(L, "decode not complete");
}
lua_rawgeti(L, LUA_REGISTRYINDEX, data->result_ref);
lua_rawgeti(L, -1, 1);
lua_remove(L, -2);
return 1;
}
static int sjson_decoder_result(lua_State *L) {
JSN_DATA *data = (JSN_DATA *)luaL_checkudata(L, 1, "sjson.decoder");
return sjson_decoder_result_int(L, data);
}
static void sjson_free_working_data(lua_State *L, JSN_DATA *data) {
jsonsl_t jsn = data->jsn;
int i;
for (i = 0; i < jsn->levels_max; i++) {
luaL_unref(L, LUA_REGISTRYINDEX, jsn->stack[i].lua_object_ref);
jsn->stack[i].lua_object_ref = LUA_NOREF;
}
luaL_unref(L, LUA_REGISTRYINDEX, data->metatable);
data->metatable = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, data->hkey_ref);
data->hkey_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref);
data->null_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, data->pos_ref);
data->pos_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, data->buffer_ref);
data->buffer_ref = LUA_NOREF;
}
static int sjson_decoder_write_int(lua_State *L, int udata_pos, int string_pos) {
JSN_DATA *data = (JSN_DATA *)luaL_checkudata(L, udata_pos, "sjson.decoder");
size_t len;
const char *str = luaL_checklstring(L, string_pos, &len);
if (data->error) {
luaL_error(L, "JSON parse error: previous call");
}
if (!data->complete) {
data->L = L;
// Merge into any existing buffer and deal with discard
if (data->buffer_ref != LUA_NOREF) {
luaL_Buffer b;
luaL_buffinit(L, &b);
lua_rawgeti(L, LUA_REGISTRYINDEX, data->buffer_ref);
size_t prev_len;
const char *prev_buffer = luaL_checklstring(L, -1, &prev_len);
lua_pop(L, 1); // But string still referenced so it cannot move
int discard = data->min_needed - data->min_available;
prev_buffer += discard;
prev_len -= discard;
if (prev_len > 0) {
luaL_addlstring(&b, prev_buffer, prev_len);
}
data->min_available += discard;
luaL_unref(L, LUA_REGISTRYINDEX, data->buffer_ref);
data->buffer_ref = LUA_NOREF;
lua_pushvalue(L, string_pos);
luaL_addvalue(&b);
luaL_pushresult(&b);
} else {
lua_pushvalue(L, string_pos);
}
size_t blen;
data->buffer = luaL_checklstring(L, -1, &blen);
data->buffer_len = blen;
data->buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
jsonsl_feed(data->jsn, str, len);
if (data->error) {
luaL_error(L, "JSON parse error: %s", data->error);
}
}
if (data->complete) {
// We no longer need the buffer
sjson_free_working_data(L, data);
return sjson_decoder_result_int(L, data);
}
return 0;
}
static int sjson_decoder_write(lua_State *L) {
return sjson_decoder_write_int(L, 1, 2);
}
static int sjson_decode(lua_State *L) {
int push_count = sjson_decoder_int(L, 2);
if (push_count != 1) {
luaL_error(L, "Internal error in sjson.deocder");
}
luaL_checkudata(L, -1, "sjson.decoder");
push_count = sjson_decoder_write_int(L, -1, 1);
if (push_count != 1) {
luaL_error(L, "Incomplete JSON object passed to sjson.decode");
}
// Now we have two items on the stack -- the udata and the result
lua_remove(L, -2);
return 1;
}
static int sjson_decoder_destructor(lua_State *L) {
JSN_DATA *data = (JSN_DATA *)luaL_checkudata(L, 1, "sjson.decoder");
sjson_free_working_data(L, data);
data->jsn = NULL;
luaL_unref(L, LUA_REGISTRYINDEX, data->result_ref);
data->result_ref = LUA_NOREF;
DBG_PRINTF("Destructor called\n");
return 0;
}
//
//--------------------------------- ENCODER BELOW
//
//
//
//#undef DBG_PRINTF
//#define DBG_PRINTF printf
typedef struct {
int lua_object_ref;
// for arrays
// 0 -> [
// 1 -> first element
// 2 -> ,
// 3 -> second element
// 4 -> ]
// for objects
// 0 -> { firstkey :
// 1 -> first value
// 2 -> , secondkey :
// 3 -> second value
// 4 -> }
short offset;
// -1 for objects
// 0 -> n maximum integer key = n
short size;
int lua_key_ref;
} ENC_DATA_STATE;
typedef struct {
ENC_DATA_STATE *stack;
int nlevels;
int level;
int current_str_ref;
int null_ref;
int offset;
} ENC_DATA;
static int sjson_encoder_get_table_size(lua_State *L, int argno) {
// Returns -1 for object, otherwise the maximum integer key value found.
lua_pushvalue(L, argno);
// stack now contains: -1 => table
lua_pushnil(L);
// stack now contains: -1 => nil; -2 => table
//
int maxkey = 0;
while (lua_next(L, -2)) {
lua_pop(L, 1);
// stack now contains: -1 => key; -2 => table
if (lua_type(L, -1) == LUA_TNUMBER) {
int val = lua_tointeger(L, -1);
if (val > maxkey) {
maxkey = val;
} else if (val <= 0) {
maxkey = -1;
lua_pop(L, 1);
break;
}
} else {
maxkey = -1;
lua_pop(L, 1);
break;
}
}
lua_pop(L, 1);
return maxkey;
}
static void enc_pop_stack(lua_State *L, ENC_DATA *data) {
if (data->level < 0) {
luaL_error(L, "encoder stack underflow");
}
ENC_DATA_STATE *state = &data->stack[data->level];
luaL_unref(L, LUA_REGISTRYINDEX, state->lua_object_ref);
state->lua_object_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, state->lua_key_ref);
state->lua_key_ref = LUA_REFNIL;
data->level--;
}
static void enc_push_stack(lua_State *L, ENC_DATA *data, int argno) {
if (++data->level >= data->nlevels) {
luaL_error(L, "encoder stack overflow");
}
lua_pushvalue(L, argno);
ENC_DATA_STATE *state = &data->stack[data->level];
state->lua_object_ref = luaL_ref(L, LUA_REGISTRYINDEX);
state->size = sjson_encoder_get_table_size(L, argno);
state->offset = 0; // We haven't started on this one yet
}
static int sjson_encoder(lua_State *L) {
int nlevels = DEFAULT_DEPTH;
int argno = 1;
// Validate first arg is a table
luaL_checktype(L, argno++, LUA_TTABLE);
if (lua_type(L, argno) == LUA_TTABLE) {
lua_getfield(L, argno, "depth");
nlevels = lua_tointeger(L, argno);
if (nlevels == 0) {
nlevels = DEFAULT_DEPTH;
}
if (nlevels < 4) {
nlevels = 4;
}
if (nlevels > 1000) {
nlevels = 1000;
}
lua_pop(L, 1);
}
ENC_DATA *data = (ENC_DATA *) lua_newuserdata(L, sizeof(ENC_DATA) + nlevels * sizeof(ENC_DATA_STATE));
// Associate its metatable
luaL_getmetatable(L, "sjson.encoder");
lua_setmetatable(L, -2);
data->nlevels = nlevels;
data->level = -1;
data->stack = (ENC_DATA_STATE *) (data + 1);
data->current_str_ref = LUA_NOREF;
int i;
for (i = 0; i < nlevels; i++) {
data->stack[i].lua_object_ref = LUA_NOREF;
data->stack[i].lua_key_ref = LUA_REFNIL;
}
enc_push_stack(L, data, 1);
data->null_ref = LUA_REFNIL;
if (lua_type(L, argno) == LUA_TTABLE) {
luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref);
data->null_ref = LUA_NOREF;
lua_getfield(L, argno, "null");
data->null_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
return 1;
}
static void encode_lua_object(lua_State *L, ENC_DATA *data, int argno, const char *prefix, const char *suffix) {
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addstring(&b, prefix);
int type = lua_type(L, argno);
if (type == LUA_TSTRING) {
// Check to see if it is the NULL value
if (data->null_ref != LUA_REFNIL) {
lua_rawgeti(L, LUA_REGISTRYINDEX, data->null_ref);
if (lua_equal(L, -1, -2)) {
type = LUA_TNIL;
}
lua_pop(L, 1);
}
}
switch (type) {
default:
luaL_error(L, "Cannot encode type %d", type);
break;
case LUA_TLIGHTUSERDATA:
case LUA_TNIL:
luaL_addstring(&b, "null");
break;
case LUA_TBOOLEAN:
luaL_addstring(&b, lua_toboolean(L, argno) ? "true" : "false");
break;
case LUA_TNUMBER:
{
lua_pushvalue(L, argno);
char value[50];
#if LUA_VERSION_NUM == 501
#ifdef LUA_NUMBER_INTEGRAL
snprintf(value, sizeof(value), "%d", lua_tointeger(L, -1));
#else
snprintf(value, sizeof(value), SJSON_FLOAT_FMT, lua_tonumber(L, -1));
#endif
#else
if (lua_isinteger(L, -1)) {
snprintf(value, sizeof(value), LUA_INTEGER_FMT, lua_tointeger(L, -1));
} else {
snprintf(value, sizeof(value), SJSON_FLOAT_FMT, lua_tonumber(L, -1));
}
#endif
lua_pop(L, 1);
if (strcmp(value, "-inf") == 0 || strcmp(value, "nan") == 0 || strcmp(value, "inf") == 0) {
luaL_addstring(&b, "null"); // According to ECMA-262 section 24.5.2 Note 4
} else {
luaL_addstring(&b, value);
}
break;
}
case LUA_TSTRING:
{
luaL_addchar(&b, '"');
size_t len;
const char *str = lua_tolstring(L, argno, &len);
while (len > 0) {
if ((*str & 0xff) < 0x20) {
char value[8];
value[0] = '\\';
char *d = value + 1;
switch(*str) {
case '\f':
*d++ = 'f';
break;
case '\n':
*d++ = 'n';
break;
case '\t':
*d++ = 't';
break;
case '\r':
*d++ = 'r';
break;
case '\b':
*d++ = 'b';
break;
default:
*d++ = 'u';
*d++ = '0';
*d++ = '0';
*d++ = "0123456789abcdef"[(*str >> 4) & 0xf];
*d++ = "0123456789abcdef"[(*str ) & 0xf];
break;
}
*d = '\0';
luaL_addstring(&b, value);
} else if (*str == '"') {
luaL_addstring(&b, "\\\"");
} else {
luaL_addchar(&b, *str);
}
str++;
len--;
}
luaL_addchar(&b, '"');
break;
}
}
luaL_addstring(&b, suffix);
luaL_pushresult(&b);
}
static int sjson_encoder_next_value_is_table(lua_State *L) {
int count = 10;
while ((lua_isfunction(L, -1)
) && count-- > 0) {
// call it and use the return value
lua_call(L, 0, 1); // Expecting replacement value
}
return (lua_type(L, -1) == LUA_TTABLE);
}
static void sjson_encoder_make_next_chunk(lua_State *L, ENC_DATA *data) {
if (data->level < 0) {
return;
}
luaL_Buffer b;
luaL_buffinit(L, &b);
// Ending condition
while (data->level >= 0 /* && !b.lvl */) {
ENC_DATA_STATE *state = &data->stack[data->level];
int finished = 0;
if (state->size >= 0) {
if (state->offset == 0) {
// start of object or whatever
luaL_addchar(&b, '[');
}
if (state->offset == state->size << 1) {
luaL_addchar(&b, ']');
finished = 1;
} else if ((state->offset & 1) == 0) {
if (state->offset > 0) {
luaL_addchar(&b, ',');
}
} else {
// output the value
lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_object_ref);
lua_rawgeti(L, -1, (state->offset >> 1) + 1);
if (sjson_encoder_next_value_is_table(L)) {
enc_push_stack(L, data, -1);
lua_pop(L, 2);
state->offset++;
continue;
}
encode_lua_object(L, data, -1, "", "");
lua_remove(L, -2);
lua_remove(L, -2);
luaL_addvalue(&b);
}
state->offset++;
} else {
lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_object_ref);
// stack now contains: -1 => table
lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_key_ref);
// stack now contains: -1 => nil or key; -2 => table
if (lua_next(L, -2)) {
// save the key
if (state->offset & 1) {
luaL_unref(L, LUA_REGISTRYINDEX, state->lua_key_ref);
state->lua_key_ref = LUA_NOREF;
// Duplicate the key
lua_pushvalue(L, -2);
state->lua_key_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
if ((state->offset & 1) == 0) {
// copy the key so that lua_tostring does not modify the original
lua_pushvalue(L, -2);
// stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
// key
lua_tostring(L, -1);
encode_lua_object(L, data, -1, state->offset ? "," : "{", ":");
lua_remove(L, -2);
lua_remove(L, -2);
lua_remove(L, -2);
lua_remove(L, -2);
} else {
if (sjson_encoder_next_value_is_table(L)) {
enc_push_stack(L, data, -1);
lua_pop(L, 3);
state->offset++;
continue;
}
encode_lua_object(L, data, -1, "", "");
lua_remove(L, -2);
lua_remove(L, -2);
lua_remove(L, -2);
}
luaL_addvalue(&b);
} else {
lua_pop(L, 1);
// We have got to the end
luaL_addchar(&b, '}');
finished = 1;
}
state->offset++;
}
if (finished) {
enc_pop_stack(L, data);
}
}
luaL_pushresult(&b);
data->current_str_ref = luaL_ref(L, LUA_REGISTRYINDEX);
data->offset = 0;
}
static int sjson_encoder_read_int(lua_State *L, ENC_DATA *data, int readsize) {
luaL_Buffer b;
luaL_buffinit(L, &b);
size_t len;
do {
// Fill the buffer with (up to) readsize characters
if (data->current_str_ref != LUA_NOREF) {
// this is not allowed
lua_rawgeti(L, LUA_REGISTRYINDEX, data->current_str_ref);
const char *str = lua_tolstring(L, -1, &len);
lua_pop(L, 1); // Note that we still have the string referenced so it can't go away
int amnt = len - data->offset;;
if (amnt > readsize) {
amnt = readsize;
}
luaL_addlstring(&b, str + data->offset, amnt);
data->offset += amnt;
readsize -= amnt;
if (data->offset == len) {
luaL_unref(L, LUA_REGISTRYINDEX, data->current_str_ref);
data->current_str_ref = LUA_NOREF;
}
}
if (readsize > 0) {
// Make the next chunk
sjson_encoder_make_next_chunk(L, data);
}
} while (readsize > 0 && data->current_str_ref != LUA_NOREF);
luaL_pushresult(&b);
lua_tolstring(L, -1, &len);
if (len == 0) {
// we have got to the end
lua_pop(L, 1);
return 0;
}
return 1;
}
static int sjson_encoder_read(lua_State *L) {
ENC_DATA *data = (ENC_DATA *)luaL_checkudata(L, 1, "sjson.encoder");
int readsize = 1024;
if (lua_type(L, 2) == LUA_TNUMBER) {
readsize = lua_tointeger(L, 2);
if (readsize < 1) {
readsize = 1;
}
}
return sjson_encoder_read_int(L, data, readsize);
}
static int sjson_encode(lua_State *L) {
sjson_encoder(L);
ENC_DATA *data = (ENC_DATA *)luaL_checkudata(L, -1, "sjson.encoder");
int rc = sjson_encoder_read_int(L, data, 1000000);
lua_remove(L, -(rc + 1));
return rc;
}
static int sjson_encoder_destructor(lua_State *L) {
ENC_DATA *data = (ENC_DATA *)luaL_checkudata(L, 1, "sjson.encoder");
int i;
for (i = 0; i < data->nlevels; i++) {
luaL_unref(L, LUA_REGISTRYINDEX, data->stack[i].lua_object_ref);
luaL_unref(L, LUA_REGISTRYINDEX, data->stack[i].lua_key_ref);
}
luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref);
luaL_unref(L, LUA_REGISTRYINDEX, data->current_str_ref);
DBG_PRINTF("Destructor called\n");
return 0;
}
LROT_BEGIN(sjson_encoder_map, NULL, LROT_MASK_GC_INDEX)
LROT_FUNCENTRY( __gc, sjson_encoder_destructor )
LROT_TABENTRY( __index, sjson_encoder_map )
LROT_FUNCENTRY( read, sjson_encoder_read )
LROT_END(sjson_encoder_map, NULL, LROT_MASK_GC_INDEX)
LROT_BEGIN(sjson_decoder_map, NULL, LROT_MASK_GC_INDEX)
LROT_FUNCENTRY( __gc, sjson_decoder_destructor )
LROT_TABENTRY( __index, sjson_decoder_map )
LROT_FUNCENTRY( write, sjson_decoder_write )
LROT_FUNCENTRY( result, sjson_decoder_result )
LROT_END(sjson_decoder_map, NULL, LROT_MASK_GC_INDEX)
LROT_BEGIN(sjson, NULL, 0)
LROT_FUNCENTRY( encode, sjson_encode )
LROT_FUNCENTRY( decode, sjson_decode )
LROT_FUNCENTRY( encoder, sjson_encoder )
LROT_FUNCENTRY( decoder, sjson_decoder )
LROT_END(sjson, NULL, 0)
LUALIB_API int luaopen_sjson (lua_State *L) {
luaL_rometatable(L, "sjson.decoder", LROT_TABLEREF(sjson_decoder_map));
luaL_rometatable(L, "sjson.encoder", LROT_TABLEREF(sjson_encoder_map));
return 1;
}
NODEMCU_MODULE(SJSON, "sjson", sjson, luaopen_sjson);