1 | /* |
2 | ** {====================================================== |
3 | ** Library for packing/unpacking structures. |
4 | ** $Id: struct.c,v 1.7 2018/05/11 22:04:31 roberto Exp $ |
5 | ** See Copyright Notice at the end of this file |
6 | ** ======================================================= |
7 | */ |
8 | /* |
9 | ** Valid formats: |
10 | ** > - big endian |
11 | ** < - little endian |
12 | ** ![num] - alignment |
13 | ** x - pading |
14 | ** b/B - signed/unsigned byte |
15 | ** h/H - signed/unsigned short |
16 | ** l/L - signed/unsigned long |
17 | ** T - size_t |
18 | ** i/In - signed/unsigned integer with size 'n' (default is size of int) |
19 | ** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means |
20 | the whole string; when unpacking, n==0 means use the previous |
21 | read number as the string length |
22 | ** s - zero-terminated string |
23 | ** f - float |
24 | ** d - double |
25 | ** ' ' - ignored |
26 | */ |
27 | |
28 | |
29 | #include <assert.h> |
30 | #include <ctype.h> |
31 | #include <limits.h> |
32 | #include <stddef.h> |
33 | #include <string.h> |
34 | |
35 | |
36 | #include "lua.h" |
37 | #include "lauxlib.h" |
38 | |
39 | |
40 | #if (LUA_VERSION_NUM >= 502) |
41 | |
42 | #define luaL_register(L,n,f) luaL_newlib(L,f) |
43 | |
44 | #endif |
45 | |
46 | |
47 | /* basic integer type */ |
48 | #if !defined(STRUCT_INT) |
49 | #define STRUCT_INT long |
50 | #endif |
51 | |
52 | typedef STRUCT_INT Inttype; |
53 | |
54 | /* corresponding unsigned version */ |
55 | typedef unsigned STRUCT_INT Uinttype; |
56 | |
57 | |
58 | /* maximum size (in bytes) for integral types */ |
59 | #define MAXINTSIZE 32 |
60 | |
61 | /* is 'x' a power of 2? */ |
62 | #define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0) |
63 | |
64 | /* dummy structure to get alignment requirements */ |
65 | struct cD { |
66 | char c; |
67 | double d; |
68 | }; |
69 | |
70 | |
71 | #define PADDING (sizeof(struct cD) - sizeof(double)) |
72 | #define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int)) |
73 | |
74 | |
75 | /* endian options */ |
76 | #define BIG 0 |
77 | #define LITTLE 1 |
78 | |
79 | |
80 | static union { |
81 | int dummy; |
82 | char endian; |
83 | } const native = {1}; |
84 | |
85 | |
86 | typedef struct { |
87 | int ; |
88 | int ; |
89 | } ; |
90 | |
91 | |
92 | static int getnum (lua_State *L, const char **fmt, int df) { |
93 | if (!isdigit(**fmt)) /* no number? */ |
94 | return df; /* return default value */ |
95 | else { |
96 | int a = 0; |
97 | do { |
98 | if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0'))) |
99 | luaL_error(L, "integral size overflow" ); |
100 | a = a*10 + *((*fmt)++) - '0'; |
101 | } while (isdigit(**fmt)); |
102 | return a; |
103 | } |
104 | } |
105 | |
106 | |
107 | #define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1) |
108 | |
109 | |
110 | |
111 | static size_t optsize (lua_State *L, char opt, const char **fmt) { |
112 | switch (opt) { |
113 | case 'B': case 'b': return sizeof(char); |
114 | case 'H': case 'h': return sizeof(short); |
115 | case 'L': case 'l': return sizeof(long); |
116 | case 'T': return sizeof(size_t); |
117 | case 'f': return sizeof(float); |
118 | case 'd': return sizeof(double); |
119 | case 'x': return 1; |
120 | case 'c': return getnum(L, fmt, 1); |
121 | case 'i': case 'I': { |
122 | int sz = getnum(L, fmt, sizeof(int)); |
123 | if (sz > MAXINTSIZE) |
124 | luaL_error(L, "integral size %d is larger than limit of %d" , |
125 | sz, MAXINTSIZE); |
126 | return sz; |
127 | } |
128 | default: return 0; /* other cases do not need alignment */ |
129 | } |
130 | } |
131 | |
132 | |
133 | /* |
134 | ** return number of bytes needed to align an element of size 'size' |
135 | ** at current position 'len' |
136 | */ |
137 | static int gettoalign (size_t len, Header *h, int opt, size_t size) { |
138 | if (size == 0 || opt == 'c') return 0; |
139 | if (size > (size_t)h->align) |
140 | size = h->align; /* respect max. alignment */ |
141 | return (size - (len & (size - 1))) & (size - 1); |
142 | } |
143 | |
144 | |
145 | /* |
146 | ** options to control endianess and alignment |
147 | */ |
148 | static void controloptions (lua_State *L, int opt, const char **fmt, |
149 | Header *h) { |
150 | switch (opt) { |
151 | case ' ': return; /* ignore white spaces */ |
152 | case '>': h->endian = BIG; return; |
153 | case '<': h->endian = LITTLE; return; |
154 | case '!': { |
155 | int a = getnum(L, fmt, MAXALIGN); |
156 | if (!isp2(a)) |
157 | luaL_error(L, "alignment %d is not a power of 2" , a); |
158 | h->align = a; |
159 | return; |
160 | } |
161 | default: { |
162 | const char *msg = lua_pushfstring(L, "invalid format option '%c'" , opt); |
163 | luaL_argerror(L, 1, msg); |
164 | } |
165 | } |
166 | } |
167 | |
168 | |
169 | static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian, |
170 | int size) { |
171 | lua_Number n = luaL_checknumber(L, arg); |
172 | Uinttype value; |
173 | char buff[MAXINTSIZE]; |
174 | if (n < 0) |
175 | value = (Uinttype)(Inttype)n; |
176 | else |
177 | value = (Uinttype)n; |
178 | if (endian == LITTLE) { |
179 | int i; |
180 | for (i = 0; i < size; i++) { |
181 | buff[i] = (value & 0xff); |
182 | value >>= 8; |
183 | } |
184 | } |
185 | else { |
186 | int i; |
187 | for (i = size - 1; i >= 0; i--) { |
188 | buff[i] = (value & 0xff); |
189 | value >>= 8; |
190 | } |
191 | } |
192 | luaL_addlstring(b, buff, size); |
193 | } |
194 | |
195 | |
196 | static void correctbytes (char *b, int size, int endian) { |
197 | if (endian != native.endian) { |
198 | int i = 0; |
199 | while (i < --size) { |
200 | char temp = b[i]; |
201 | b[i++] = b[size]; |
202 | b[size] = temp; |
203 | } |
204 | } |
205 | } |
206 | |
207 | |
208 | static int b_pack (lua_State *L) { |
209 | luaL_Buffer b; |
210 | const char *fmt = luaL_checkstring(L, 1); |
211 | Header h; |
212 | int arg = 2; |
213 | size_t totalsize = 0; |
214 | defaultoptions(&h); |
215 | lua_pushnil(L); /* mark to separate arguments from string buffer */ |
216 | luaL_buffinit(L, &b); |
217 | while (*fmt != '\0') { |
218 | int opt = *fmt++; |
219 | size_t size = optsize(L, opt, &fmt); |
220 | int toalign = gettoalign(totalsize, &h, opt, size); |
221 | totalsize += toalign; |
222 | while (toalign-- > 0) luaL_addchar(&b, '\0'); |
223 | switch (opt) { |
224 | case 'b': case 'B': case 'h': case 'H': |
225 | case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ |
226 | putinteger(L, &b, arg++, h.endian, size); |
227 | break; |
228 | } |
229 | case 'x': { |
230 | luaL_addchar(&b, '\0'); |
231 | break; |
232 | } |
233 | case 'f': { |
234 | float f = (float)luaL_checknumber(L, arg++); |
235 | correctbytes((char *)&f, size, h.endian); |
236 | luaL_addlstring(&b, (char *)&f, size); |
237 | break; |
238 | } |
239 | case 'd': { |
240 | double d = luaL_checknumber(L, arg++); |
241 | correctbytes((char *)&d, size, h.endian); |
242 | luaL_addlstring(&b, (char *)&d, size); |
243 | break; |
244 | } |
245 | case 'c': case 's': { |
246 | size_t l; |
247 | const char *s = luaL_checklstring(L, arg++, &l); |
248 | if (size == 0) size = l; |
249 | luaL_argcheck(L, l >= (size_t)size, arg, "string too short" ); |
250 | luaL_addlstring(&b, s, size); |
251 | if (opt == 's') { |
252 | luaL_addchar(&b, '\0'); /* add zero at the end */ |
253 | size++; |
254 | } |
255 | break; |
256 | } |
257 | default: controloptions(L, opt, &fmt, &h); |
258 | } |
259 | totalsize += size; |
260 | } |
261 | luaL_pushresult(&b); |
262 | return 1; |
263 | } |
264 | |
265 | |
266 | static lua_Number getinteger (const char *buff, int endian, |
267 | int issigned, int size) { |
268 | Uinttype l = 0; |
269 | int i; |
270 | if (endian == BIG) { |
271 | for (i = 0; i < size; i++) { |
272 | l <<= 8; |
273 | l |= (Uinttype)(unsigned char)buff[i]; |
274 | } |
275 | } |
276 | else { |
277 | for (i = size - 1; i >= 0; i--) { |
278 | l <<= 8; |
279 | l |= (Uinttype)(unsigned char)buff[i]; |
280 | } |
281 | } |
282 | if (!issigned) |
283 | return (lua_Number)l; |
284 | else { /* signed format */ |
285 | Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1); |
286 | if (l & mask) /* negative value? */ |
287 | l |= mask; /* signal extension */ |
288 | return (lua_Number)(Inttype)l; |
289 | } |
290 | } |
291 | |
292 | |
293 | static int b_unpack (lua_State *L) { |
294 | Header h; |
295 | const char *fmt = luaL_checkstring(L, 1); |
296 | size_t ld; |
297 | const char *data = luaL_checklstring(L, 2, &ld); |
298 | size_t pos = luaL_optinteger(L, 3, 1); |
299 | luaL_argcheck(L, pos > 0, 3, "offset must be 1 or greater" ); |
300 | pos--; /* Lua indexes are 1-based, but here we want 0-based for C |
301 | * pointer math. */ |
302 | int n = 0; /* number of results */ |
303 | defaultoptions(&h); |
304 | while (*fmt) { |
305 | int opt = *fmt++; |
306 | size_t size = optsize(L, opt, &fmt); |
307 | pos += gettoalign(pos, &h, opt, size); |
308 | luaL_argcheck(L, size <= ld && pos <= ld - size, |
309 | 2, "data string too short" ); |
310 | /* stack space for item + next position */ |
311 | luaL_checkstack(L, 2, "too many results" ); |
312 | switch (opt) { |
313 | case 'b': case 'B': case 'h': case 'H': |
314 | case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ |
315 | int issigned = islower(opt); |
316 | lua_Number res = getinteger(data+pos, h.endian, issigned, size); |
317 | lua_pushnumber(L, res); n++; |
318 | break; |
319 | } |
320 | case 'x': { |
321 | break; |
322 | } |
323 | case 'f': { |
324 | float f; |
325 | memcpy(&f, data+pos, size); |
326 | correctbytes((char *)&f, sizeof(f), h.endian); |
327 | lua_pushnumber(L, f); n++; |
328 | break; |
329 | } |
330 | case 'd': { |
331 | double d; |
332 | memcpy(&d, data+pos, size); |
333 | correctbytes((char *)&d, sizeof(d), h.endian); |
334 | lua_pushnumber(L, d); n++; |
335 | break; |
336 | } |
337 | case 'c': { |
338 | if (size == 0) { |
339 | if (n == 0 || !lua_isnumber(L, -1)) |
340 | luaL_error(L, "format 'c0' needs a previous size" ); |
341 | size = lua_tonumber(L, -1); |
342 | lua_pop(L, 1); n--; |
343 | luaL_argcheck(L, size <= ld && pos <= ld - size, |
344 | 2, "data string too short" ); |
345 | } |
346 | lua_pushlstring(L, data+pos, size); n++; |
347 | break; |
348 | } |
349 | case 's': { |
350 | const char *e = (const char *)memchr(data+pos, '\0', ld - pos); |
351 | if (e == NULL) |
352 | luaL_error(L, "unfinished string in data" ); |
353 | size = (e - (data+pos)) + 1; |
354 | lua_pushlstring(L, data+pos, size - 1); n++; |
355 | break; |
356 | } |
357 | default: controloptions(L, opt, &fmt, &h); |
358 | } |
359 | pos += size; |
360 | } |
361 | lua_pushinteger(L, pos + 1); /* next position */ |
362 | return n + 1; |
363 | } |
364 | |
365 | |
366 | static int b_size (lua_State *L) { |
367 | Header h; |
368 | const char *fmt = luaL_checkstring(L, 1); |
369 | size_t pos = 0; |
370 | defaultoptions(&h); |
371 | while (*fmt) { |
372 | int opt = *fmt++; |
373 | size_t size = optsize(L, opt, &fmt); |
374 | pos += gettoalign(pos, &h, opt, size); |
375 | if (opt == 's') |
376 | luaL_argerror(L, 1, "option 's' has no fixed size" ); |
377 | else if (opt == 'c' && size == 0) |
378 | luaL_argerror(L, 1, "option 'c0' has no fixed size" ); |
379 | if (!isalnum(opt)) |
380 | controloptions(L, opt, &fmt, &h); |
381 | pos += size; |
382 | } |
383 | lua_pushinteger(L, pos); |
384 | return 1; |
385 | } |
386 | |
387 | /* }====================================================== */ |
388 | |
389 | |
390 | |
391 | static const struct luaL_Reg thislib[] = { |
392 | {"pack" , b_pack}, |
393 | {"unpack" , b_unpack}, |
394 | {"size" , b_size}, |
395 | {NULL, NULL} |
396 | }; |
397 | |
398 | |
399 | LUALIB_API int luaopen_struct (lua_State *L); |
400 | |
401 | LUALIB_API int luaopen_struct (lua_State *L) { |
402 | luaL_register(L, "struct" , thislib); |
403 | return 1; |
404 | } |
405 | |
406 | |
407 | /****************************************************************************** |
408 | * Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved. |
409 | * |
410 | * Permission is hereby granted, free of charge, to any person obtaining |
411 | * a copy of this software and associated documentation files (the |
412 | * "Software"), to deal in the Software without restriction, including |
413 | * without limitation the rights to use, copy, modify, merge, publish, |
414 | * distribute, sublicense, and/or sell copies of the Software, and to |
415 | * permit persons to whom the Software is furnished to do so, subject to |
416 | * the following conditions: |
417 | * |
418 | * The above copyright notice and this permission notice shall be |
419 | * included in all copies or substantial portions of the Software. |
420 | * |
421 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
422 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
423 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
424 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
425 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
426 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
427 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
428 | ******************************************************************************/ |
429 | |
430 | |