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