$search
00001 00002 #include <assert.h> 00003 #include <ctype.h> 00004 #include <limits.h> 00005 #include <string.h> 00006 00007 00008 #include "lua.h" 00009 #include "lauxlib.h" 00010 00011 00012 /* 00013 ** {====================================================== 00014 ** Library for packing/unpacking structures. 00015 ** $Id: struct.c,v 1.2 2008/04/18 20:06:01 roberto Exp $ 00016 ** ======================================================= 00017 */ 00018 /* 00019 ** Valid formats: 00020 ** > - big endian 00021 ** < - little endian 00022 ** ![num] - alignment 00023 ** x - pading 00024 ** b/B - signed/unsigned byte 00025 ** h/H - signed/unsigned short 00026 ** l/L - signed/unsigned long 00027 ** i/In - signed/unsigned integer with size `n' (default is size of int) 00028 ** cn - sequence of `n' chars (from/to a string); when packing, n==0 means 00029 the whole string; when unpacking, n==0 means use the previous 00030 read number as the string length 00031 ** s - zero-terminated string 00032 ** f - float 00033 ** d - doulbe 00034 ** ' ' - ignored 00035 */ 00036 00037 00038 /* is 'x' a power of 2? */ 00039 #define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0) 00040 00041 /* dummy structure to get alignment requirements */ 00042 struct cD { 00043 char c; 00044 double d; 00045 }; 00046 00047 00048 #define PADDING (sizeof(struct cD) - sizeof(double)) 00049 #define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int)) 00050 00051 00052 /* endian options */ 00053 #define BIG 0 00054 #define LITTLE 1 00055 00056 00057 static union { 00058 int dummy; 00059 char endian; 00060 } const native = {1}; 00061 00062 00063 typedef struct Header { 00064 int endian; 00065 int align; 00066 } Header; 00067 00068 00069 static size_t getnum (const char **fmt, size_t df) { 00070 if (!isdigit(**fmt)) /* no number? */ 00071 return df; /* return default value */ 00072 else { 00073 size_t a = 0; 00074 do { 00075 a = a*10 + *((*fmt)++) - '0'; 00076 } while (isdigit(**fmt)); 00077 return a; 00078 } 00079 } 00080 00081 00082 #define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1) 00083 00084 00085 00086 static size_t optsize (lua_State *L, char opt, const char **fmt) { 00087 switch (opt) { 00088 case 'B': case 'b': return sizeof(char); 00089 case 'H': case 'h': return sizeof(short); 00090 case 'L': case 'l': return sizeof(long); 00091 case 'f': return sizeof(float); 00092 case 'd': return sizeof(double); 00093 case 'x': return 1; 00094 case 'c': return getnum(fmt, 1); 00095 case 's': case ' ': case '<': case '>': case '!': return 0; 00096 case 'i': case 'I': { 00097 int sz = getnum(fmt, sizeof(int)); 00098 if (!isp2(sz)) 00099 luaL_error(L, "integral size %d is not a power of 2", sz); 00100 return sz; 00101 } 00102 default: { 00103 const char *msg = lua_pushfstring(L, "invalid format option [%c]", opt); 00104 return luaL_argerror(L, 1, msg); 00105 } 00106 } 00107 } 00108 00109 00110 static int gettoalign (size_t len, Header *h, int opt, size_t size) { 00111 if (size == 0 || opt == 'c') return 0; 00112 if (size > (size_t)h->align) size = h->align; /* respect max. alignment */ 00113 return (size - (len & (size - 1))) & (size - 1); 00114 } 00115 00116 00117 static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) { 00118 switch (opt) { 00119 case ' ': return; /* ignore white spaces */ 00120 case '>': h->endian = BIG; return; 00121 case '<': h->endian = LITTLE; return; 00122 case '!': { 00123 int a = getnum(fmt, MAXALIGN); 00124 if (!isp2(a)) 00125 luaL_error(L, "alignment %d is not a power of 2", a); 00126 h->align = a; 00127 return; 00128 } 00129 default: assert(0); 00130 } 00131 } 00132 00133 00134 static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian, 00135 int size) { 00136 lua_Number n = luaL_checknumber(L, arg); 00137 unsigned long value; 00138 if (n < (lua_Number)LONG_MAX) 00139 value = (long)n; 00140 else 00141 value = (unsigned long)n; 00142 if (endian == LITTLE) { 00143 int i; 00144 for (i = 0; i < size; i++) 00145 luaL_addchar(b, (value >> 8*i) & 0xff); 00146 } 00147 else { 00148 int i; 00149 for (i = size - 1; i >= 0; i--) 00150 luaL_addchar(b, (value >> 8*i) & 0xff); 00151 } 00152 } 00153 00154 00155 static void correctbytes (char *b, int size, int endian) { 00156 if (endian != native.endian) { 00157 int i = 0; 00158 while (i < --size) { 00159 char temp = b[i]; 00160 b[i++] = b[size]; 00161 b[size] = temp; 00162 } 00163 } 00164 } 00165 00166 00167 static int b_pack (lua_State *L) { 00168 luaL_Buffer b; 00169 const char *fmt = luaL_checkstring(L, 1); 00170 Header h; 00171 int arg = 2; 00172 size_t totalsize = 0; 00173 defaultoptions(&h); 00174 lua_pushnil(L); /* mark to separate arguments from string buffer */ 00175 luaL_buffinit(L, &b); 00176 while (*fmt != '\0') { 00177 int opt = *fmt++; 00178 size_t size = optsize(L, opt, &fmt); 00179 int toalign = gettoalign(totalsize, &h, opt, size); 00180 totalsize += toalign; 00181 while (toalign-- > 0) luaL_putchar(&b, '\0'); 00182 switch (opt) { 00183 case 'b': case 'B': case 'h': case 'H': 00184 case 'l': case 'L': case 'i': case 'I': { /* integer types */ 00185 putinteger(L, &b, arg++, h.endian, size); 00186 break; 00187 } 00188 case 'x': { 00189 luaL_putchar(&b, '\0'); 00190 break; 00191 } 00192 case 'f': { 00193 float f = (float)luaL_checknumber(L, arg++); 00194 correctbytes((char *)&f, size, h.endian); 00195 luaL_addlstring(&b, (char *)&f, size); 00196 break; 00197 } 00198 case 'd': { 00199 double d = luaL_checknumber(L, arg++); 00200 correctbytes((char *)&d, size, h.endian); 00201 luaL_addlstring(&b, (char *)&d, size); 00202 break; 00203 } 00204 case 'c': case 's': { 00205 size_t l; 00206 const char *s = luaL_checklstring(L, arg++, &l); 00207 if (size == 0) size = l; 00208 luaL_argcheck(L, l >= (size_t)size, arg, "string too short"); 00209 luaL_addlstring(&b, s, size); 00210 if (opt == 's') { 00211 luaL_putchar(&b, '\0'); /* add zero at the end */ 00212 size++; 00213 } 00214 break; 00215 } 00216 default: commoncases(L, opt, &fmt, &h); 00217 } 00218 totalsize += size; 00219 } 00220 luaL_pushresult(&b); 00221 return 1; 00222 } 00223 00224 00225 static lua_Number getinteger (const char *buff, int endian, 00226 int issigned, int size) { 00227 unsigned long l = 0; 00228 if (endian == BIG) { 00229 int i; 00230 for (i = 0; i < size; i++) 00231 l |= (unsigned long)(unsigned char)buff[size - i - 1] << (i*8); 00232 } 00233 else { 00234 int i; 00235 for (i = 0; i < size; i++) 00236 l |= (unsigned long)(unsigned char)buff[i] << (i*8); 00237 } 00238 if (!issigned) 00239 return (lua_Number)l; 00240 else { /* signed format */ 00241 unsigned long mask = ~(0UL) << (size*8 - 1); 00242 if (l & mask) /* negative value? */ 00243 l |= mask; /* signal extension */ 00244 return (lua_Number)(long)l; 00245 } 00246 } 00247 00248 00249 static int b_size (lua_State *L) { 00250 const char *fmt = luaL_checkstring(L, 1); 00251 //printf("Processing %s\n", fmt); 00252 size_t totalsize = 0; 00253 if (lua_isnoneornil(L, 2) == 1) { /* Just the "base" size, e.g. ignore c0 */ 00254 while (*fmt != '\0') { 00255 int opt = *fmt++; 00256 size_t size = optsize(L, opt, &fmt); 00257 totalsize += size; 00258 } 00259 } else { 00260 Header h; 00261 size_t ld; 00262 const char *data = luaL_checklstring(L, 2, &ld); 00263 size_t pos = luaL_optinteger(L, 3, 1) - 1; 00264 defaultoptions(&h); 00265 lua_settop(L, 2); 00266 while (*fmt) { 00267 int opt = *fmt++; 00268 size_t size = optsize(L, opt, &fmt); 00269 pos += gettoalign(pos, &h, opt, size); 00270 luaL_argcheck(L, pos+size <= ld, 2, "general data string too short"); 00271 switch (opt) { 00272 case 'b': case 'B': case 'h': case 'H': 00273 case 'l': case 'L': case 'i': case 'I': { /* integer types */ 00274 int issigned = islower(opt); 00275 lua_Number res = getinteger(data+pos, h.endian, issigned, size); 00276 lua_pushnumber(L, res); 00277 break; 00278 } 00279 case 'x': { 00280 break; 00281 } 00282 case 'f': { 00283 float f; 00284 memcpy(&f, data+pos, size); 00285 correctbytes((char *)&f, sizeof(f), h.endian); 00286 lua_pushnumber(L, f); 00287 break; 00288 } 00289 case 'd': { 00290 double d; 00291 memcpy(&d, data+pos, size); 00292 correctbytes((char *)&d, sizeof(d), h.endian); 00293 lua_pushnumber(L, d); 00294 break; 00295 } 00296 case 'c': { 00297 if (size == 0) { 00298 if (!lua_isnumber(L, -1)) 00299 luaL_error(L, "format `c0' needs a previous size"); 00300 size = lua_tonumber(L, -1); 00301 lua_pop(L, 1); 00302 luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); 00303 } 00304 lua_pushlstring(L, data+pos, size); 00305 break; 00306 } 00307 case 's': { 00308 const char *e = (const char *)memchr(data+pos, '\0', ld - pos); 00309 if (e == NULL) 00310 luaL_error(L, "unfinished string in data"); 00311 size = (e - (data+pos)) + 1; 00312 lua_pushlstring(L, data+pos, size - 1); 00313 break; 00314 } 00315 default: commoncases(L, opt, &fmt, &h); 00316 } 00317 pos += size; 00318 totalsize += size; 00319 } 00320 } 00321 //printf("Pushing size %zu\n", totalsize); 00322 lua_settop(L, 2); /* Remove "result values", we're not interested */ 00323 lua_pushinteger(L, totalsize); 00324 return 1; 00325 } 00326 00327 00328 static int b_unpack (lua_State *L) { 00329 Header h; 00330 const char *fmt = luaL_checkstring(L, 1); 00331 size_t ld; 00332 const char *data = luaL_checklstring(L, 2, &ld); 00333 size_t pos = luaL_optinteger(L, 3, 1) - 1; 00334 defaultoptions(&h); 00335 lua_settop(L, 2); 00336 while (*fmt) { 00337 int opt = *fmt++; 00338 size_t size = optsize(L, opt, &fmt); 00339 pos += gettoalign(pos, &h, opt, size); 00340 luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); 00341 switch (opt) { 00342 case 'b': case 'B': case 'h': case 'H': 00343 case 'l': case 'L': case 'i': case 'I': { /* integer types */ 00344 int issigned = islower(opt); 00345 lua_Number res = getinteger(data+pos, h.endian, issigned, size); 00346 lua_pushnumber(L, res); 00347 break; 00348 } 00349 case 'x': { 00350 break; 00351 } 00352 case 'f': { 00353 float f; 00354 memcpy(&f, data+pos, size); 00355 correctbytes((char *)&f, sizeof(f), h.endian); 00356 lua_pushnumber(L, f); 00357 break; 00358 } 00359 case 'd': { 00360 double d; 00361 memcpy(&d, data+pos, size); 00362 correctbytes((char *)&d, sizeof(d), h.endian); 00363 lua_pushnumber(L, d); 00364 break; 00365 } 00366 case 'c': { 00367 if (size == 0) { 00368 if (!lua_isnumber(L, -1)) 00369 luaL_error(L, "format `c0' needs a previous size"); 00370 size = lua_tonumber(L, -1); 00371 lua_pop(L, 1); 00372 luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); 00373 } 00374 lua_pushlstring(L, data+pos, size); 00375 break; 00376 } 00377 case 's': { 00378 const char *e = (const char *)memchr(data+pos, '\0', ld - pos); 00379 if (e == NULL) 00380 luaL_error(L, "unfinished string in data"); 00381 size = (e - (data+pos)) + 1; 00382 lua_pushlstring(L, data+pos, size - 1); 00383 break; 00384 } 00385 default: commoncases(L, opt, &fmt, &h); 00386 } 00387 pos += size; 00388 } 00389 lua_pushinteger(L, pos + 1); 00390 return lua_gettop(L) - 2; 00391 } 00392 00393 00394 static int b_unpack_table (lua_State *L) { 00395 Header h; 00396 const char *fmt = luaL_checkstring(L, 1); 00397 size_t ld; 00398 const char *data = luaL_checklstring(L, 2, &ld); 00399 size_t pos = luaL_optinteger(L, 3, 1) - 1; 00400 defaultoptions(&h); 00401 lua_settop(L, 2); 00402 00403 int arrind = 0; 00404 00405 lua_newtable(L); 00406 00407 while (*fmt) { 00408 int opt = *fmt++; 00409 size_t size = optsize(L, opt, &fmt); 00410 pos += gettoalign(pos, &h, opt, size); 00411 luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); 00412 switch (opt) { 00413 case 'b': case 'B': case 'h': case 'H': 00414 case 'l': case 'L': case 'i': case 'I': { /* integer types */ 00415 int issigned = islower(opt); 00416 lua_Number res = getinteger(data+pos, h.endian, issigned, size); 00417 lua_pushinteger(L, ++arrind); 00418 lua_pushnumber(L, res); 00419 lua_settable(L, 3); 00420 break; 00421 } 00422 case 'x': { 00423 break; 00424 } 00425 case 'f': { 00426 float f; 00427 memcpy(&f, data+pos, size); 00428 correctbytes((char *)&f, sizeof(f), h.endian); 00429 lua_pushinteger(L, ++arrind); 00430 lua_pushnumber(L, f); 00431 lua_settable(L, 3); 00432 break; 00433 } 00434 case 'd': { 00435 double d; 00436 memcpy(&d, data+pos, size); 00437 correctbytes((char *)&d, sizeof(d), h.endian); 00438 lua_pushinteger(L, ++arrind); 00439 lua_pushnumber(L, d); 00440 lua_settable(L, 3); 00441 break; 00442 } 00443 case 'c': { 00444 if (size == 0) { 00445 lua_pushinteger(L, arrind); 00446 lua_gettable(L, 3); 00447 if (!lua_isnumber(L, -1)) 00448 luaL_error(L, "format `c0' needs a previous size"); 00449 size = lua_tonumber(L, -1); 00450 lua_pop(L, 1); 00451 luaL_argcheck(L, pos+size <= ld, 2, "data string too short"); 00452 lua_pushinteger(L, arrind--); 00453 lua_pushnil(L); 00454 lua_settable(L, 3); 00455 } 00456 lua_pushinteger(L, ++arrind); 00457 lua_pushlstring(L, data+pos, size); 00458 lua_settable(L, 3); 00459 break; 00460 } 00461 case 's': { 00462 const char *e = (const char *)memchr(data+pos, '\0', ld - pos); 00463 if (e == NULL) 00464 luaL_error(L, "unfinished string in data"); 00465 size = (e - (data+pos)) + 1; 00466 lua_pushinteger(L, ++arrind); 00467 lua_pushlstring(L, data+pos, size - 1); 00468 lua_settable(L, 3); 00469 break; 00470 } 00471 default: commoncases(L, opt, &fmt, &h); 00472 } 00473 pos += size; 00474 } 00475 lua_pushinteger(L, ++arrind); 00476 lua_pushinteger(L, pos + 1); 00477 lua_settable(L, 3); 00478 return lua_gettop(L) - 2; 00479 } 00480 00481 /* }====================================================== */ 00482 00483 00484 00485 static const struct luaL_reg thislib[] = { 00486 {"pack", b_pack}, 00487 {"unpack", b_unpack}, 00488 {"unpack_table", b_unpack_table}, 00489 {"size", b_size}, 00490 {NULL, NULL} 00491 }; 00492 00493 00494 LUALIB_API int luaopen_struct (lua_State *L) { 00495 luaL_register(L, "struct", thislib); 00496 return 1; 00497 } 00498 00499