1
0
Fork 0
Aegisub/vendor/luabins/src/load.c

368 lines
7.2 KiB
C

/*
* load.c
* Luabins load code
* See copyright notice in luabins.h
*/
#include <string.h>
#include "luaheaders.h"
#include "luabins.h"
#include "saveload.h"
#include "luainternals.h"
#if 0
#define XSPAM(a) printf a
#else
#define XSPAM(a) (void)0
#endif
#if 0
#define SPAM(a) printf a
#else
#define SPAM(a) (void)0
#endif
typedef struct lbs_LoadState
{
const unsigned char * pos;
size_t unread;
} lbs_LoadState;
static void lbsLS_init(
lbs_LoadState * ls,
const unsigned char * data,
size_t len
)
{
ls->pos = (len > 0) ? data : NULL;
ls->unread = len;
}
#define lbsLS_good(ls) \
((ls)->pos != NULL)
#define lbsLS_unread(ls) \
((ls)->unread)
static unsigned char lbsLS_readbyte(lbs_LoadState * ls)
{
if (lbsLS_good(ls))
{
if (lbsLS_unread(ls) > 0)
{
const unsigned char b = *ls->pos;
++ls->pos;
--ls->unread;
return b;
}
else
{
ls->unread = 0;
ls->pos = NULL;
}
}
return 0;
}
static const unsigned char * lbsLS_eat(lbs_LoadState * ls, size_t len)
{
const unsigned char * result = NULL;
if (lbsLS_good(ls))
{
if (lbsLS_unread(ls) >= len)
{
XSPAM(("* eat: len %u\n", (int)len));
result = ls->pos;
ls->pos += len;
ls->unread -= len;
XSPAM(("* eat: done len %u\n", (int)len));
}
else
{
ls->unread = 0;
ls->pos = NULL;
}
}
return result;
}
static int lbsLS_readbytes(
lbs_LoadState * ls,
unsigned char * buf,
size_t len
)
{
const unsigned char * pos = lbsLS_eat(ls, len);
if (pos != NULL)
{
memcpy(buf, pos, len);
return LUABINS_ESUCCESS;
}
SPAM(("load: Failed to read %lu bytes\n", (unsigned long)len));
return LUABINS_EBADDATA;
}
static int load_value(lua_State * L, lbs_LoadState * ls);
static int load_table(lua_State * L, lbs_LoadState * ls)
{
int array_size = 0;
int hash_size = 0;
unsigned int total_size = 0;
int result = lbsLS_readbytes(ls, (unsigned char *)&array_size, LUABINS_LINT);
if (result == LUABINS_ESUCCESS)
{
result = lbsLS_readbytes(ls, (unsigned char *)&hash_size, LUABINS_LINT);
}
if (result == LUABINS_ESUCCESS)
{
total_size = array_size + hash_size;
/*
SPAM((
"LT SIZE CHECK\n"
"* array_size %d limit 0 .. %d\n"
"* hash_size %d limit >0\n"
"* hash_size bytes %d, limit %d\n"
"* unread %u limit >min_size %u (total_size %u)\n",
array_size, MAXASIZE,
hash_size,
ceillog2((unsigned int)hash_size), MAXBITS,
(unsigned int)lbsLS_unread(ls),
(unsigned int)luabins_min_table_data_size(total_size),
(unsigned int)total_size
));
*/
if (
array_size < 0 || array_size > MAXASIZE ||
hash_size < 0 ||
(hash_size > 0 && ceillog2((unsigned int)hash_size) > MAXBITS) ||
lbsLS_unread(ls) < luabins_min_table_data_size(total_size)
)
{
result = LUABINS_EBADSIZE;
}
}
if (result == LUABINS_ESUCCESS)
{
unsigned int i = 0;
XSPAM((
"* load: creating table a:%d + h:%d = %d\n",
array_size, hash_size, total_size
));
lua_createtable(L, array_size, hash_size);
for (i = 0; i < total_size; ++i)
{
int key_type = LUA_TNONE;
result = load_value(L, ls); /* Load key. */
if (result != LUABINS_ESUCCESS)
{
break;
}
/* Table key can't be nil or NaN */
key_type = lua_type(L, -1);
if (key_type == LUA_TNIL)
{
/* Corrupt data? */
SPAM(("load: nil as key detected\n"));
result = LUABINS_EBADDATA;
break;
}
if (key_type == LUA_TNUMBER)
{
lua_Number key = lua_tonumber(L, -1);
if (luai_numisnan(key))
{
/* Corrupt data? */
SPAM(("load: NaN as key detected\n"));
result = LUABINS_EBADDATA;
break;
}
}
result = load_value(L, ls); /* Load value. */
if (result != LUABINS_ESUCCESS)
{
break;
}
lua_rawset(L, -3);
}
}
return result;
}
static int load_value(lua_State * L, lbs_LoadState * ls)
{
int result = LUABINS_ESUCCESS;
unsigned char type = lbsLS_readbyte(ls);
if (!lbsLS_good(ls))
{
SPAM(("load: Failed to read value type byte\n"));
return LUABINS_EBADDATA;
}
XSPAM(("* load: begin load_value\n"));
luaL_checkstack(L, 1, "load_value");
switch (type)
{
case LUABINS_CNIL:
XSPAM(("* load: nil\n"));
lua_pushnil(L);
break;
case LUABINS_CFALSE:
XSPAM(("* load: false\n"));
lua_pushboolean(L, 0);
break;
case LUABINS_CTRUE:
XSPAM(("* load: true\n"));
lua_pushboolean(L, 1);
break;
case LUABINS_CNUMBER:
{
lua_Number value;
XSPAM(("* load: number\n"));
result = lbsLS_readbytes(ls, (unsigned char *)&value, LUABINS_LNUMBER);
if (result == LUABINS_ESUCCESS)
{
lua_pushnumber(L, value);
}
}
break;
case LUABINS_CSTRING:
{
size_t len = 0;
XSPAM(("* load: string\n"));
result = lbsLS_readbytes(ls, (unsigned char *)&len, LUABINS_LSIZET);
if (result == LUABINS_ESUCCESS)
{
const unsigned char * pos = lbsLS_eat(ls, len);
XSPAM(("* load: string size %u\n", (int)len));
if (pos != NULL)
{
lua_pushlstring(L, (const char *)pos, len);
}
else
{
result = LUABINS_EBADSIZE;
}
}
}
break;
case LUABINS_CTABLE:
XSPAM(("* load: table\n"));
result = load_table(L, ls);
break;
default:
SPAM(("load: Unknown type char 0x%02X found\n", type));
result = LUABINS_EBADDATA;
break;
}
XSPAM(("* load: end load_value\n"));
return result;
}
int luabins_load(
lua_State * L,
const unsigned char * data,
size_t len,
int * count
)
{
lbs_LoadState ls;
int result = LUABINS_ESUCCESS;
unsigned char num_items = 0;
int base = 0;
int i = 0;
base = lua_gettop(L);
lbsLS_init(&ls, data, len);
num_items = lbsLS_readbyte(&ls);
if (!lbsLS_good(&ls))
{
SPAM(("load: failed to read num_items byte\n"));
result = LUABINS_EBADDATA;
}
else if (num_items > LUABINS_MAXTUPLE)
{
SPAM(("load: tuple too large: %d\n", (int)num_items));
result = LUABINS_EBADSIZE;
}
else
{
XSPAM(("* load: tuple size %d\n", (int)num_items));
for (
i = 0;
i < num_items && result == LUABINS_ESUCCESS;
++i
)
{
XSPAM(("* load: loading tuple item %d\n", i));
result = load_value(L, &ls);
}
}
if (result == LUABINS_ESUCCESS && lbsLS_unread(&ls) > 0)
{
SPAM(("load: %lu chars left at tail\n", lbsLS_unread(&ls)));
result = LUABINS_ETAILEFT;
}
if (result == LUABINS_ESUCCESS)
{
*count = num_items;
}
else
{
lua_settop(L, base); /* Discard intermediate results */
switch (result)
{
case LUABINS_EBADDATA:
lua_pushliteral(L, "can't load: corrupt data");
break;
case LUABINS_EBADSIZE:
lua_pushliteral(L, "can't load: corrupt data, bad size");
break;
case LUABINS_ETAILEFT:
lua_pushliteral(L, "can't load: extra data at end");
break;
default: /* Should not happen */
lua_pushliteral(L, "load failed");
break;
}
}
return result;
}