mirror of
https://github.com/azerothcore/mod-ale
synced 2025-11-29 15:38:17 +08:00
Some fixes for TC and changes overall Pass map object to hook via function arguments The map object is no longer stored inside the instance data table. Fix mistake in base64 decoder It was failing whenever it encountered a '=' character, which is completely valid. Make ElunaInstanceAI::Load always load something When it failed to load data, it was leaving nothing on the stack. Since subsequent code expected Load to always load something, this was causing issues. Now, when Load fails to load anything, it just leaves a new empty table on the stack. Also: the error messages for Load have been improved. Modify lua-marshal to allow saving of functions/userdata. Some additional code was needed to save functions due to the inclusion of a reference to _ENV within their upvalues (since Lua 5.2). During encoding, a placeholder is left where the _ENV reference would be. During decoding, a reference to the current _G is swapped with the placeholder. Make ElunaInstanceAI::Load re-initialize if data failed to load. Also improve error messages by not including the raw data. Improve storage format of upvalues Instead of storing the upvalues by name, store by index. A wrapper is still used in case the upvalue is nil, to prevent holes in the upvalues table. A special field in the upvalues table, "E", is used to store the index of the _ENV reference (if there was one). A reference to the current globals table is set as the upvalue upon decoding. Remove wrapping from upvalue storing, instead save amount of upvalues
580 lines
16 KiB
C++
580 lines
16 KiB
C++
/*
|
|
* lmarshal.c
|
|
* A Lua library for serializing and deserializing Lua values
|
|
* Richard Hundt <richardhundt@gmail.com>, Eluna Lua Engine <http://emudevs.com/>
|
|
*
|
|
* License: MIT
|
|
*
|
|
* Copyright (c) 2010 Richard Hundt
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
extern "C" {
|
|
#include "lua.h"
|
|
#include "lualib.h"
|
|
#include "lauxlib.h"
|
|
}
|
|
|
|
#define MAR_TREF 1
|
|
#define MAR_TVAL 2
|
|
#define MAR_TUSR 3
|
|
|
|
#define MAR_CHR 1
|
|
#define MAR_I32 4
|
|
#define MAR_I64 8
|
|
|
|
#define MAR_MAGIC 0x8f
|
|
#define SEEN_IDX 3
|
|
|
|
#define MAR_ENV_IDX_KEY "E"
|
|
#define MAR_NUPS_IDX_KEY "n"
|
|
|
|
typedef struct mar_Buffer {
|
|
size_t size;
|
|
size_t seek;
|
|
size_t head;
|
|
char* data;
|
|
} mar_Buffer;
|
|
|
|
static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx);
|
|
static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx);
|
|
|
|
static void buf_init(lua_State *L, mar_Buffer *buf)
|
|
{
|
|
buf->size = 128;
|
|
buf->seek = 0;
|
|
buf->head = 0;
|
|
if (!(buf->data = (char*)malloc(buf->size))) luaL_error(L, "Out of memory!");
|
|
}
|
|
|
|
static void buf_done(lua_State* L, mar_Buffer *buf)
|
|
{
|
|
free(buf->data);
|
|
}
|
|
|
|
static int buf_write(lua_State* L, const char* str, size_t len, mar_Buffer *buf)
|
|
{
|
|
if (len > UINT32_MAX) luaL_error(L, "buffer too long");
|
|
if (buf->size - buf->head < len) {
|
|
size_t new_size = buf->size << 1;
|
|
size_t cur_head = buf->head;
|
|
while (new_size - cur_head <= len) {
|
|
new_size = new_size << 1;
|
|
}
|
|
if (!(buf->data = (char*)realloc(buf->data, new_size))) {
|
|
luaL_error(L, "Out of memory!");
|
|
}
|
|
buf->size = new_size;
|
|
}
|
|
memcpy(&buf->data[buf->head], str, len);
|
|
buf->head += len;
|
|
return 0;
|
|
}
|
|
|
|
static const char* buf_read(lua_State *L, mar_Buffer *buf, size_t *len)
|
|
{
|
|
if (buf->seek < buf->head) {
|
|
buf->seek = buf->head;
|
|
*len = buf->seek;
|
|
return buf->data;
|
|
}
|
|
*len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static void mar_encode_value(lua_State *L, mar_Buffer *buf, int val, size_t *idx)
|
|
{
|
|
size_t l;
|
|
int val_type = lua_type(L, val);
|
|
lua_pushvalue(L, val);
|
|
|
|
buf_write(L, (const char*)&val_type, MAR_CHR, buf);
|
|
switch (val_type) {
|
|
case LUA_TBOOLEAN: {
|
|
int int_val = lua_toboolean(L, -1);
|
|
buf_write(L, (const char*)&int_val, MAR_CHR, buf);
|
|
break;
|
|
}
|
|
case LUA_TSTRING: {
|
|
const char *str_val = lua_tolstring(L, -1, &l);
|
|
buf_write(L, (const char*)&l, MAR_I32, buf);
|
|
buf_write(L, str_val, l, buf);
|
|
break;
|
|
}
|
|
case LUA_TNUMBER: {
|
|
lua_Number num_val = lua_tonumber(L, -1);
|
|
buf_write(L, (const char*)&num_val, MAR_I64, buf);
|
|
break;
|
|
}
|
|
case LUA_TTABLE: {
|
|
int tag, ref;
|
|
lua_pushvalue(L, -1);
|
|
lua_rawget(L, SEEN_IDX);
|
|
if (!lua_isnil(L, -1)) {
|
|
ref = lua_tointeger(L, -1);
|
|
tag = MAR_TREF;
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&ref, MAR_I32, buf);
|
|
lua_pop(L, 1);
|
|
}
|
|
else {
|
|
mar_Buffer rec_buf;
|
|
lua_pop(L, 1); /* pop nil */
|
|
if (luaL_getmetafield(L, -1, "__persist")) {
|
|
tag = MAR_TUSR;
|
|
|
|
lua_pushvalue(L, -2); /* self */
|
|
lua_call(L, 1, 1);
|
|
if (!lua_isfunction(L, -1)) {
|
|
luaL_error(L, "__persist must return a function");
|
|
}
|
|
|
|
lua_remove(L, -2); /* __persist */
|
|
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -2); /* callback */
|
|
lua_rawseti(L, -2, 1);
|
|
|
|
buf_init(L, &rec_buf);
|
|
mar_encode_table(L, &rec_buf, idx);
|
|
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf);
|
|
buf_write(L, rec_buf.data, rec_buf.head, buf);
|
|
buf_done(L, &rec_buf);
|
|
lua_pop(L, 1);
|
|
}
|
|
else {
|
|
tag = MAR_TVAL;
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_pushinteger(L, (*idx)++);
|
|
lua_rawset(L, SEEN_IDX);
|
|
|
|
lua_pushvalue(L, -1);
|
|
buf_init(L, &rec_buf);
|
|
mar_encode_table(L, &rec_buf, idx);
|
|
lua_pop(L, 1);
|
|
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf);
|
|
buf_write(L, rec_buf.data,rec_buf.head, buf);
|
|
buf_done(L, &rec_buf);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case LUA_TFUNCTION: {
|
|
int tag, ref;
|
|
lua_pushvalue(L, -1);
|
|
lua_rawget(L, SEEN_IDX);
|
|
if (!lua_isnil(L, -1)) {
|
|
ref = lua_tointeger(L, -1);
|
|
tag = MAR_TREF;
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&ref, MAR_I32, buf);
|
|
lua_pop(L, 1);
|
|
}
|
|
else {
|
|
mar_Buffer rec_buf;
|
|
unsigned int i;
|
|
lua_Debug ar;
|
|
lua_pop(L, 1); /* pop nil */
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_getinfo(L, ">nuS", &ar);
|
|
if (ar.what[0] != 'L') {
|
|
luaL_error(L, "attempt to persist a C function '%s'", ar.name);
|
|
}
|
|
tag = MAR_TVAL;
|
|
lua_pushvalue(L, -1);
|
|
lua_pushinteger(L, (*idx)++);
|
|
lua_rawset(L, SEEN_IDX);
|
|
|
|
lua_pushvalue(L, -1);
|
|
buf_init(L, &rec_buf);
|
|
lua_dump(L, (lua_Writer)buf_write, &rec_buf);
|
|
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf);
|
|
buf_write(L, rec_buf.data, rec_buf.head, buf);
|
|
buf_done(L, &rec_buf);
|
|
lua_pop(L, 1);
|
|
|
|
lua_newtable(L);
|
|
for (i = 1; i <= ar.nups; i++) {
|
|
const char* upvalue_name = lua_getupvalue(L, -2, i);
|
|
if (strcmp("_ENV", upvalue_name) == 0) {
|
|
lua_pop(L, 1);
|
|
// Mark where _ENV is expected.
|
|
lua_pushstring(L, MAR_ENV_IDX_KEY);
|
|
lua_pushinteger(L, i);
|
|
lua_rawset(L, -3);
|
|
}
|
|
else {
|
|
lua_rawseti(L, -2, i);
|
|
}
|
|
}
|
|
lua_pushstring(L, MAR_NUPS_IDX_KEY);
|
|
lua_pushnumber(L, ar.nups);
|
|
lua_rawset(L, -3);
|
|
|
|
buf_init(L, &rec_buf);
|
|
mar_encode_table(L, &rec_buf, idx);
|
|
|
|
buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf);
|
|
buf_write(L, rec_buf.data, rec_buf.head, buf);
|
|
buf_done(L, &rec_buf);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case LUA_TUSERDATA: {
|
|
int tag, ref;
|
|
lua_pushvalue(L, -1);
|
|
lua_rawget(L, SEEN_IDX);
|
|
if (!lua_isnil(L, -1)) {
|
|
ref = lua_tointeger(L, -1);
|
|
tag = MAR_TREF;
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&ref, MAR_I32, buf);
|
|
lua_pop(L, 1);
|
|
}
|
|
else {
|
|
mar_Buffer rec_buf;
|
|
lua_pop(L, 1); /* pop nil */
|
|
if (luaL_getmetafield(L, -1, "__persist")) {
|
|
tag = MAR_TUSR;
|
|
|
|
lua_pushvalue(L, -2);
|
|
lua_pushinteger(L, (*idx)++);
|
|
lua_rawset(L, SEEN_IDX);
|
|
|
|
lua_pushvalue(L, -2);
|
|
lua_call(L, 1, 1);
|
|
if (!lua_isfunction(L, -1)) {
|
|
luaL_error(L, "__persist must return a function");
|
|
}
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -2);
|
|
lua_rawseti(L, -2, 1);
|
|
lua_remove(L, -2);
|
|
|
|
buf_init(L, &rec_buf);
|
|
mar_encode_table(L, &rec_buf, idx);
|
|
|
|
buf_write(L, (const char*)&tag, MAR_CHR, buf);
|
|
buf_write(L, (const char*)&rec_buf.head, MAR_I32, buf);
|
|
buf_write(L, rec_buf.data, rec_buf.head, buf);
|
|
buf_done(L, &rec_buf);
|
|
}
|
|
else {
|
|
luaL_error(L, "attempt to encode userdata (no __persist hook)");
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
break;
|
|
}
|
|
case LUA_TNIL: break;
|
|
default:
|
|
luaL_error(L, "invalid value type (%s)", lua_typename(L, val_type));
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx)
|
|
{
|
|
lua_pushnil(L);
|
|
while (lua_next(L, -2) != 0) {
|
|
mar_encode_value(L, buf, -2, idx);
|
|
mar_encode_value(L, buf, -1, idx);
|
|
lua_pop(L, 1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#define mar_incr_ptr(l) \
|
|
if (((*p)-buf)+(ptrdiff_t)(l) > (ptrdiff_t)len) luaL_error(L, "bad code"); (*p) += (l);
|
|
|
|
#define mar_next_len(l,T) \
|
|
if (((*p)-buf)+(ptrdiff_t)sizeof(T) > (ptrdiff_t)len) luaL_error(L, "bad code"); \
|
|
l = *(T*)*p; (*p) += sizeof(T);
|
|
|
|
static void mar_decode_value
|
|
(lua_State *L, const char *buf, size_t len, const char **p, size_t *idx)
|
|
{
|
|
size_t l;
|
|
char val_type = **p;
|
|
mar_incr_ptr(MAR_CHR);
|
|
switch (val_type) {
|
|
case LUA_TBOOLEAN:
|
|
lua_pushboolean(L, *(char*)*p);
|
|
mar_incr_ptr(MAR_CHR);
|
|
break;
|
|
case LUA_TNUMBER:
|
|
lua_pushnumber(L, *(lua_Number*)*p);
|
|
mar_incr_ptr(MAR_I64);
|
|
break;
|
|
case LUA_TSTRING:
|
|
mar_next_len(l, uint32_t);
|
|
lua_pushlstring(L, *p, l);
|
|
mar_incr_ptr(l);
|
|
break;
|
|
case LUA_TTABLE: {
|
|
char tag = *(char*)*p;
|
|
mar_incr_ptr(MAR_CHR);
|
|
if (tag == MAR_TREF) {
|
|
int ref;
|
|
mar_next_len(ref, int);
|
|
lua_rawgeti(L, SEEN_IDX, ref);
|
|
}
|
|
else if (tag == MAR_TVAL) {
|
|
mar_next_len(l, uint32_t);
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -1);
|
|
lua_rawseti(L, SEEN_IDX, (*idx)++);
|
|
mar_decode_table(L, *p, l, idx);
|
|
mar_incr_ptr(l);
|
|
}
|
|
else if (tag == MAR_TUSR) {
|
|
mar_next_len(l, uint32_t);
|
|
lua_newtable(L);
|
|
mar_decode_table(L, *p, l, idx);
|
|
lua_rawgeti(L, -1, 1);
|
|
lua_call(L, 0, 1);
|
|
lua_remove(L, -2);
|
|
lua_pushvalue(L, -1);
|
|
lua_rawseti(L, SEEN_IDX, (*idx)++);
|
|
mar_incr_ptr(l);
|
|
}
|
|
else {
|
|
luaL_error(L, "bad encoded data");
|
|
}
|
|
break;
|
|
}
|
|
case LUA_TFUNCTION: {
|
|
unsigned int nups;
|
|
unsigned int i;
|
|
mar_Buffer dec_buf;
|
|
char tag = *(char*)*p;
|
|
mar_incr_ptr(1);
|
|
if (tag == MAR_TREF) {
|
|
int ref;
|
|
mar_next_len(ref, int);
|
|
lua_rawgeti(L, SEEN_IDX, ref);
|
|
}
|
|
else {
|
|
mar_next_len(l, uint32_t);
|
|
dec_buf.data = (char*)*p;
|
|
dec_buf.size = l;
|
|
dec_buf.head = l;
|
|
dec_buf.seek = 0;
|
|
lua_load(L, (lua_Reader)buf_read, &dec_buf, "=marshal", NULL);
|
|
mar_incr_ptr(l);
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_rawseti(L, SEEN_IDX, (*idx)++);
|
|
|
|
mar_next_len(l, uint32_t);
|
|
lua_newtable(L);
|
|
mar_decode_table(L, *p, l, idx);
|
|
|
|
lua_pushstring(L, MAR_ENV_IDX_KEY);
|
|
lua_rawget(L, -2);
|
|
if (lua_isnumber(L, -1)) {
|
|
lua_pushglobaltable(L);
|
|
lua_rawset(L, -3);
|
|
}
|
|
else {
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
lua_pushstring(L, MAR_NUPS_IDX_KEY);
|
|
lua_rawget(L, -2);
|
|
nups = luaL_checknumber(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
for (i = 1; i <= nups; i++) {
|
|
lua_rawgeti(L, -1, i);
|
|
lua_setupvalue(L, -3, i);
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
mar_incr_ptr(l);
|
|
}
|
|
break;
|
|
}
|
|
case LUA_TUSERDATA: {
|
|
char tag = *(char*)*p;
|
|
mar_incr_ptr(MAR_CHR);
|
|
if (tag == MAR_TREF) {
|
|
int ref;
|
|
mar_next_len(ref, int);
|
|
lua_rawgeti(L, SEEN_IDX, ref);
|
|
}
|
|
else if (tag == MAR_TUSR) {
|
|
mar_next_len(l, uint32_t);
|
|
lua_newtable(L);
|
|
mar_decode_table(L, *p, l, idx);
|
|
lua_rawgeti(L, -1, 1);
|
|
lua_call(L, 0, 1);
|
|
lua_remove(L, -2);
|
|
lua_pushvalue(L, -1);
|
|
lua_rawseti(L, SEEN_IDX, (*idx)++);
|
|
mar_incr_ptr(l);
|
|
}
|
|
else { /* tag == MAR_TVAL */
|
|
lua_pushnil(L);
|
|
}
|
|
break;
|
|
}
|
|
case LUA_TNIL:
|
|
case LUA_TTHREAD:
|
|
lua_pushnil(L);
|
|
break;
|
|
default:
|
|
luaL_error(L, "bad code");
|
|
}
|
|
}
|
|
|
|
static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx)
|
|
{
|
|
const char* p;
|
|
p = buf;
|
|
while (p - buf < (ptrdiff_t)len) {
|
|
mar_decode_value(L, buf, len, &p, idx);
|
|
mar_decode_value(L, buf, len, &p, idx);
|
|
lua_settable(L, -3);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int mar_encode(lua_State* L)
|
|
{
|
|
const unsigned char m = MAR_MAGIC;
|
|
size_t idx, len;
|
|
mar_Buffer buf;
|
|
|
|
if (lua_isnone(L, 1)) {
|
|
lua_pushnil(L);
|
|
}
|
|
if (lua_isnoneornil(L, 2)) {
|
|
lua_newtable(L);
|
|
}
|
|
else if (!lua_istable(L, 2)) {
|
|
luaL_error(L, "bad argument #2 to encode (expected table)");
|
|
}
|
|
lua_settop(L, 2);
|
|
|
|
len = lua_rawlen(L, 2);
|
|
lua_newtable(L);
|
|
for (idx = 1; idx <= len; idx++) {
|
|
lua_rawgeti(L, 2, idx);
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1);
|
|
continue;
|
|
}
|
|
lua_pushinteger(L, idx);
|
|
lua_rawset(L, SEEN_IDX);
|
|
}
|
|
lua_pushvalue(L, 1);
|
|
|
|
buf_init(L, &buf);
|
|
buf_write(L, (const char*)&m, 1, &buf);
|
|
|
|
mar_encode_value(L, &buf, -1, &idx);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
lua_pushlstring(L, buf.data, buf.head);
|
|
|
|
buf_done(L, &buf);
|
|
|
|
lua_remove(L, SEEN_IDX);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int mar_decode(lua_State* L)
|
|
{
|
|
size_t l, idx, len;
|
|
const char *p;
|
|
const char *s = luaL_checklstring(L, 1, &l);
|
|
|
|
if (l < 1) luaL_error(L, "bad header");
|
|
if (*(unsigned char *)s++ != MAR_MAGIC) luaL_error(L, "bad magic");
|
|
l -= 1;
|
|
|
|
if (lua_isnoneornil(L, 2)) {
|
|
lua_newtable(L);
|
|
}
|
|
else if (!lua_istable(L, 2)) {
|
|
luaL_error(L, "bad argument #2 to decode (expected table)");
|
|
}
|
|
lua_settop(L, 2);
|
|
|
|
len = lua_rawlen(L, 2);
|
|
lua_newtable(L);
|
|
for (idx = 1; idx <= len; idx++) {
|
|
lua_rawgeti(L, 2, idx);
|
|
lua_rawseti(L, SEEN_IDX, idx);
|
|
}
|
|
|
|
p = s;
|
|
mar_decode_value(L, s, l, &p, &idx);
|
|
|
|
lua_remove(L, SEEN_IDX);
|
|
lua_remove(L, 2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int mar_clone(lua_State* L)
|
|
{
|
|
mar_encode(L);
|
|
lua_replace(L, 1);
|
|
mar_decode(L);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg R[] =
|
|
{
|
|
{"encode", mar_encode},
|
|
{"decode", mar_decode},
|
|
{"clone", mar_clone},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
int luaopen_marshal(lua_State *L)
|
|
{
|
|
lua_newtable(L);
|
|
luaL_setfuncs(L, R, 0);
|
|
return 1;
|
|
}
|
|
|