Software /
code /
prosody
Comparison
util-src/struct.c @ 12354:3ce3633527af
util.struct: Import Roberto 'struct' library v0.3
Downloaded from http://www.inf.puc-rio.br/~roberto/struct/
This is for compatibility with Lua 5.2 (and 5.1). Eventually we can replace
this with string.pack/string.unpack which are available in 5.3+.
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 04 Mar 2022 15:03:02 +0000 |
child | 12387:05c250fa335a |
comparison
equal
deleted
inserted
replaced
12353:5ace23519e71 | 12354:3ce3633527af |
---|---|
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 |