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