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