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