aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pall <mike>2021-08-12 21:10:13 +0200
committerMike Pall <mike>2021-08-12 21:27:58 +0200
commit15ed84bd499b3ecdba9f431f2d24696a313227e4 (patch)
tree637662b1edce646fec939e8bf75685a09e374656
parent983d66b8c5032b421e0f5fe8d39e9930bceb7031 (diff)
downloadluajit-15ed84bd499b3ecdba9f431f2d24696a313227e4.tar.gz
luajit-15ed84bd499b3ecdba9f431f2d24696a313227e4.tar.bz2
luajit-15ed84bd499b3ecdba9f431f2d24696a313227e4.zip
String buffers, part 4a: Add metatable serialization dictionary.
Sponsored by fmad.io.
-rw-r--r--doc/ext_buffer.html45
-rw-r--r--src/lib_buffer.c17
-rw-r--r--src/lj_buf.h3
-rw-r--r--src/lj_gc.c6
-rw-r--r--src/lj_serialize.c83
-rw-r--r--src/lj_serialize.h3
6 files changed, 116 insertions, 41 deletions
diff --git a/doc/ext_buffer.html b/doc/ext_buffer.html
index 2443fc90..63c2efe3 100644
--- a/doc/ext_buffer.html
+++ b/doc/ext_buffer.html
@@ -127,7 +127,7 @@ space.
127<p> 127<p>
128Buffers operate like a FIFO (first-in first-out) data structure. Data 128Buffers operate like a FIFO (first-in first-out) data structure. Data
129can be appended (written) to the end of the buffer and consumed (read) 129can be appended (written) to the end of the buffer and consumed (read)
130from the front of the buffer. These operations can be freely mixed. 130from the front of the buffer. These operations may be freely mixed.
131</p> 131</p>
132<p> 132<p>
133The buffer space that holds the characters is managed automatically 133The buffer space that holds the characters is managed automatically
@@ -199,7 +199,7 @@ may be reused.
199<h3 id="buffer_free"><tt>buf = buf:free()</tt></h3> 199<h3 id="buffer_free"><tt>buf = buf:free()</tt></h3>
200<p> 200<p>
201The buffer space of the buffer object is freed. The object itself 201The buffer space of the buffer object is freed. The object itself
202remains intact, empty and it may be reused. 202remains intact, empty and may be reused.
203</p> 203</p>
204<p> 204<p>
205Note: you normally don't need to use this method. The garbage collector 205Note: you normally don't need to use this method. The garbage collector
@@ -404,8 +404,8 @@ speed is mostly constrained by object creation cost.
404</p> 404</p>
405<p> 405<p>
406The serializer handles most Lua types, common FFI number types and 406The serializer handles most Lua types, common FFI number types and
407nested structures. Functions, thread objects, other FFI cdata, full 407nested structures. Functions, thread objects, other FFI cdata and full
408userdata and associated metatables cannot be serialized (yet). 408userdata cannot be serialized (yet).
409</p> 409</p>
410<p> 410<p>
411The encoder serializes nested structures as trees. Multiple references 411The encoder serializes nested structures as trees. Multiple references
@@ -461,21 +461,31 @@ commonly occur as table keys of objects you are serializing. These keys
461are compactly encoded as indexes during serialization. A well chosen 461are compactly encoded as indexes during serialization. A well chosen
462dictionary saves space and improves serialization performance. 462dictionary saves space and improves serialization performance.
463</li> 463</li>
464<li>
465<tt>metatable</tt> is a Lua table holding a <b>dictionary of metatables</b>
466for the table objects you are serializing.
467</li>
464</ul> 468</ul>
465<p> 469<p>
466<tt>dict</tt> needs to be an array of strings, starting at index 1 and 470<tt>dict</tt> needs to be an array of strings and <tt>metatable</tt> needs
467without holes (no <tt>nil</tt> inbetween). The table is anchored in the 471to be an array of tables. Both starting at index 1 and without holes (no
468buffer object and internally modified into a two-way index (don't do 472<tt>nil</tt> inbetween). The tables are anchored in the buffer object and
469this yourself, just pass a plain array). The table must not be modified 473internally modified into a two-way index (don't do this yourself, just pass
470after it has been passed to <tt>buffer.new()</tt>. 474a plain array). The tables must not be modified after they have been passed
475to <tt>buffer.new()</tt>.
476</p>
477<p>
478The <tt>dict</tt> and <tt>metatable</tt> tables used by the encoder and
479decoder must be the same. Put the most common entries at the front. Extend
480at the end to ensure backwards-compatibility &mdash; older encodings can
481then still be read. You may also set some indexes to <tt>false</tt> to
482explicitly drop backwards-compatibility. Old encodings that use these
483indexes will throw an error when decoded.
471</p> 484</p>
472<p> 485<p>
473The <tt>dict</tt> tables used by the encoder and decoder must be the 486Metatables that are not found in the <tt>metatable</tt> dictionary are
474same. Put the most common entries at the front. Extend at the end to 487ignored when encoding. Decoding returns a table with a <tt>nil</tt>
475ensure backwards-compatibility &mdash; older encodings can then still be 488metatable.
476read. You may also set some indexes to <tt>false</tt> to explicitly drop
477backwards-compatibility. Old encodings that use these indexes will throw
478an error when decoded.
479</p> 489</p>
480<p> 490<p>
481Note: parsing and preparation of the options table is somewhat 491Note: parsing and preparation of the options table is somewhat
@@ -564,7 +574,7 @@ suffix.
564<pre> 574<pre>
565object → nil | false | true 575object → nil | false | true
566 | null | lightud32 | lightud64 576 | null | lightud32 | lightud64
567 | int | num | tab 577 | int | num | tab | tab_mt
568 | int64 | uint64 | complex 578 | int64 | uint64 | complex
569 | string 579 | string
570 580
@@ -585,13 +595,14 @@ tab → 0x08 // Empty table
585 | 0x0b a.U a*object h.U h*{object object} // Mixed 595 | 0x0b a.U a*object h.U h*{object object} // Mixed
586 | 0x0c a.U (a-1)*object // 1-based array 596 | 0x0c a.U (a-1)*object // 1-based array
587 | 0x0d a.U (a-1)*object h.U h*{object object} // Mixed 597 | 0x0d a.U (a-1)*object h.U h*{object object} // Mixed
598tab_mt → 0x0e (index-1).U tab // Metatable dict entry
588 599
589int64 → 0x10 int.L // FFI int64_t 600int64 → 0x10 int.L // FFI int64_t
590uint64 → 0x11 uint.L // FFI uint64_t 601uint64 → 0x11 uint.L // FFI uint64_t
591complex → 0x12 re.L im.L // FFI complex 602complex → 0x12 re.L im.L // FFI complex
592 603
593string → (0x20+len).U len*char.B 604string → (0x20+len).U len*char.B
594 | 0x0f (index-1).U // Dict entry 605 | 0x0f (index-1).U // String dict entry
595 606
596.B = 8 bit 607.B = 8 bit
597.I = 32 bit little-endian 608.I = 32 bit little-endian
diff --git a/src/lib_buffer.c b/src/lib_buffer.c
index ae065759..2e364861 100644
--- a/src/lib_buffer.c
+++ b/src/lib_buffer.c
@@ -288,7 +288,7 @@ LJLIB_CF(buffer_new)
288{ 288{
289 MSize sz = 0; 289 MSize sz = 0;
290 int targ = 1; 290 int targ = 1;
291 GCtab *env, *dict = NULL; 291 GCtab *env, *dict_str = NULL, *dict_mt = NULL;
292 GCudata *ud; 292 GCudata *ud;
293 SBufExt *sbx; 293 SBufExt *sbx;
294 if (L->base < L->top && !tvistab(L->base)) { 294 if (L->base < L->top && !tvistab(L->base)) {
@@ -298,10 +298,16 @@ LJLIB_CF(buffer_new)
298 } 298 }
299 if (L->base+targ-1 < L->top) { 299 if (L->base+targ-1 < L->top) {
300 GCtab *options = lj_lib_checktab(L, targ); 300 GCtab *options = lj_lib_checktab(L, targ);
301 cTValue *opt_dict = lj_tab_getstr(options, lj_str_newlit(L, "dict")); 301 cTValue *opt_dict, *opt_mt;
302 opt_dict = lj_tab_getstr(options, lj_str_newlit(L, "dict"));
302 if (opt_dict && tvistab(opt_dict)) { 303 if (opt_dict && tvistab(opt_dict)) {
303 dict = tabV(opt_dict); 304 dict_str = tabV(opt_dict);
304 lj_serialize_dict_prep(L, dict); 305 lj_serialize_dict_prep_str(L, dict_str);
306 }
307 opt_mt = lj_tab_getstr(options, lj_str_newlit(L, "metatable"));
308 if (opt_mt && tvistab(opt_mt)) {
309 dict_mt = tabV(opt_mt);
310 lj_serialize_dict_prep_mt(L, dict_mt);
305 } 311 }
306 } 312 }
307 env = tabref(curr_func(L)->c.env); 313 env = tabref(curr_func(L)->c.env);
@@ -312,7 +318,8 @@ LJLIB_CF(buffer_new)
312 setudataV(L, L->top++, ud); 318 setudataV(L, L->top++, ud);
313 sbx = (SBufExt *)uddata(ud); 319 sbx = (SBufExt *)uddata(ud);
314 lj_bufx_init(L, sbx); 320 lj_bufx_init(L, sbx);
315 setgcref(sbx->dict, obj2gco(dict)); 321 setgcref(sbx->dict_str, obj2gco(dict_str));
322 setgcref(sbx->dict_mt, obj2gco(dict_mt));
316 if (sz > 0) lj_buf_need2((SBuf *)sbx, sz); 323 if (sz > 0) lj_buf_need2((SBuf *)sbx, sz);
317 return 1; 324 return 1;
318} 325}
diff --git a/src/lj_buf.h b/src/lj_buf.h
index 4ace2685..e2ac922e 100644
--- a/src/lj_buf.h
+++ b/src/lj_buf.h
@@ -27,7 +27,8 @@ typedef struct SBufExt {
27 MRef bsb; /* Borrowed string buffer. */ 27 MRef bsb; /* Borrowed string buffer. */
28 }; 28 };
29 char *r; /* Read pointer. */ 29 char *r; /* Read pointer. */
30 GCRef dict; /* Serialization string dictionary table. */ 30 GCRef dict_str; /* Serialization string dictionary table. */
31 GCRef dict_mt; /* Serialization metatable dictionary table. */
31 int depth; /* Remaining recursion depth. */ 32 int depth; /* Remaining recursion depth. */
32} SBufExt; 33} SBufExt;
33 34
diff --git a/src/lj_gc.c b/src/lj_gc.c
index 646a27b2..5a238542 100644
--- a/src/lj_gc.c
+++ b/src/lj_gc.c
@@ -69,8 +69,10 @@ static void gc_mark(global_State *g, GCobj *o)
69 SBufExt *sbx = (SBufExt *)uddata(gco2ud(o)); 69 SBufExt *sbx = (SBufExt *)uddata(gco2ud(o));
70 if (sbufiscow(sbx) && gcref(sbx->cowref)) 70 if (sbufiscow(sbx) && gcref(sbx->cowref))
71 gc_markobj(g, gcref(sbx->cowref)); 71 gc_markobj(g, gcref(sbx->cowref));
72 if (gcref(sbx->dict)) 72 if (gcref(sbx->dict_str))
73 gc_markobj(g, gcref(sbx->dict)); 73 gc_markobj(g, gcref(sbx->dict_str));
74 if (gcref(sbx->dict_mt))
75 gc_markobj(g, gcref(sbx->dict_mt));
74 } 76 }
75 } else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) { 77 } else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) {
76 GCupval *uv = gco2uv(o); 78 GCupval *uv = gco2uv(o);
diff --git a/src/lj_serialize.c b/src/lj_serialize.c
index 70ff4796..e12e3668 100644
--- a/src/lj_serialize.c
+++ b/src/lj_serialize.c
@@ -34,8 +34,8 @@ enum {
34 SER_TAG_INT, 34 SER_TAG_INT,
35 SER_TAG_NUM, 35 SER_TAG_NUM,
36 SER_TAG_TAB, /* 0x08 */ 36 SER_TAG_TAB, /* 0x08 */
37 SER_TAG_0x0e = SER_TAG_TAB+6, 37 SER_TAG_DICT_MT = SER_TAG_TAB+6,
38 SER_TAG_DICT, 38 SER_TAG_DICT_STR,
39 SER_TAG_INT64, /* 0x10 */ 39 SER_TAG_INT64, /* 0x10 */
40 SER_TAG_UINT64, 40 SER_TAG_UINT64,
41 SER_TAG_COMPLEX, 41 SER_TAG_COMPLEX,
@@ -124,7 +124,7 @@ static LJ_AINLINE char *serialize_ru124(char *r, char *w, uint32_t *pv)
124} 124}
125 125
126/* Prepare string dictionary for use (once). */ 126/* Prepare string dictionary for use (once). */
127void LJ_FASTCALL lj_serialize_dict_prep(lua_State *L, GCtab *dict) 127void LJ_FASTCALL lj_serialize_dict_prep_str(lua_State *L, GCtab *dict)
128{ 128{
129 if (!dict->hmask) { /* No hash part means not prepared, yet. */ 129 if (!dict->hmask) { /* No hash part means not prepared, yet. */
130 MSize i, len = lj_tab_len(dict); 130 MSize i, len = lj_tab_len(dict);
@@ -143,6 +143,26 @@ void LJ_FASTCALL lj_serialize_dict_prep(lua_State *L, GCtab *dict)
143 } 143 }
144} 144}
145 145
146/* Prepare metatable dictionary for use (once). */
147void LJ_FASTCALL lj_serialize_dict_prep_mt(lua_State *L, GCtab *dict)
148{
149 if (!dict->hmask) { /* No hash part means not prepared, yet. */
150 MSize i, len = lj_tab_len(dict);
151 if (!len) return;
152 lj_tab_resize(L, dict, dict->asize, hsize2hbits(len));
153 for (i = 1; i <= len && i < dict->asize; i++) {
154 cTValue *o = arrayslot(dict, i);
155 if (tvistab(o)) {
156 if (tvisnil(lj_tab_get(L, dict, o))) { /* Ignore dups. */
157 lj_tab_newkey(L, dict, o)->u64 = (uint64_t)(i-1);
158 }
159 } else if (!tvisfalse(o)) {
160 lj_err_caller(L, LJ_ERR_BUFFER_BADOPT);
161 }
162 }
163 }
164}
165
146/* -- Internal serializer ------------------------------------------------- */ 166/* -- Internal serializer ------------------------------------------------- */
147 167
148/* Put serialized object into buffer. */ 168/* Put serialized object into buffer. */
@@ -185,6 +205,22 @@ static char *serialize_put(char *w, SBufExt *sbx, cTValue *o)
185 for (i = 0; i <= hmask; i++) 205 for (i = 0; i <= hmask; i++)
186 nhash += !tvisnil(&node[i].val); 206 nhash += !tvisnil(&node[i].val);
187 } 207 }
208 /* Write metatable index. */
209 if (LJ_UNLIKELY(tabref(sbx->dict_mt)) && tabref(t->metatable)) {
210 TValue mto;
211 Node *n;
212 settabV(sbufL(sbx), &mto, tabref(t->metatable));
213 n = hashgcref(tabref(sbx->dict_mt), mto.gcr);
214 do {
215 if (n->key.u64 == mto.u64) {
216 uint32_t idx = n->val.u32.lo;
217 w = serialize_more(w, sbx, 1+5);
218 *w++ = SER_TAG_DICT_MT;
219 w = serialize_wu124(w, idx);
220 break;
221 }
222 } while ((n = nextnode(n)));
223 }
188 /* Write number of array slots and hash slots. */ 224 /* Write number of array slots and hash slots. */
189 w = serialize_more(w, sbx, 1+2*5); 225 w = serialize_more(w, sbx, 1+2*5);
190 *w++ = (char)(SER_TAG_TAB + (nhash ? 1 : 0) + (narray ? one : 0)); 226 *w++ = (char)(SER_TAG_TAB + (nhash ? 1 : 0) + (narray ? one : 0));
@@ -197,19 +233,19 @@ static char *serialize_put(char *w, SBufExt *sbx, cTValue *o)
197 } 233 }
198 if (nhash) { /* Write hash entries. */ 234 if (nhash) { /* Write hash entries. */
199 const Node *node = noderef(t->node) + t->hmask; 235 const Node *node = noderef(t->node) + t->hmask;
200 GCtab *dict = tabref(sbx->dict); 236 GCtab *dict_str = tabref(sbx->dict_str);
201 if (LJ_UNLIKELY(dict)) { 237 if (LJ_UNLIKELY(dict_str)) {
202 for (;; node--) 238 for (;; node--)
203 if (!tvisnil(&node->val)) { 239 if (!tvisnil(&node->val)) {
204 if (LJ_LIKELY(tvisstr(&node->key))) { 240 if (LJ_LIKELY(tvisstr(&node->key))) {
205 /* Inlined lj_tab_getstr is 30% faster. */ 241 /* Inlined lj_tab_getstr is 30% faster. */
206 const GCstr *str = strV(&node->key); 242 const GCstr *str = strV(&node->key);
207 Node *n = hashstr(dict, str); 243 Node *n = hashstr(dict_str, str);
208 do { 244 do {
209 if (tvisstr(&n->key) && strV(&n->key) == str) { 245 if (tvisstr(&n->key) && strV(&n->key) == str) {
210 uint32_t idx = n->val.u32.lo; 246 uint32_t idx = n->val.u32.lo;
211 w = serialize_more(w, sbx, 1+5); 247 w = serialize_more(w, sbx, 1+5);
212 *w++ = SER_TAG_DICT; 248 *w++ = SER_TAG_DICT_STR;
213 w = serialize_wu124(w, idx); 249 w = serialize_wu124(w, idx);
214 break; 250 break;
215 } 251 }
@@ -322,19 +358,32 @@ static char *serialize_get(char *r, SBufExt *sbx, TValue *o)
322 if (!tvisnum(o)) setnanV(o); 358 if (!tvisnum(o)) setnanV(o);
323 } else if (tp <= SER_TAG_TRUE) { 359 } else if (tp <= SER_TAG_TRUE) {
324 setpriV(o, ~tp); 360 setpriV(o, ~tp);
325 } else if (tp == SER_TAG_DICT) { 361 } else if (tp == SER_TAG_DICT_STR) {
326 GCtab *dict; 362 GCtab *dict_str;
327 uint32_t idx; 363 uint32_t idx;
328 r = serialize_ru124(r, w, &idx); 364 r = serialize_ru124(r, w, &idx);
329 idx++; 365 idx++;
330 dict = tabref(sbx->dict); 366 dict_str = tabref(sbx->dict_str);
331 if (dict && idx < dict->asize && tvisstr(arrayslot(dict, idx))) 367 if (dict_str && idx < dict_str->asize && tvisstr(arrayslot(dict_str, idx)))
332 copyTV(sbufL(sbx), o, arrayslot(dict, idx)); 368 copyTV(sbufL(sbx), o, arrayslot(dict_str, idx));
333 else 369 else
334 lj_err_callerv(sbufL(sbx), LJ_ERR_BUFFER_BADDICTX, idx); 370 lj_err_callerv(sbufL(sbx), LJ_ERR_BUFFER_BADDICTX, idx);
335 } else if (tp >= SER_TAG_TAB && tp < SER_TAG_TAB+6) { 371 } else if (tp >= SER_TAG_TAB && tp <= SER_TAG_DICT_MT) {
336 uint32_t narray = 0, nhash = 0; 372 uint32_t narray = 0, nhash = 0;
337 GCtab *t; 373 GCtab *t, *mt = NULL;
374 if (tp == SER_TAG_DICT_MT) {
375 GCtab *dict_mt;
376 uint32_t idx;
377 r = serialize_ru124(r, w, &idx); if (LJ_UNLIKELY(!r)) goto eob;
378 idx++;
379 dict_mt = tabref(sbx->dict_mt);
380 if (dict_mt && idx < dict_mt->asize && tvistab(arrayslot(dict_mt, idx)))
381 mt = tabV(arrayslot(dict_mt, idx));
382 else
383 lj_err_callerv(sbufL(sbx), LJ_ERR_BUFFER_BADDICTX, idx);
384 r = serialize_ru124(r, w, &tp); if (LJ_UNLIKELY(!r)) goto eob;
385 if (!(tp >= SER_TAG_TAB && tp < SER_TAG_DICT_MT)) goto badtag;
386 }
338 if (tp >= SER_TAG_TAB+2) { 387 if (tp >= SER_TAG_TAB+2) {
339 r = serialize_ru124(r, w, &narray); if (LJ_UNLIKELY(!r)) goto eob; 388 r = serialize_ru124(r, w, &narray); if (LJ_UNLIKELY(!r)) goto eob;
340 } 389 }
@@ -342,6 +391,8 @@ static char *serialize_get(char *r, SBufExt *sbx, TValue *o)
342 r = serialize_ru124(r, w, &nhash); if (LJ_UNLIKELY(!r)) goto eob; 391 r = serialize_ru124(r, w, &nhash); if (LJ_UNLIKELY(!r)) goto eob;
343 } 392 }
344 t = lj_tab_new(sbufL(sbx), narray, hsize2hbits(nhash)); 393 t = lj_tab_new(sbufL(sbx), narray, hsize2hbits(nhash));
394 /* NOBARRIER: The table is new (marked white). */
395 setgcref(t->metatable, obj2gco(mt));
345 settabV(sbufL(sbx), o, t); 396 settabV(sbufL(sbx), o, t);
346 if (narray) { 397 if (narray) {
347 TValue *oa = tvref(t->array) + (tp >= SER_TAG_TAB+4); 398 TValue *oa = tvref(t->array) + (tp >= SER_TAG_TAB+4);
@@ -395,6 +446,7 @@ static char *serialize_get(char *r, SBufExt *sbx, TValue *o)
395 setrawlightudV(o, (void *)ud); 446 setrawlightudV(o, (void *)ud);
396#endif 447#endif
397 } else { 448 } else {
449badtag:
398 lj_err_callerv(sbufL(sbx), LJ_ERR_BUFFER_BADDEC, tp); 450 lj_err_callerv(sbufL(sbx), LJ_ERR_BUFFER_BADDEC, tp);
399 } 451 }
400 return r; 452 return r;
@@ -460,10 +512,11 @@ LJ_FUNC MSize LJ_FASTCALL lj_serialize_peektype(SBufExt *sbx)
460 case SER_TAG_NUM: return IRT_NUM; 512 case SER_TAG_NUM: return IRT_NUM;
461 case SER_TAG_TAB: case SER_TAG_TAB+1: case SER_TAG_TAB+2: 513 case SER_TAG_TAB: case SER_TAG_TAB+1: case SER_TAG_TAB+2:
462 case SER_TAG_TAB+3: case SER_TAG_TAB+4: case SER_TAG_TAB+5: 514 case SER_TAG_TAB+3: case SER_TAG_TAB+4: case SER_TAG_TAB+5:
515 case SER_TAG_DICT_MT:
463 return IRT_TAB; 516 return IRT_TAB;
464 case SER_TAG_INT64: case SER_TAG_UINT64: case SER_TAG_COMPLEX: 517 case SER_TAG_INT64: case SER_TAG_UINT64: case SER_TAG_COMPLEX:
465 return IRT_CDATA; 518 return IRT_CDATA;
466 case SER_TAG_DICT: 519 case SER_TAG_DICT_STR:
467 default: 520 default:
468 return IRT_STR; 521 return IRT_STR;
469 } 522 }
diff --git a/src/lj_serialize.h b/src/lj_serialize.h
index 9bd780ca..1fda23eb 100644
--- a/src/lj_serialize.h
+++ b/src/lj_serialize.h
@@ -13,7 +13,8 @@
13 13
14#define LJ_SERIALIZE_DEPTH 100 /* Default depth. */ 14#define LJ_SERIALIZE_DEPTH 100 /* Default depth. */
15 15
16LJ_FUNC void LJ_FASTCALL lj_serialize_dict_prep(lua_State *L, GCtab *dict); 16LJ_FUNC void LJ_FASTCALL lj_serialize_dict_prep_str(lua_State *L, GCtab *dict);
17LJ_FUNC void LJ_FASTCALL lj_serialize_dict_prep_mt(lua_State *L, GCtab *dict);
17LJ_FUNC SBufExt * LJ_FASTCALL lj_serialize_put(SBufExt *sbx, cTValue *o); 18LJ_FUNC SBufExt * LJ_FASTCALL lj_serialize_put(SBufExt *sbx, cTValue *o);
18LJ_FUNC char * LJ_FASTCALL lj_serialize_get(SBufExt *sbx, TValue *o); 19LJ_FUNC char * LJ_FASTCALL lj_serialize_get(SBufExt *sbx, TValue *o);
19LJ_FUNC GCstr * LJ_FASTCALL lj_serialize_encode(lua_State *L, cTValue *o); 20LJ_FUNC GCstr * LJ_FASTCALL lj_serialize_encode(lua_State *L, cTValue *o);