1 /* 2 ** {====================================================== 3 ** Library for packing/unpacking structures. 4 ** $Id: struct.c,v 1.7 2018/05/11 22:04:31 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); 297 luaL_argcheck(L, pos > 0, 3, "offset must be 1 or greater"); 298 pos--; /* Lua indexes are 1-based, but here we want 0-based for C 299 * pointer math. */ 300 int n = 0; /* number of results */ 301 defaultoptions(&h); 302 while (*fmt) { 303 int opt = *fmt++; 304 size_t size = optsize(L, opt, &fmt); 305 pos += gettoalign(pos, &h, opt, size); 306 luaL_argcheck(L, size <= ld && pos <= ld - size, 307 2, "data string too short"); 308 /* stack space for item + next position */ 309 luaL_checkstack(L, 2, "too many results"); 310 switch (opt) { 311 case 'b': case 'B': case 'h': case 'H': 312 case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ 313 int issigned = islower(opt); 314 lua_Number res = getinteger(data+pos, h.endian, issigned, size); 315 lua_pushnumber(L, res); n++; 316 break; 317 } 318 case 'x': { 319 break; 320 } 321 case 'f': { 322 float f; 323 memcpy(&f, data+pos, size); 324 correctbytes((char *)&f, sizeof(f), h.endian); 325 lua_pushnumber(L, f); n++; 326 break; 327 } 328 case 'd': { 329 double d; 330 memcpy(&d, data+pos, size); 331 correctbytes((char *)&d, sizeof(d), h.endian); 332 lua_pushnumber(L, d); n++; 333 break; 334 } 335 case 'c': { 336 if (size == 0) { 337 if (n == 0 || !lua_isnumber(L, -1)) 338 luaL_error(L, "format 'c0' needs a previous size"); 339 size = lua_tonumber(L, -1); 340 lua_pop(L, 1); n--; 341 luaL_argcheck(L, size <= ld && pos <= ld - size, 342 2, "data string too short"); 343 } 344 lua_pushlstring(L, data+pos, size); n++; 345 break; 346 } 347 case 's': { 348 const char *e = (const char *)memchr(data+pos, '\0', ld - pos); 349 if (e == NULL) 350 luaL_error(L, "unfinished string in data"); 351 size = (e - (data+pos)) + 1; 352 lua_pushlstring(L, data+pos, size - 1); n++; 353 break; 354 } 355 default: controloptions(L, opt, &fmt, &h); 356 } 357 pos += size; 358 } 359 lua_pushinteger(L, pos + 1); /* next position */ 360 return n + 1; 361 } 362 363 364 static int b_size (lua_State *L) { 365 Header h; 366 const char *fmt = luaL_checkstring(L, 1); 367 size_t pos = 0; 368 defaultoptions(&h); 369 while (*fmt) { 370 int opt = *fmt++; 371 size_t size = optsize(L, opt, &fmt); 372 pos += gettoalign(pos, &h, opt, size); 373 if (opt == 's') 374 luaL_argerror(L, 1, "option 's' has no fixed size"); 375 else if (opt == 'c' && size == 0) 376 luaL_argerror(L, 1, "option 'c0' has no fixed size"); 377 if (!isalnum(opt)) 378 controloptions(L, opt, &fmt, &h); 379 pos += size; 380 } 381 lua_pushinteger(L, pos); 382 return 1; 383 } 384 385 /* }====================================================== */ 386 387 388 389 static const struct luaL_Reg thislib[] = { 390 {"pack", b_pack}, 391 {"unpack", b_unpack}, 392 {"size", b_size}, 393 {NULL, NULL} 394 }; 395 396 397 LUALIB_API int luaopen_struct (lua_State *L); 398 399 LUALIB_API int luaopen_struct (lua_State *L) { 400 luaL_register(L, "struct", thislib); 401 return 1; 402 } 403 404 405 /****************************************************************************** 406 * Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved. 407 * 408 * Permission is hereby granted, free of charge, to any person obtaining 409 * a copy of this software and associated documentation files (the 410 * "Software"), to deal in the Software without restriction, including 411 * without limitation the rights to use, copy, modify, merge, publish, 412 * distribute, sublicense, and/or sell copies of the Software, and to 413 * permit persons to whom the Software is furnished to do so, subject to 414 * the following conditions: 415 * 416 * The above copyright notice and this permission notice shall be 417 * included in all copies or substantial portions of the Software. 418 * 419 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 420 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 421 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 422 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 423 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 424 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 425 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 426 ******************************************************************************/ 427 428