]>
Commit | Line | Data |
---|---|---|
f057bddb | 1 | /* |
2 | * lpack.c | |
3 | * a Lua library for packing and unpacking binary data | |
4 | * Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> | |
5 | * 29 Jun 2007 19:27:20 | |
6 | * This code is hereby placed in the public domain. | |
7 | * with contributions from Ignacio Castao <castanyo@yahoo.es> and | |
8 | * Roberto Ierusalimschy <roberto@inf.puc-rio.br>. | |
9 | */ | |
10 | ||
11 | #define OP_ZSTRING 'z' /* zero-terminated string */ | |
12 | #define OP_BSTRING 'p' /* string preceded by length byte */ | |
13 | #define OP_WSTRING 'P' /* string preceded by length word */ | |
14 | #define OP_SSTRING 'a' /* string preceded by length size_t */ | |
15 | #define OP_STRING 'A' /* string */ | |
16 | #define OP_FLOAT 'f' /* float */ | |
17 | #define OP_DOUBLE 'd' /* double */ | |
18 | #define OP_NUMBER 'n' /* Lua number */ | |
19 | #define OP_CHAR 'c' /* char (1-byte int) */ | |
20 | #define OP_BYTE 'C' /* byte = unsigned char (1-byte unsigned int) */ | |
21 | #define OP_SHORT 's' /* short (2-byte int) */ | |
22 | #define OP_USHORT 'S' /* unsigned short (2-byte unsigned int) */ | |
23 | #define OP_INT 'i' /* int (4-byte int) */ | |
24 | #define OP_UINT 'I' /* unsigned int (4-byte unsigned int) */ | |
25 | #define OP_LONG 'l' /* long (8-byte int) */ | |
26 | #define OP_ULONG 'L' /* unsigned long (8-byte unsigned int) */ | |
27 | #define OP_LITTLEENDIAN '<' /* little endian */ | |
28 | #define OP_BIGENDIAN '>' /* big endian */ | |
29 | #define OP_NATIVE '=' /* native endian */ | |
30 | ||
31 | #define OP_HEX 'H' | |
32 | ||
33 | #include <ctype.h> | |
34 | #include <string.h> | |
35 | #include <lua.h> | |
36 | #include <lualib.h> | |
37 | #include <lauxlib.h> | |
ca363c23 | 38 | #include <stdint.h> |
f057bddb | 39 | #include "pm3_binlib.h" |
40 | ||
41 | ||
42 | static void badcode(lua_State *L, int c) | |
43 | { | |
44 | char s[]="bad code `?'"; | |
45 | s[sizeof(s)-3]=c; | |
46 | luaL_argerror(L,1,s); | |
47 | } | |
48 | ||
49 | static int doendian(int c) | |
50 | { | |
51 | int x=1; | |
52 | int e=*(char*)&x; | |
53 | if (c==OP_LITTLEENDIAN) return !e; | |
54 | if (c==OP_BIGENDIAN) return e; | |
55 | if (c==OP_NATIVE) return 0; | |
56 | return 0; | |
57 | } | |
58 | ||
59 | static void doswap(int swap, void *p, size_t n) | |
60 | { | |
61 | if (swap) | |
62 | { | |
63 | char *a=(char*)p; | |
64 | int i,j; | |
65 | for (i=0, j=n-1, n=n/2; n--; i++, j--) | |
66 | { | |
67 | char t=a[i]; a[i]=a[j]; a[j]=t; | |
68 | } | |
69 | } | |
70 | } | |
71 | ||
72 | #define UNPACKNUMBER(OP,T) \ | |
73 | case OP: \ | |
74 | { \ | |
75 | T a; \ | |
76 | int m=sizeof(a); \ | |
77 | if (i+m>len) { done = 1; break;} \ | |
78 | memcpy(&a,s+i,m); \ | |
79 | i+=m; \ | |
80 | doswap(swap,&a,m); \ | |
81 | lua_pushnumber(L,(lua_Number)a); \ | |
82 | ++n; \ | |
83 | break; \ | |
84 | } | |
85 | ||
86 | #define UNPACKSTRING(OP,T) \ | |
87 | case OP: \ | |
88 | { \ | |
89 | T l; \ | |
90 | int m=sizeof(l); \ | |
91 | if (i+m>len) { done = 1; break; } \ | |
92 | memcpy(&l,s+i,m); \ | |
93 | doswap(swap,&l,m); \ | |
94 | if (i+m+l>len) { done = 1; break;} \ | |
95 | i+=m; \ | |
96 | lua_pushlstring(L,s+i,l); \ | |
97 | i+=l; \ | |
98 | ++n; \ | |
99 | break; \ | |
100 | } | |
101 | ||
102 | #define HEXDIGITS(DIG) \ | |
103 | "0123456789ABCDEF"[DIG] | |
104 | ||
105 | static int l_unpack(lua_State *L) /** unpack(f,s, [init]) */ | |
106 | { | |
107 | size_t len; | |
108 | const char *s=luaL_checklstring(L,2,&len); /* switched s and f */ | |
109 | const char *f=luaL_checkstring(L,1); | |
110 | int i_read = luaL_optint(L,3,1)-1; | |
111 | unsigned int i; | |
112 | if (i_read >= 0) { | |
113 | i = i_read; | |
114 | } else { | |
115 | i = 0; | |
116 | } | |
117 | int n=0; | |
118 | int swap=0; | |
119 | int done=0; | |
120 | lua_pushnil(L); | |
121 | while (*f && done == 0) | |
122 | { | |
123 | int c=*f++; | |
124 | int N=1; | |
125 | if (isdigit((int) (unsigned char) *f)) | |
126 | { | |
127 | N=0; | |
128 | while (isdigit((int) (unsigned char) *f)) N=10*N+(*f++)-'0'; | |
129 | if (N==0 && c==OP_STRING) { lua_pushliteral(L,""); ++n; } | |
130 | } | |
131 | while (N-- && done == 0) switch (c) | |
132 | { | |
133 | case OP_LITTLEENDIAN: | |
134 | case OP_BIGENDIAN: | |
135 | case OP_NATIVE: | |
136 | { | |
137 | swap=doendian(c); | |
138 | N=0; | |
139 | break; | |
140 | } | |
141 | case OP_STRING: | |
142 | { | |
143 | ++N; | |
144 | if (i+N>len) {done = 1; break; } | |
145 | lua_pushlstring(L,s+i,N); | |
146 | i+=N; | |
147 | ++n; | |
148 | N=0; | |
149 | break; | |
150 | } | |
151 | case OP_ZSTRING: | |
152 | { | |
153 | size_t l; | |
154 | if (i>=len) {done = 1; break; } | |
155 | l=strlen(s+i); | |
156 | lua_pushlstring(L,s+i,l); | |
157 | i+=l+1; | |
158 | ++n; | |
159 | break; | |
160 | } | |
ca363c23 | 161 | |
162 | UNPACKSTRING(OP_BSTRING, uint8_t) | |
163 | UNPACKSTRING(OP_WSTRING, uint16_t) | |
164 | UNPACKSTRING(OP_SSTRING, uint32_t) | |
f057bddb | 165 | UNPACKNUMBER(OP_NUMBER, lua_Number) |
166 | UNPACKNUMBER(OP_DOUBLE, double) | |
167 | UNPACKNUMBER(OP_FLOAT, float) | |
ca363c23 | 168 | UNPACKNUMBER(OP_CHAR, int8_t) |
169 | UNPACKNUMBER(OP_BYTE, uint8_t) | |
170 | UNPACKNUMBER(OP_SHORT, int16_t) | |
171 | UNPACKNUMBER(OP_USHORT, uint16_t) | |
172 | UNPACKNUMBER(OP_INT, int32_t) | |
173 | UNPACKNUMBER(OP_UINT, uint32_t) | |
174 | UNPACKNUMBER(OP_LONG, int64_t) | |
175 | UNPACKNUMBER(OP_ULONG, uint64_t) | |
f057bddb | 176 | case OP_HEX: |
177 | { | |
178 | luaL_Buffer buf; | |
179 | char hdigit = '0'; | |
180 | int val = 0; | |
181 | luaL_buffinit(L,&buf); | |
182 | N++; | |
183 | if (i+N > len) {done = 1; break;} | |
184 | for (unsigned int ii = i; ii < i+N; ii++) { | |
185 | val = s[ii] & 0xF0; | |
186 | val = val >> 4; | |
187 | hdigit = HEXDIGITS(val); | |
188 | luaL_addlstring(&buf, &hdigit, 1); | |
189 | ||
190 | val = s[ii] & 0x0F; | |
191 | hdigit = HEXDIGITS(val); | |
192 | luaL_addlstring(&buf, &hdigit, 1); | |
193 | } | |
194 | luaL_pushresult(&buf); | |
195 | n++; | |
196 | i += N; | |
197 | N = 0; | |
198 | break; | |
199 | } | |
200 | ||
201 | case ' ': case ',': | |
202 | break; | |
203 | default: | |
204 | badcode(L,c); | |
205 | break; | |
206 | } | |
207 | } | |
208 | lua_pushnumber(L,i+1); | |
209 | lua_replace(L,-n-2); | |
210 | return n+1; | |
211 | } | |
212 | ||
213 | #define PACKNUMBER(OP,T) \ | |
214 | case OP: \ | |
215 | { \ | |
216 | T a=(T)luaL_checknumber(L,i++); \ | |
217 | doswap(swap,&a,sizeof(a)); \ | |
218 | luaL_addlstring(&b,(char*)&a,sizeof(a)); \ | |
219 | break; \ | |
220 | } | |
221 | ||
222 | #define PACKSTRING(OP,T) \ | |
223 | case OP: \ | |
224 | { \ | |
225 | size_t l; \ | |
226 | const char *a=luaL_checklstring(L,i++,&l); \ | |
227 | T ll=(T)l; \ | |
228 | doswap(swap,&ll,sizeof(ll)); \ | |
229 | luaL_addlstring(&b,(char*)&ll,sizeof(ll)); \ | |
230 | luaL_addlstring(&b,a,l); \ | |
231 | break; \ | |
232 | } | |
233 | ||
234 | static int l_pack(lua_State *L) /** pack(f,...) */ | |
235 | { | |
236 | int i=2; | |
237 | const char *f=luaL_checkstring(L,1); | |
238 | int swap=0; | |
239 | luaL_Buffer b; | |
240 | luaL_buffinit(L,&b); | |
241 | while (*f) | |
242 | { | |
243 | int c=*f++; | |
244 | int N=1; | |
245 | if (isdigit((int) (unsigned char) *f)) | |
246 | { | |
247 | N=0; | |
248 | while (isdigit((int) (unsigned char) *f)) N=10*N+(*f++)-'0'; | |
249 | } | |
250 | while (N--) switch (c) | |
251 | { | |
252 | case OP_LITTLEENDIAN: | |
253 | case OP_BIGENDIAN: | |
254 | case OP_NATIVE: | |
255 | { | |
256 | swap=doendian(c); | |
257 | N=0; | |
258 | break; | |
259 | } | |
260 | case OP_STRING: | |
261 | case OP_ZSTRING: | |
262 | { | |
263 | size_t l; | |
264 | const char *a=luaL_checklstring(L,i++,&l); | |
265 | luaL_addlstring(&b,a,l+(c==OP_ZSTRING)); | |
266 | break; | |
267 | } | |
ca363c23 | 268 | PACKSTRING(OP_BSTRING, uint8_t) |
269 | PACKSTRING(OP_WSTRING, uint16_t) | |
270 | PACKSTRING(OP_SSTRING, uint32_t) | |
f057bddb | 271 | PACKNUMBER(OP_NUMBER, lua_Number) |
272 | PACKNUMBER(OP_DOUBLE, double) | |
273 | PACKNUMBER(OP_FLOAT, float) | |
ca363c23 | 274 | PACKNUMBER(OP_CHAR, int8_t) |
275 | PACKNUMBER(OP_BYTE, uint8_t) | |
276 | PACKNUMBER(OP_SHORT, int16_t) | |
277 | PACKNUMBER(OP_USHORT, uint16_t) | |
278 | PACKNUMBER(OP_INT, int32_t) | |
279 | PACKNUMBER(OP_UINT, uint32_t) | |
280 | PACKNUMBER(OP_LONG, int64_t) | |
281 | PACKNUMBER(OP_ULONG, uint64_t) | |
f057bddb | 282 | case OP_HEX: |
283 | { // doing digit parsing the lpack way | |
284 | unsigned char sbyte = 0; | |
285 | size_t l; | |
286 | unsigned int ii = 0; | |
287 | int odd = 0; | |
288 | const char *a = luaL_checklstring(L, i++, &l); | |
289 | for (ii = 0; ii < l; ii++) { | |
290 | if (isxdigit((int) (unsigned char) a[ii])) { | |
291 | if (isdigit((int) (unsigned char) a[ii])) { | |
292 | sbyte += a[ii] - '0'; | |
293 | odd++; | |
294 | } else if (a[ii] >= 'A' && a[ii] <= 'F') { | |
295 | sbyte += a[ii] - 'A' + 10; | |
296 | odd++; | |
297 | } else if (a[ii] >= 'a' && a[ii] <= 'f') { | |
298 | sbyte += a[ii] - 'a' + 10; | |
299 | odd++; | |
300 | } | |
301 | if (odd == 1) { | |
302 | sbyte = sbyte << 4; | |
303 | } else if (odd == 2) { | |
304 | luaL_addlstring(&b, (char *) &sbyte, 1); | |
305 | sbyte = 0; | |
306 | odd = 0; | |
307 | } | |
308 | } else if (isspace(a[ii])) { | |
309 | /* ignore */ | |
310 | } else { | |
311 | /* err ... ignore too*/ | |
312 | } | |
313 | } | |
314 | if (odd == 1) { | |
315 | luaL_addlstring(&b, (char *) &sbyte, 1); | |
316 | } | |
317 | break; | |
318 | } | |
319 | case ' ': case ',': | |
320 | break; | |
321 | default: | |
322 | badcode(L,c); | |
323 | break; | |
324 | } | |
325 | } | |
326 | luaL_pushresult(&b); | |
327 | return 1; | |
328 | } | |
329 | ||
330 | static const luaL_Reg binlib[] = | |
331 | { | |
332 | {"pack", l_pack}, | |
333 | {"unpack", l_unpack}, | |
334 | {NULL, NULL} | |
335 | }; | |
336 | ||
337 | LUALIB_API int luaopen_binlib (lua_State *L) { | |
338 | luaL_newlib(L, binlib); | |
339 | return 1; | |
340 | } | |
341 | /* | |
342 | ** Open bin library | |
343 | */ | |
344 | int set_bin_library (lua_State *L) { | |
345 | ||
346 | luaL_requiref(L, "bin", luaopen_binlib, 1); | |
347 | lua_pop(L, 1); | |
348 | return 1; | |
349 | } | |
350 |