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