]>
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); | |
1df4df6d | 110 | int i_read = luaL_optinteger(L,3,1)-1; |
111 | // int i_read = luaL_optint(L,3,1)-1; | |
f057bddb | 112 | unsigned int i; |
113 | if (i_read >= 0) { | |
114 | i = i_read; | |
115 | } else { | |
116 | i = 0; | |
117 | } | |
118 | int n=0; | |
119 | int swap=0; | |
120 | int done=0; | |
121 | lua_pushnil(L); | |
122 | while (*f && done == 0) | |
123 | { | |
124 | int c=*f++; | |
125 | int N=1; | |
126 | if (isdigit((int) (unsigned char) *f)) | |
127 | { | |
128 | N=0; | |
129 | while (isdigit((int) (unsigned char) *f)) N=10*N+(*f++)-'0'; | |
130 | if (N==0 && c==OP_STRING) { lua_pushliteral(L,""); ++n; } | |
131 | } | |
132 | while (N-- && done == 0) switch (c) | |
133 | { | |
134 | case OP_LITTLEENDIAN: | |
135 | case OP_BIGENDIAN: | |
136 | case OP_NATIVE: | |
137 | { | |
138 | swap=doendian(c); | |
139 | N=0; | |
140 | break; | |
141 | } | |
142 | case OP_STRING: | |
143 | { | |
144 | ++N; | |
145 | if (i+N>len) {done = 1; break; } | |
146 | lua_pushlstring(L,s+i,N); | |
147 | i+=N; | |
148 | ++n; | |
149 | N=0; | |
150 | break; | |
151 | } | |
152 | case OP_ZSTRING: | |
153 | { | |
154 | size_t l; | |
155 | if (i>=len) {done = 1; break; } | |
156 | l=strlen(s+i); | |
157 | lua_pushlstring(L,s+i,l); | |
158 | i+=l+1; | |
159 | ++n; | |
160 | break; | |
161 | } | |
ca363c23 | 162 | |
163 | UNPACKSTRING(OP_BSTRING, uint8_t) | |
164 | UNPACKSTRING(OP_WSTRING, uint16_t) | |
165 | UNPACKSTRING(OP_SSTRING, uint32_t) | |
f057bddb | 166 | UNPACKNUMBER(OP_NUMBER, lua_Number) |
167 | UNPACKNUMBER(OP_DOUBLE, double) | |
168 | UNPACKNUMBER(OP_FLOAT, float) | |
ca363c23 | 169 | UNPACKNUMBER(OP_CHAR, int8_t) |
170 | UNPACKNUMBER(OP_BYTE, uint8_t) | |
171 | UNPACKNUMBER(OP_SHORT, int16_t) | |
172 | UNPACKNUMBER(OP_USHORT, uint16_t) | |
173 | UNPACKNUMBER(OP_INT, int32_t) | |
174 | UNPACKNUMBER(OP_UINT, uint32_t) | |
175 | UNPACKNUMBER(OP_LONG, int64_t) | |
176 | UNPACKNUMBER(OP_ULONG, uint64_t) | |
f057bddb | 177 | case OP_HEX: |
178 | { | |
179 | luaL_Buffer buf; | |
180 | char hdigit = '0'; | |
181 | int val = 0; | |
182 | luaL_buffinit(L,&buf); | |
183 | N++; | |
184 | if (i+N > len) {done = 1; break;} | |
185 | for (unsigned int ii = i; ii < i+N; ii++) { | |
186 | val = s[ii] & 0xF0; | |
187 | val = val >> 4; | |
188 | hdigit = HEXDIGITS(val); | |
189 | luaL_addlstring(&buf, &hdigit, 1); | |
190 | ||
191 | val = s[ii] & 0x0F; | |
192 | hdigit = HEXDIGITS(val); | |
193 | luaL_addlstring(&buf, &hdigit, 1); | |
194 | } | |
195 | luaL_pushresult(&buf); | |
196 | n++; | |
197 | i += N; | |
198 | N = 0; | |
199 | break; | |
200 | } | |
201 | ||
202 | case ' ': case ',': | |
203 | break; | |
204 | default: | |
205 | badcode(L,c); | |
206 | break; | |
207 | } | |
208 | } | |
209 | lua_pushnumber(L,i+1); | |
210 | lua_replace(L,-n-2); | |
211 | return n+1; | |
212 | } | |
213 | ||
214 | #define PACKNUMBER(OP,T) \ | |
215 | case OP: \ | |
216 | { \ | |
217 | T a=(T)luaL_checknumber(L,i++); \ | |
218 | doswap(swap,&a,sizeof(a)); \ | |
219 | luaL_addlstring(&b,(char*)&a,sizeof(a)); \ | |
220 | break; \ | |
221 | } | |
222 | ||
223 | #define PACKSTRING(OP,T) \ | |
224 | case OP: \ | |
225 | { \ | |
226 | size_t l; \ | |
227 | const char *a=luaL_checklstring(L,i++,&l); \ | |
228 | T ll=(T)l; \ | |
229 | doswap(swap,&ll,sizeof(ll)); \ | |
230 | luaL_addlstring(&b,(char*)&ll,sizeof(ll)); \ | |
231 | luaL_addlstring(&b,a,l); \ | |
232 | break; \ | |
233 | } | |
234 | ||
235 | static int l_pack(lua_State *L) /** pack(f,...) */ | |
236 | { | |
237 | int i=2; | |
238 | const char *f=luaL_checkstring(L,1); | |
239 | int swap=0; | |
240 | luaL_Buffer b; | |
241 | luaL_buffinit(L,&b); | |
242 | while (*f) | |
243 | { | |
244 | int c=*f++; | |
245 | int N=1; | |
246 | if (isdigit((int) (unsigned char) *f)) | |
247 | { | |
248 | N=0; | |
249 | while (isdigit((int) (unsigned char) *f)) N=10*N+(*f++)-'0'; | |
250 | } | |
251 | while (N--) switch (c) | |
252 | { | |
253 | case OP_LITTLEENDIAN: | |
254 | case OP_BIGENDIAN: | |
255 | case OP_NATIVE: | |
256 | { | |
257 | swap=doendian(c); | |
258 | N=0; | |
259 | break; | |
260 | } | |
261 | case OP_STRING: | |
262 | case OP_ZSTRING: | |
263 | { | |
264 | size_t l; | |
265 | const char *a=luaL_checklstring(L,i++,&l); | |
266 | luaL_addlstring(&b,a,l+(c==OP_ZSTRING)); | |
267 | break; | |
268 | } | |
ca363c23 | 269 | PACKSTRING(OP_BSTRING, uint8_t) |
270 | PACKSTRING(OP_WSTRING, uint16_t) | |
271 | PACKSTRING(OP_SSTRING, uint32_t) | |
f057bddb | 272 | PACKNUMBER(OP_NUMBER, lua_Number) |
273 | PACKNUMBER(OP_DOUBLE, double) | |
274 | PACKNUMBER(OP_FLOAT, float) | |
ca363c23 | 275 | PACKNUMBER(OP_CHAR, int8_t) |
276 | PACKNUMBER(OP_BYTE, uint8_t) | |
277 | PACKNUMBER(OP_SHORT, int16_t) | |
278 | PACKNUMBER(OP_USHORT, uint16_t) | |
279 | PACKNUMBER(OP_INT, int32_t) | |
280 | PACKNUMBER(OP_UINT, uint32_t) | |
281 | PACKNUMBER(OP_LONG, int64_t) | |
282 | PACKNUMBER(OP_ULONG, uint64_t) | |
f057bddb | 283 | case OP_HEX: |
284 | { // doing digit parsing the lpack way | |
285 | unsigned char sbyte = 0; | |
286 | size_t l; | |
287 | unsigned int ii = 0; | |
288 | int odd = 0; | |
289 | const char *a = luaL_checklstring(L, i++, &l); | |
290 | for (ii = 0; ii < l; ii++) { | |
291 | if (isxdigit((int) (unsigned char) a[ii])) { | |
292 | if (isdigit((int) (unsigned char) a[ii])) { | |
293 | sbyte += a[ii] - '0'; | |
294 | odd++; | |
295 | } else if (a[ii] >= 'A' && a[ii] <= 'F') { | |
296 | sbyte += a[ii] - 'A' + 10; | |
297 | odd++; | |
298 | } else if (a[ii] >= 'a' && a[ii] <= 'f') { | |
299 | sbyte += a[ii] - 'a' + 10; | |
300 | odd++; | |
301 | } | |
302 | if (odd == 1) { | |
303 | sbyte = sbyte << 4; | |
304 | } else if (odd == 2) { | |
305 | luaL_addlstring(&b, (char *) &sbyte, 1); | |
306 | sbyte = 0; | |
307 | odd = 0; | |
308 | } | |
309 | } else if (isspace(a[ii])) { | |
310 | /* ignore */ | |
311 | } else { | |
312 | /* err ... ignore too*/ | |
313 | } | |
314 | } | |
315 | if (odd == 1) { | |
316 | luaL_addlstring(&b, (char *) &sbyte, 1); | |
317 | } | |
318 | break; | |
319 | } | |
320 | case ' ': case ',': | |
321 | break; | |
322 | default: | |
323 | badcode(L,c); | |
324 | break; | |
325 | } | |
326 | } | |
327 | luaL_pushresult(&b); | |
328 | return 1; | |
329 | } | |
330 | ||
331 | static const luaL_Reg binlib[] = | |
332 | { | |
333 | {"pack", l_pack}, | |
334 | {"unpack", l_unpack}, | |
335 | {NULL, NULL} | |
336 | }; | |
337 | ||
338 | LUALIB_API int luaopen_binlib (lua_State *L) { | |
339 | luaL_newlib(L, binlib); | |
340 | return 1; | |
341 | } | |
342 | /* | |
343 | ** Open bin library | |
344 | */ | |
345 | int set_bin_library (lua_State *L) { | |
346 | ||
347 | luaL_requiref(L, "bin", luaopen_binlib, 1); | |
348 | lua_pop(L, 1); | |
349 | return 1; | |
350 | } |