]>
Commit | Line | Data |
---|---|---|
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 | ******************************************************************************/ |