diff options
author | Mike Pall <mike> | 2021-03-25 02:21:31 +0100 |
---|---|---|
committer | Mike Pall <mike> | 2021-03-25 02:21:31 +0100 |
commit | 4c6b669c419f313306b9e6ee43be4ad5f6d73ec6 (patch) | |
tree | 4547f52836e186e94b68b331ea73d6b0086cb415 /src/lj_serialize.c | |
parent | 836fb5bbd3a0d48cf3e4de70535925a85aea835f (diff) | |
download | luajit-4c6b669c419f313306b9e6ee43be4ad5f6d73ec6.tar.gz luajit-4c6b669c419f313306b9e6ee43be4ad5f6d73ec6.tar.bz2 luajit-4c6b669c419f313306b9e6ee43be4ad5f6d73ec6.zip |
String buffers, part 1: object serialization.
Sponsored by fmad.io.
Diffstat (limited to 'src/lj_serialize.c')
-rw-r--r-- | src/lj_serialize.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/src/lj_serialize.c b/src/lj_serialize.c new file mode 100644 index 00000000..5d7b7721 --- /dev/null +++ b/src/lj_serialize.c | |||
@@ -0,0 +1,351 @@ | |||
1 | /* | ||
2 | ** Object de/serialization. | ||
3 | ** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #define lj_serialize_c | ||
7 | #define LUA_CORE | ||
8 | |||
9 | #include "lj_obj.h" | ||
10 | #include "lj_err.h" | ||
11 | #include "lj_buf.h" | ||
12 | #include "lj_str.h" | ||
13 | #include "lj_tab.h" | ||
14 | #include "lj_udata.h" | ||
15 | #if LJ_HASFFI | ||
16 | #include "lj_ctype.h" | ||
17 | #include "lj_cdata.h" | ||
18 | #endif | ||
19 | #include "lj_serialize.h" | ||
20 | |||
21 | /* Tags for internal serialization format. */ | ||
22 | enum { | ||
23 | SER_TAG_NIL, /* 0x00 */ | ||
24 | SER_TAG_FALSE, | ||
25 | SER_TAG_TRUE, | ||
26 | SER_TAG_NULL, | ||
27 | SER_TAG_LIGHTUD32, | ||
28 | SER_TAG_LIGHTUD64, | ||
29 | SER_TAG_INT, | ||
30 | SER_TAG_NUM, | ||
31 | SER_TAG_TAB, /* 0x08 */ | ||
32 | SER_TAG_0x0e = SER_TAG_TAB+6, | ||
33 | SER_TAG_0x0f, | ||
34 | SER_TAG_INT64, /* 0x10 */ | ||
35 | SER_TAG_UINT64, | ||
36 | SER_TAG_COMPLEX, | ||
37 | SER_TAG_0x13, | ||
38 | SER_TAG_0x14, | ||
39 | SER_TAG_0x15, | ||
40 | SER_TAG_0x16, | ||
41 | SER_TAG_0x17, | ||
42 | SER_TAG_0x18, /* 0x18 */ | ||
43 | SER_TAG_0x19, | ||
44 | SER_TAG_0x1a, | ||
45 | SER_TAG_0x1b, | ||
46 | SER_TAG_0x1c, | ||
47 | SER_TAG_0x1d, | ||
48 | SER_TAG_0x1e, | ||
49 | SER_TAG_0x1f, | ||
50 | SER_TAG_STR, /* 0x20 + str->len */ | ||
51 | }; | ||
52 | LJ_STATIC_ASSERT((SER_TAG_TAB & 7) == 0); | ||
53 | |||
54 | /* -- Helper functions ---------------------------------------------------- */ | ||
55 | |||
56 | static LJ_AINLINE char *serialize_more(char *w, StrBuf *sbuf, MSize sz) | ||
57 | { | ||
58 | if (LJ_UNLIKELY(sz > (MSize)(sbufE(sbuf->sb) - w))) { | ||
59 | setsbufP(sbuf->sb, w); | ||
60 | w = lj_buf_more2(sbuf->sb, sz); | ||
61 | } | ||
62 | return w; | ||
63 | } | ||
64 | |||
65 | /* Write U124 to buffer. */ | ||
66 | static LJ_NOINLINE char *serialize_wu124_(char *w, uint32_t v) | ||
67 | { | ||
68 | if (v < 0x1fe0) { | ||
69 | v -= 0xe0; | ||
70 | *w++ = (char)(0xe0 | (v >> 8)); *w++ = (char)v; | ||
71 | } else { | ||
72 | *w++ = (char)0xff; | ||
73 | #if LJ_BE | ||
74 | v = lj_bswap(v); | ||
75 | #endif | ||
76 | memcpy(w, &v, 4); w += 4; | ||
77 | } | ||
78 | return w; | ||
79 | } | ||
80 | |||
81 | static LJ_AINLINE char *serialize_wu124(char *w, uint32_t v) | ||
82 | { | ||
83 | if (LJ_LIKELY(v < 0xe0)) { | ||
84 | *w++ = (char)v; | ||
85 | return w; | ||
86 | } else { | ||
87 | return serialize_wu124_(w, v); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static LJ_NOINLINE char *serialize_ru124_(char *r, char *e, uint32_t *pv) | ||
92 | { | ||
93 | uint32_t v = *pv; | ||
94 | if (v != 0xff) { | ||
95 | if (r >= e) return NULL; | ||
96 | v = ((v & 0x1f) << 8) + *(uint8_t *)r + 0xe0; r++; | ||
97 | } else { | ||
98 | if (r + 4 > e) return NULL; | ||
99 | v = lj_getu32(r); r += 4; | ||
100 | #if LJ_BE | ||
101 | v = lj_bswap(v); | ||
102 | #endif | ||
103 | } | ||
104 | *pv = v; | ||
105 | return r; | ||
106 | } | ||
107 | |||
108 | static LJ_AINLINE char *serialize_ru124(char *r, char *e, uint32_t *pv) | ||
109 | { | ||
110 | if (LJ_LIKELY(r < e)) { | ||
111 | uint32_t v = *(uint8_t *)r; r++; | ||
112 | *pv = v; | ||
113 | if (LJ_UNLIKELY(v >= 0xe0)) { | ||
114 | r = serialize_ru124_(r, e, pv); | ||
115 | } | ||
116 | return r; | ||
117 | } | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | /* -- Internal serializer ------------------------------------------------- */ | ||
122 | |||
123 | /* Put serialized object into buffer. */ | ||
124 | static char *serialize_put(char *w, StrBuf *sbuf, cTValue *o) | ||
125 | { | ||
126 | if (LJ_LIKELY(tvisstr(o))) { | ||
127 | const GCstr *str = strV(o); | ||
128 | MSize len = str->len; | ||
129 | w = serialize_more(w, sbuf, 5+len); | ||
130 | w = serialize_wu124(w, SER_TAG_STR + len); | ||
131 | w = lj_buf_wmem(w, strdata(str), len); | ||
132 | } else if (tvisint(o)) { | ||
133 | uint32_t x = LJ_BE ? lj_bswap((uint32_t)intV(o)) : (uint32_t)intV(o); | ||
134 | w = serialize_more(w, sbuf, 1+4); | ||
135 | *w++ = SER_TAG_INT; memcpy(w, &x, 4); w += 4; | ||
136 | } else if (tvisnum(o)) { | ||
137 | uint64_t x = LJ_BE ? lj_bswap64(o->u64) : o->u64; | ||
138 | w = serialize_more(w, sbuf, 1+sizeof(lua_Number)); | ||
139 | *w++ = SER_TAG_NUM; memcpy(w, &x, 8); w += 8; | ||
140 | } else if (tvispri(o)) { | ||
141 | w = serialize_more(w, sbuf, 1); | ||
142 | *w++ = (char)(SER_TAG_NIL + ~itype(o)); | ||
143 | } else if (tvistab(o)) { | ||
144 | const GCtab *t = tabV(o); | ||
145 | uint32_t narray = 0, nhash = 0, one = 2; | ||
146 | if (sbuf->depth <= 0) lj_err_caller(sbufL(sbuf->sb), LJ_ERR_BUFFER_DEPTH); | ||
147 | sbuf->depth--; | ||
148 | if (t->asize > 0) { /* Determine max. length of array part. */ | ||
149 | ptrdiff_t i; | ||
150 | TValue *array = tvref(t->array); | ||
151 | for (i = (ptrdiff_t)t->asize-1; i >= 0; i--) | ||
152 | if (!tvisnil(&array[i])) | ||
153 | break; | ||
154 | narray = (uint32_t)(i+1); | ||
155 | if (narray && tvisnil(&array[0])) one = 4; | ||
156 | } | ||
157 | if (t->hmask > 0) { /* Count number of used hash slots. */ | ||
158 | uint32_t i, hmask = t->hmask; | ||
159 | Node *node = noderef(t->node); | ||
160 | for (i = 0; i <= hmask; i++) | ||
161 | nhash += !tvisnil(&node[i].val); | ||
162 | } | ||
163 | /* Write number of array slots and hash slots. */ | ||
164 | w = serialize_more(w, sbuf, 1+2*5); | ||
165 | *w++ = (char)(SER_TAG_TAB + (nhash ? 1 : 0) + (narray ? one : 0)); | ||
166 | if (narray) w = serialize_wu124(w, narray); | ||
167 | if (nhash) w = serialize_wu124(w, nhash); | ||
168 | if (narray) { /* Write array entries. */ | ||
169 | cTValue *oa = tvref(t->array) + (one >> 2); | ||
170 | cTValue *oe = tvref(t->array) + narray; | ||
171 | while (oa < oe) w = serialize_put(w, sbuf, oa++); | ||
172 | } | ||
173 | if (nhash) { /* Write hash entries. */ | ||
174 | const Node *node = noderef(t->node) + t->hmask; | ||
175 | for (;; node--) | ||
176 | if (!tvisnil(&node->val)) { | ||
177 | w = serialize_put(w, sbuf, &node->key); | ||
178 | w = serialize_put(w, sbuf, &node->val); | ||
179 | if (--nhash == 0) break; | ||
180 | } | ||
181 | } | ||
182 | sbuf->depth++; | ||
183 | #if LJ_HASFFI | ||
184 | } else if (tviscdata(o)) { | ||
185 | CTState *cts = ctype_cts(sbufL(sbuf->sb)); | ||
186 | CType *s = ctype_raw(cts, cdataV(o)->ctypeid); | ||
187 | uint8_t *sp = cdataptr(cdataV(o)); | ||
188 | if (ctype_isinteger(s->info) && s->size == 8) { | ||
189 | w = serialize_more(w, sbuf, 1+8); | ||
190 | *w++ = (s->info & CTF_UNSIGNED) ? SER_TAG_UINT64 : SER_TAG_INT64; | ||
191 | #if LJ_BE | ||
192 | { uint64_t u = lj_bswap64(*(uint64_t *)sp); memcpy(w, &u, 8); } | ||
193 | #else | ||
194 | memcpy(w, sp, 8); | ||
195 | #endif | ||
196 | w += 8; | ||
197 | } else if (ctype_iscomplex(s->info) && s->size == 16) { | ||
198 | w = serialize_more(w, sbuf, 1+16); | ||
199 | *w++ = SER_TAG_COMPLEX; | ||
200 | #if LJ_BE | ||
201 | { /* Only swap the doubles. The re/im order stays the same. */ | ||
202 | uint64_t u = lj_bswap64(((uint64_t *)sp)[0]); memcpy(w, &u, 8); | ||
203 | u = lj_bswap64(((uint64_t *)sp)[1]); memcpy(w+8, &u, 8); | ||
204 | } | ||
205 | #else | ||
206 | memcpy(w, sp, 16); | ||
207 | #endif | ||
208 | w += 16; | ||
209 | } else { | ||
210 | goto badenc; /* NYI other cdata */ | ||
211 | } | ||
212 | #endif | ||
213 | } else if (tvislightud(o)) { | ||
214 | uintptr_t ud = (uintptr_t)lightudV(G(sbufL(sbuf->sb)), o); | ||
215 | w = serialize_more(w, sbuf, 1+sizeof(ud)); | ||
216 | if (ud == 0) { | ||
217 | *w++ = SER_TAG_NULL; | ||
218 | } else if (LJ_32 || checku32(ud)) { | ||
219 | #if LJ_BE && LJ_64 | ||
220 | ud = lj_bswap64(ud); | ||
221 | #elif LJ_BE | ||
222 | ud = lj_bswap(ud); | ||
223 | #endif | ||
224 | *w++ = SER_TAG_LIGHTUD32; memcpy(w, &ud, 4); w += 4; | ||
225 | } else { | ||
226 | #if LJ_BE | ||
227 | ud = lj_bswap64(ud); | ||
228 | #endif | ||
229 | *w++ = SER_TAG_LIGHTUD64; memcpy(w, &ud, 8); w += 8; | ||
230 | } | ||
231 | } else { | ||
232 | /* NYI userdata */ | ||
233 | #if LJ_HASFFI | ||
234 | badenc: | ||
235 | #endif | ||
236 | lj_err_callerv(sbufL(sbuf->sb), LJ_ERR_BUFFER_BADENC, lj_typename(o)); | ||
237 | } | ||
238 | return w; | ||
239 | } | ||
240 | |||
241 | /* Get serialized object from buffer. */ | ||
242 | static char *serialize_get(char *r, StrBuf *sbuf, TValue *o) | ||
243 | { | ||
244 | char *e = sbufE(sbuf->sb); | ||
245 | uint32_t tp; | ||
246 | r = serialize_ru124(r, e, &tp); if (LJ_UNLIKELY(!r)) goto eob; | ||
247 | if (LJ_LIKELY(tp >= SER_TAG_STR)) { | ||
248 | uint32_t len = tp - SER_TAG_STR; | ||
249 | if (LJ_UNLIKELY(len > (uint32_t)(e - r))) goto eob; | ||
250 | setstrV(sbufL(sbuf->sb), o, lj_str_new(sbufL(sbuf->sb), r, len)); | ||
251 | r += len; | ||
252 | } else if (tp == SER_TAG_INT) { | ||
253 | if (LJ_UNLIKELY(r + 4 > e)) goto eob; | ||
254 | setintV(o, (int32_t)(LJ_BE ? lj_bswap(lj_getu32(r)) : lj_getu32(r))); | ||
255 | r += 4; | ||
256 | } else if (tp == SER_TAG_NUM) { | ||
257 | if (LJ_UNLIKELY(r + 8 > e)) goto eob; | ||
258 | memcpy(o, r, 8); r += 8; | ||
259 | #if LJ_BE | ||
260 | o->u64 = lj_bswap64(o->u64); | ||
261 | #endif | ||
262 | if (!tvisnum(o)) setnanV(o); | ||
263 | } else if (tp <= SER_TAG_TRUE) { | ||
264 | setpriV(o, ~tp); | ||
265 | } else if (tp >= SER_TAG_TAB && tp < SER_TAG_TAB+6) { | ||
266 | uint32_t narray = 0, nhash = 0; | ||
267 | GCtab *t; | ||
268 | if (tp >= SER_TAG_TAB+2) { | ||
269 | r = serialize_ru124(r, e, &narray); if (LJ_UNLIKELY(!r)) goto eob; | ||
270 | } | ||
271 | if ((tp & 1)) { | ||
272 | r = serialize_ru124(r, e, &nhash); if (LJ_UNLIKELY(!r)) goto eob; | ||
273 | } | ||
274 | t = lj_tab_new(sbufL(sbuf->sb), narray, hsize2hbits(nhash)); | ||
275 | settabV(sbufL(sbuf->sb), o, t); | ||
276 | if (narray) { | ||
277 | TValue *oa = tvref(t->array) + (tp >= SER_TAG_TAB+4); | ||
278 | TValue *oe = tvref(t->array) + narray; | ||
279 | while (oa < oe) r = serialize_get(r, sbuf, oa++); | ||
280 | } | ||
281 | if (nhash) { | ||
282 | do { | ||
283 | TValue k, *v; | ||
284 | r = serialize_get(r, sbuf, &k); | ||
285 | v = lj_tab_set(sbufL(sbuf->sb), t, &k); | ||
286 | if (LJ_UNLIKELY(!tvisnil(v))) | ||
287 | lj_err_caller(sbufL(sbuf->sb), LJ_ERR_BUFFER_DUPKEY); | ||
288 | r = serialize_get(r, sbuf, v); | ||
289 | } while (--nhash); | ||
290 | } | ||
291 | #if LJ_HASFFI | ||
292 | } else if (tp >= SER_TAG_INT64 && tp <= SER_TAG_COMPLEX) { | ||
293 | uint32_t sz = tp == SER_TAG_COMPLEX ? 16 : 8; | ||
294 | GCcdata *cd; | ||
295 | if (LJ_UNLIKELY(r + sz > e)) goto eob; | ||
296 | cd = lj_cdata_new_(sbufL(sbuf->sb), | ||
297 | tp == SER_TAG_INT64 ? CTID_INT64 : | ||
298 | tp == SER_TAG_UINT64 ? CTID_UINT64 : CTID_COMPLEX_DOUBLE, | ||
299 | sz); | ||
300 | memcpy(cdataptr(cd), r, sz); r += sz; | ||
301 | #if LJ_BE | ||
302 | *(uint64_t *)cdataptr(cd) = lj_bswap64(*(uint64_t *)cdataptr(cd)); | ||
303 | if (sz == 16) | ||
304 | ((uint64_t *)cdataptr(cd))[1] = lj_bswap64(((uint64_t *)cdataptr(cd))[1]); | ||
305 | #endif | ||
306 | setcdataV(sbufL(sbuf->sb), o, cd); | ||
307 | #endif | ||
308 | } else if (tp <= (LJ_64 ? SER_TAG_LIGHTUD64 : SER_TAG_LIGHTUD32)) { | ||
309 | uintptr_t ud = 0; | ||
310 | if (tp == SER_TAG_LIGHTUD32) { | ||
311 | if (LJ_UNLIKELY(r + 4 > e)) goto eob; | ||
312 | ud = (uintptr_t)(LJ_BE ? lj_bswap(lj_getu32(r)) : lj_getu32(r)); | ||
313 | r += 4; | ||
314 | } | ||
315 | #if LJ_64 | ||
316 | else if (tp == SER_TAG_LIGHTUD64) { | ||
317 | if (LJ_UNLIKELY(r + 8 > e)) goto eob; | ||
318 | memcpy(&ud, r, 8); r += 8; | ||
319 | #if LJ_BE | ||
320 | ud = lj_bswap64(ud); | ||
321 | #endif | ||
322 | } | ||
323 | setrawlightudV(o, lj_lightud_intern(sbufL(sbuf->sb), (void *)ud)); | ||
324 | #else | ||
325 | setrawlightudV(o, (void *)ud); | ||
326 | #endif | ||
327 | } else { | ||
328 | lj_err_callerv(sbufL(sbuf->sb), LJ_ERR_BUFFER_BADDEC, tp); | ||
329 | } | ||
330 | return r; | ||
331 | eob: | ||
332 | lj_err_caller(sbufL(sbuf->sb), LJ_ERR_BUFFER_EOB); | ||
333 | return NULL; | ||
334 | } | ||
335 | |||
336 | StrBuf * LJ_FASTCALL lj_serialize_put(StrBuf *sbuf, cTValue *o) | ||
337 | { | ||
338 | sbuf->depth = LJ_SERIALIZE_DEPTH; | ||
339 | setsbufP(sbuf->sb, serialize_put(sbufP(sbuf->sb), sbuf, o)); | ||
340 | return sbuf; | ||
341 | } | ||
342 | |||
343 | StrBuf * LJ_FASTCALL lj_serialize_get(StrBuf *sbuf, TValue *o) | ||
344 | { | ||
345 | char *r = serialize_get(sbuf->r, sbuf, o); | ||
346 | if (r != sbufP(sbuf->sb)) | ||
347 | lj_err_caller(sbufL(sbuf->sb), LJ_ERR_BUFFER_LEFTOV); | ||
348 | sbuf->r = r; | ||
349 | return sbuf; | ||
350 | } | ||
351 | |||