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