]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/marshall.c
Add forceloading
[dragonfireclient.git] / src / script / lua_api / marshall.c
1 /*
2 * lmarshal.c
3 * A Lua library for serializing and deserializing Lua values
4 * Richard Hundt <richardhundt@gmail.com>
5 *
6 * License: MIT
7 *
8 * Copyright (c) 2010 Richard Hundt
9 *
10 * Permission is hereby granted, free of charge, to any person
11 * obtaining a copy of this software and associated documentation
12 * files (the "Software"), to deal in the Software without
13 * restriction, including without limitation the rights to use,
14 * copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following
17 * conditions:
18 *
19 * The above copyright notice and this permission notice shall be
20 * included in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29 * OTHER DEALINGS IN THE SOFTWARE.
30 */
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdint.h>
35
36 #include "lua.h"
37 #include "lualib.h"
38 #include "lauxlib.h"
39
40
41 #define MAR_TREF 1
42 #define MAR_TVAL 2
43 #define MAR_TUSR 3
44
45 #define MAR_CHR 1
46 #define MAR_I32 4
47 #define MAR_I64 8
48
49 #define MAR_MAGIC 0x8e
50 #define SEEN_IDX  3
51
52 typedef struct mar_Buffer {
53     size_t size;
54     size_t seek;
55     size_t head;
56     char*  data;
57 } mar_Buffer;
58
59 static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx);
60 static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx);
61
62 static void buf_init(lua_State *L, mar_Buffer *buf)
63 {
64     buf->size = 128;
65     buf->seek = 0;
66     buf->head = 0;
67     if (!(buf->data = malloc(buf->size))) luaL_error(L, "Out of memory!");
68 }
69
70 static void buf_done(lua_State* L, mar_Buffer *buf)
71 {
72     free(buf->data);
73 }
74
75 static int buf_write(lua_State* L, const char* str, size_t len, mar_Buffer *buf)
76 {
77     if (len > UINT32_MAX) luaL_error(L, "buffer too long");
78     if (buf->size - buf->head < len) {
79         size_t new_size = buf->size << 1;
80         size_t cur_head = buf->head;
81         while (new_size - cur_head <= len) {
82             new_size = new_size << 1;
83         }
84         if (!(buf->data = realloc(buf->data, new_size))) {
85             luaL_error(L, "Out of memory!");
86         }
87         buf->size = new_size;
88     }
89     memcpy(&buf->data[buf->head], str, len);
90     buf->head += len;
91     return 0;
92 }
93
94 static const char* buf_read(lua_State *L, mar_Buffer *buf, size_t *len)
95 {
96     if (buf->seek < buf->head) {
97         buf->seek = buf->head;
98         *len = buf->seek;
99         return buf->data;
100     }
101     *len = 0;
102     return NULL;
103 }
104
105 static void mar_encode_value(lua_State *L, mar_Buffer *buf, int val, size_t *idx)
106 {
107     size_t l;
108     int val_type = lua_type(L, val);
109     lua_pushvalue(L, val);
110
111     buf_write(L, (void*)&val_type, MAR_CHR, buf);
112     switch (val_type) {
113     case LUA_TBOOLEAN: {
114         int int_val = lua_toboolean(L, -1);
115         buf_write(L, (void*)&int_val, MAR_CHR, buf);
116         break;
117     }
118     case LUA_TSTRING: {
119         const char *str_val = lua_tolstring(L, -1, &l);
120         buf_write(L, (void*)&l, MAR_I32, buf);
121         buf_write(L, str_val, l, buf);
122         break;
123     }
124     case LUA_TNUMBER: {
125         lua_Number num_val = lua_tonumber(L, -1);
126         buf_write(L, (void*)&num_val, MAR_I64, buf);
127         break;
128     }
129     case LUA_TTABLE: {
130         int tag, ref;
131         lua_pushvalue(L, -1);
132         lua_rawget(L, SEEN_IDX);
133         if (!lua_isnil(L, -1)) {
134             ref = lua_tointeger(L, -1);
135             tag = MAR_TREF;
136             buf_write(L, (void*)&tag, MAR_CHR, buf);
137             buf_write(L, (void*)&ref, MAR_I32, buf);
138             lua_pop(L, 1);
139         }
140         else {
141             mar_Buffer rec_buf;
142             lua_pop(L, 1); /* pop nil */
143             if (luaL_getmetafield(L, -1, "__persist")) {
144                 tag = MAR_TUSR;
145
146                 lua_pushvalue(L, -2); /* self */
147                 lua_call(L, 1, 1);
148                 if (!lua_isfunction(L, -1)) {
149                     luaL_error(L, "__persist must return a function");
150                 }
151
152                 lua_remove(L, -2); /* __persist */
153
154                 lua_newtable(L);
155                 lua_pushvalue(L, -2); /* callback */
156                 lua_rawseti(L, -2, 1);
157
158                 buf_init(L, &rec_buf);
159                 mar_encode_table(L, &rec_buf, idx);
160
161                 buf_write(L, (void*)&tag, MAR_CHR, buf);
162                 buf_write(L, (void*)&rec_buf.head, MAR_I32, buf);
163                 buf_write(L, rec_buf.data, rec_buf.head, buf);
164                 buf_done(L, &rec_buf);
165                 lua_pop(L, 1);
166             }
167             else {
168                 tag = MAR_TVAL;
169
170                 lua_pushvalue(L, -1);
171                 lua_pushinteger(L, (*idx)++);
172                 lua_rawset(L, SEEN_IDX);
173
174                 lua_pushvalue(L, -1);
175                 buf_init(L, &rec_buf);
176                 mar_encode_table(L, &rec_buf, idx);
177                 lua_pop(L, 1);
178
179                 buf_write(L, (void*)&tag, MAR_CHR, buf);
180                 buf_write(L, (void*)&rec_buf.head, MAR_I32, buf);
181                 buf_write(L, rec_buf.data,rec_buf.head, buf);
182                 buf_done(L, &rec_buf);
183             }
184         }
185         break;
186     }
187     case LUA_TFUNCTION: {
188         int tag, ref;
189         lua_pushvalue(L, -1);
190         lua_rawget(L, SEEN_IDX);
191         if (!lua_isnil(L, -1)) {
192             ref = lua_tointeger(L, -1);
193             tag = MAR_TREF;
194             buf_write(L, (void*)&tag, MAR_CHR, buf);
195             buf_write(L, (void*)&ref, MAR_I32, buf);
196             lua_pop(L, 1);
197         }
198         else {
199             mar_Buffer rec_buf;
200             int i;
201             lua_Debug ar;
202             lua_pop(L, 1); /* pop nil */
203
204             lua_pushvalue(L, -1);
205             lua_getinfo(L, ">nuS", &ar);
206             if (ar.what[0] != 'L') {
207                 luaL_error(L, "attempt to persist a C function '%s'", ar.name);
208             }
209             tag = MAR_TVAL;
210             lua_pushvalue(L, -1);
211             lua_pushinteger(L, (*idx)++);
212             lua_rawset(L, SEEN_IDX);
213
214             lua_pushvalue(L, -1);
215             buf_init(L, &rec_buf);
216             lua_dump(L, (lua_Writer)buf_write, &rec_buf);
217
218             buf_write(L, (void*)&tag, MAR_CHR, buf);
219             buf_write(L, (void*)&rec_buf.head, MAR_I32, buf);
220             buf_write(L, rec_buf.data, rec_buf.head, buf);
221             buf_done(L, &rec_buf);
222             lua_pop(L, 1);
223
224             lua_newtable(L);
225             for (i=1; i <= ar.nups; i++) {
226                 lua_getupvalue(L, -2, i);
227                 lua_rawseti(L, -2, i);
228             }
229
230             buf_init(L, &rec_buf);
231             mar_encode_table(L, &rec_buf, idx);
232
233             buf_write(L, (void*)&rec_buf.head, MAR_I32, buf);
234             buf_write(L, rec_buf.data, rec_buf.head, buf);
235             buf_done(L, &rec_buf);
236             lua_pop(L, 1);
237         }
238
239         break;
240     }
241     case LUA_TUSERDATA: {
242         int tag, ref;
243         lua_pushvalue(L, -1);
244         lua_rawget(L, SEEN_IDX);
245         if (!lua_isnil(L, -1)) {
246             ref = lua_tointeger(L, -1);
247             tag = MAR_TREF;
248             buf_write(L, (void*)&tag, MAR_CHR, buf);
249             buf_write(L, (void*)&ref, MAR_I32, buf);
250             lua_pop(L, 1);
251         }
252         else {
253             mar_Buffer rec_buf;
254             lua_pop(L, 1); /* pop nil */
255             if (luaL_getmetafield(L, -1, "__persist")) {
256                 tag = MAR_TUSR;
257
258                 lua_pushvalue(L, -2);
259                 lua_pushinteger(L, (*idx)++);
260                 lua_rawset(L, SEEN_IDX);
261
262                 lua_pushvalue(L, -2);
263                 lua_call(L, 1, 1);
264                 if (!lua_isfunction(L, -1)) {
265                     luaL_error(L, "__persist must return a function");
266                 }
267                 lua_newtable(L);
268                 lua_pushvalue(L, -2);
269                 lua_rawseti(L, -2, 1);
270                 lua_remove(L, -2);
271
272                 buf_init(L, &rec_buf);
273                 mar_encode_table(L, &rec_buf, idx);
274
275                 buf_write(L, (void*)&tag, MAR_CHR, buf);
276                 buf_write(L, (void*)&rec_buf.head, MAR_I32, buf);
277                 buf_write(L, rec_buf.data, rec_buf.head, buf);
278                 buf_done(L, &rec_buf);
279             }
280             else {
281                 luaL_error(L, "attempt to encode userdata (no __persist hook)");
282             }
283             lua_pop(L, 1);
284         }
285         break;
286     }
287     case LUA_TNIL: break;
288     default:
289         luaL_error(L, "invalid value type (%s)", lua_typename(L, val_type));
290     }
291     lua_pop(L, 1);
292 }
293
294 static int mar_encode_table(lua_State *L, mar_Buffer *buf, size_t *idx)
295 {
296     lua_pushnil(L);
297     while (lua_next(L, -2) != 0) {
298         mar_encode_value(L, buf, -2, idx);
299         mar_encode_value(L, buf, -1, idx);
300         lua_pop(L, 1);
301     }
302     return 1;
303 }
304
305 #define mar_incr_ptr(l) \
306     if (((*p)-buf)+(l) > len) luaL_error(L, "bad code"); (*p) += (l);
307
308 #define mar_next_len(l,T) \
309     if (((*p)-buf)+sizeof(T) > len) luaL_error(L, "bad code"); \
310     l = *(T*)*p; (*p) += sizeof(T);
311
312 static void mar_decode_value
313     (lua_State *L, const char *buf, size_t len, const char **p, size_t *idx)
314 {
315     size_t l;
316     char val_type = **p;
317     mar_incr_ptr(MAR_CHR);
318     switch (val_type) {
319     case LUA_TBOOLEAN:
320         lua_pushboolean(L, *(char*)*p);
321         mar_incr_ptr(MAR_CHR);
322         break;
323     case LUA_TNUMBER:
324         lua_pushnumber(L, *(lua_Number*)*p);
325         mar_incr_ptr(MAR_I64);
326         break;
327     case LUA_TSTRING:
328         mar_next_len(l, uint32_t);
329         lua_pushlstring(L, *p, l);
330         mar_incr_ptr(l);
331         break;
332     case LUA_TTABLE: {
333         char tag = *(char*)*p;
334         mar_incr_ptr(MAR_CHR);
335         if (tag == MAR_TREF) {
336             int ref;
337             mar_next_len(ref, int);
338             lua_rawgeti(L, SEEN_IDX, ref);
339         }
340         else if (tag == MAR_TVAL) {
341             mar_next_len(l, uint32_t);
342             lua_newtable(L);
343             lua_pushvalue(L, -1);
344             lua_rawseti(L, SEEN_IDX, (*idx)++);
345             mar_decode_table(L, *p, l, idx);
346             mar_incr_ptr(l);
347         }
348         else if (tag == MAR_TUSR) {
349             mar_next_len(l, uint32_t);
350             lua_newtable(L);
351             mar_decode_table(L, *p, l, idx);
352             lua_rawgeti(L, -1, 1);
353             lua_call(L, 0, 1);
354             lua_remove(L, -2);
355             lua_pushvalue(L, -1);
356             lua_rawseti(L, SEEN_IDX, (*idx)++);
357             mar_incr_ptr(l);
358         }
359         else {
360             luaL_error(L, "bad encoded data");
361         }
362         break;
363     }
364     case LUA_TFUNCTION: {
365         size_t nups;
366         int i;
367         mar_Buffer dec_buf;
368         char tag = *(char*)*p;
369         mar_incr_ptr(1);
370         if (tag == MAR_TREF) {
371             int ref;
372             mar_next_len(ref, int);
373             lua_rawgeti(L, SEEN_IDX, ref);
374         }
375         else {
376             mar_next_len(l, uint32_t);
377             dec_buf.data = (char*)*p;
378             dec_buf.size = l;
379             dec_buf.head = l;
380             dec_buf.seek = 0;
381             lua_load(L, (lua_Reader)buf_read, &dec_buf, "=marshal");
382             mar_incr_ptr(l);
383
384             lua_pushvalue(L, -1);
385             lua_rawseti(L, SEEN_IDX, (*idx)++);
386
387             mar_next_len(l, uint32_t);
388             lua_newtable(L);
389             mar_decode_table(L, *p, l, idx);
390             nups = lua_objlen(L, -1);
391             for (i=1; i <= nups; i++) {
392                 lua_rawgeti(L, -1, i);
393                 lua_setupvalue(L, -3, i);
394             }
395             lua_pop(L, 1);
396             mar_incr_ptr(l);
397         }
398         break;
399     }
400     case LUA_TUSERDATA: {
401         char tag = *(char*)*p;
402         mar_incr_ptr(MAR_CHR);
403         if (tag == MAR_TREF) {
404             int ref;
405             mar_next_len(ref, int);
406             lua_rawgeti(L, SEEN_IDX, ref);
407         }
408         else if (tag == MAR_TUSR) {
409             mar_next_len(l, uint32_t);
410             lua_newtable(L);
411             mar_decode_table(L, *p, l, idx);
412             lua_rawgeti(L, -1, 1);
413             lua_call(L, 0, 1);
414             lua_remove(L, -2);
415             lua_pushvalue(L, -1);
416             lua_rawseti(L, SEEN_IDX, (*idx)++);
417             mar_incr_ptr(l);
418         }
419         else { /* tag == MAR_TVAL */
420             lua_pushnil(L);
421         }
422         break;
423     }
424     case LUA_TNIL:
425     case LUA_TTHREAD:
426         lua_pushnil(L);
427         break;
428     default:
429         luaL_error(L, "bad code");
430     }
431 }
432
433 static int mar_decode_table(lua_State *L, const char* buf, size_t len, size_t *idx)
434 {
435     const char* p;
436     p = buf;
437     while (p - buf < len) {
438         mar_decode_value(L, buf, len, &p, idx);
439         mar_decode_value(L, buf, len, &p, idx);
440         lua_settable(L, -3);
441     }
442     return 1;
443 }
444
445 static int mar_encode(lua_State* L)
446 {
447     const unsigned char m = MAR_MAGIC;
448     size_t idx, len;
449     mar_Buffer buf;
450
451     if (lua_isnone(L, 1)) {
452         lua_pushnil(L);
453     }
454     if (lua_isnoneornil(L, 2)) {
455         lua_newtable(L);
456     }
457     else if (!lua_istable(L, 2)) {
458         luaL_error(L, "bad argument #2 to encode (expected table)");
459     }
460     lua_settop(L, 2);
461
462     len = lua_objlen(L, 2);
463     lua_newtable(L);
464     for (idx = 1; idx <= len; idx++) {
465         lua_rawgeti(L, 2, idx);
466         if (lua_isnil(L, -1)) {
467             lua_pop(L, 1);
468             continue;
469         }
470         lua_pushinteger(L, idx);
471         lua_rawset(L, SEEN_IDX);
472     }
473     lua_pushvalue(L, 1);
474
475     buf_init(L, &buf);
476     buf_write(L, (void*)&m, 1, &buf);
477
478     mar_encode_value(L, &buf, -1, &idx);
479
480     lua_pop(L, 1);
481
482     lua_pushlstring(L, buf.data, buf.head);
483
484     buf_done(L, &buf);
485
486     lua_remove(L, SEEN_IDX);
487
488     return 1;
489 }
490
491 static int mar_decode(lua_State* L)
492 {
493     size_t l, idx, len;
494     const char *p;
495     const char *s = luaL_checklstring(L, 1, &l);
496
497     if (l < 1) luaL_error(L, "bad header");
498     if (*(unsigned char *)s++ != MAR_MAGIC) luaL_error(L, "bad magic");
499     l -= 1;
500
501     if (lua_isnoneornil(L, 2)) {
502         lua_newtable(L);
503     }
504     else if (!lua_istable(L, 2)) {
505         luaL_error(L, "bad argument #2 to decode (expected table)");
506     }
507     lua_settop(L, 2);
508
509     len = lua_objlen(L, 2);
510     lua_newtable(L);
511     for (idx = 1; idx <= len; idx++) {
512         lua_rawgeti(L, 2, idx);
513         lua_rawseti(L, SEEN_IDX, idx);
514     }
515
516     p = s;
517     mar_decode_value(L, s, l, &p, &idx);
518
519     lua_remove(L, SEEN_IDX);
520     lua_remove(L, 2);
521
522     return 1;
523 }
524
525 static int mar_clone(lua_State* L)
526 {
527     mar_encode(L);
528     lua_replace(L, 1);
529     mar_decode(L);
530     return 1;
531 }
532
533 static const luaL_reg R[] =
534 {
535     {"encode",      mar_encode},
536     {"decode",      mar_decode},
537     {"clone",       mar_clone},
538     {NULL,          NULL}
539 };
540
541 int luaopen_marshal(lua_State *L)
542 {
543     lua_newtable(L);
544     luaL_register(L, "marshal", R);
545     return 1;
546 }
547
548
549
550
551