makefile0000444000175000017500000000122411775063570012500 0ustar robertoroberto# point it to where the compiler can find the Lua header files (lua.h, etc.) # LUADIR = ../lua LUADIR = /usr/include/lua5.1/ # define your own "large" integer type; not defining a proper type # will default to 'long', which may cause problems with 'size_t' INTTYPE = -DSTRUCT_INT="long long" CWARNS = -Wall -W -pedantic \ -Waggregate-return \ -Wcast-align \ -Wmissing-prototypes \ -Wnested-externs \ -Wpointer-arith \ -Wshadow \ -Wwrite-strings CFLAGS = -D_POSIX_SOURCE $(CWARNS) $(INTTYPE) -O2 -I$(LUADIR) CC = gcc struct.so: struct.c makefile $(CC) $(CFLAGS) -shared -fpic -o struct.so struct.c struct.c0000444000175000017500000002563713277007577012512 0ustar robertoroberto/* ** {====================================================== ** Library for packing/unpacking structures. ** $Id: struct.c,v 1.8 2018/05/16 11:00:23 roberto Exp $ ** See Copyright Notice at the end of this file ** ======================================================= */ /* ** Valid formats: ** > - big endian ** < - little endian ** ![num] - alignment ** x - pading ** b/B - signed/unsigned byte ** h/H - signed/unsigned short ** l/L - signed/unsigned long ** T - size_t ** i/In - signed/unsigned integer with size 'n' (default is size of int) ** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means the whole string; when unpacking, n==0 means use the previous read number as the string length ** s - zero-terminated string ** f - float ** d - double ** ' ' - ignored */ #include #include #include #include #include "lua.h" #include "lauxlib.h" #if (LUA_VERSION_NUM >= 502) #define luaL_register(L,n,f) luaL_newlib(L,f) #endif /* basic integer type */ #if !defined(STRUCT_INT) #define STRUCT_INT long #endif typedef STRUCT_INT Inttype; /* corresponding unsigned version */ typedef unsigned STRUCT_INT Uinttype; /* maximum size (in bytes) for integral types */ #define MAXINTSIZE 32 /* is 'x' a power of 2? */ #define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0) /* dummy structure to get alignment requirements */ struct cD { char c; double d; }; #define PADDING (sizeof(struct cD) - sizeof(double)) #define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int)) /* endian options */ #define BIG 0 #define LITTLE 1 static union { int dummy; char endian; } const native = {1}; typedef struct Header { int endian; int align; } Header; static int getnum (const char **fmt, int df) { if (!isdigit(**fmt)) /* no number? */ return df; /* return default value */ else { int a = 0; do { a = a*10 + *((*fmt)++) - '0'; } while (isdigit(**fmt)); return a; } } #define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1) static size_t optsize (lua_State *L, char opt, const char **fmt) { switch (opt) { case 'B': case 'b': return sizeof(char); case 'H': case 'h': return sizeof(short); case 'L': case 'l': return sizeof(long); case 'T': return sizeof(size_t); case 'f': return sizeof(float); case 'd': return sizeof(double); case 'x': return 1; case 'c': return getnum(fmt, 1); case 'i': case 'I': { int sz = getnum(fmt, sizeof(int)); if (sz > MAXINTSIZE) luaL_error(L, "integral size %d is larger than limit of %d", sz, MAXINTSIZE); return sz; } default: return 0; /* other cases do not need alignment */ } } /* ** return number of bytes needed to align an element of size 'size' ** at current position 'len' */ static int gettoalign (size_t len, Header *h, int opt, size_t size) { if (size == 0 || opt == 'c') return 0; if (size > (size_t)h->align) size = h->align; /* respect max. alignment */ return (size - (len & (size - 1))) & (size - 1); } /* ** options to control endianess and alignment */ static void controloptions (lua_State *L, int opt, const char **fmt, Header *h) { switch (opt) { case ' ': return; /* ignore white spaces */ case '>': h->endian = BIG; return; case '<': h->endian = LITTLE; return; case '!': { int a = getnum(fmt, MAXALIGN); if (!isp2(a)) luaL_error(L, "alignment %d is not a power of 2", a); h->align = a; return; } default: { const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt); luaL_argerror(L, 1, msg); } } } static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian, int size) { lua_Number n = luaL_checknumber(L, arg); Uinttype value; char buff[MAXINTSIZE]; if (n < 0) value = (Uinttype)(Inttype)n; else value = (Uinttype)n; if (endian == LITTLE) { int i; for (i = 0; i < size; i++) { buff[i] = (value & 0xff); value >>= 8; } } else { int i; for (i = size - 1; i >= 0; i--) { buff[i] = (value & 0xff); value >>= 8; } } luaL_addlstring(b, buff, size); } static void correctbytes (char *b, int size, int endian) { if (endian != native.endian) { int i = 0; while (i < --size) { char temp = b[i]; b[i++] = b[size]; b[size] = temp; } } } static int b_pack (lua_State *L) { luaL_Buffer b; const char *fmt = luaL_checkstring(L, 1); Header h; int arg = 2; size_t totalsize = 0; defaultoptions(&h); lua_pushnil(L); /* mark to separate arguments from string buffer */ luaL_buffinit(L, &b); while (*fmt != '\0') { int opt = *fmt++; size_t size = optsize(L, opt, &fmt); int toalign = gettoalign(totalsize, &h, opt, size); totalsize += toalign; while (toalign-- > 0) luaL_addchar(&b, '\0'); switch (opt) { case 'b': case 'B': case 'h': case 'H': case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ putinteger(L, &b, arg++, h.endian, size); break; } case 'x': { luaL_addchar(&b, '\0'); break; } case 'f': { float f = (float)luaL_checknumber(L, arg++); correctbytes((char *)&f, size, h.endian); luaL_addlstring(&b, (char *)&f, size); break; } case 'd': { double d = luaL_checknumber(L, arg++); correctbytes((char *)&d, size, h.endian); luaL_addlstring(&b, (char *)&d, size); break; } case 'c': case 's': { size_t l; const char *s = luaL_checklstring(L, arg++, &l); if (size == 0) size = l; luaL_argcheck(L, l >= (size_t)size, arg, "string too short"); luaL_addlstring(&b, s, size); if (opt == 's') { luaL_addchar(&b, '\0'); /* add zero at the end */ size++; } break; } default: controloptions(L, opt, &fmt, &h); } totalsize += size; } luaL_pushresult(&b); return 1; } static lua_Number getinteger (const char *buff, int endian, int issigned, int size) { Uinttype l = 0; int i; if (endian == BIG) { for (i = 0; i < size; i++) { l <<= 8; l |= (Uinttype)(unsigned char)buff[i]; } } else { for (i = size - 1; i >= 0; i--) { l <<= 8; l |= (Uinttype)(unsigned char)buff[i]; } } if (!issigned) return (lua_Number)l; else { /* signed format */ Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1); if (l & mask) /* negative value? */ l |= mask; /* signal extension */ return (lua_Number)(Inttype)l; } } static int b_unpack (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); size_t ld; const char *data = luaL_checklstring(L, 2, &ld); size_t pos = (size_t)luaL_optinteger(L, 3, 1) - 1; int n = 0; /* number of results */ luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); defaultoptions(&h); while (*fmt) { int opt = *fmt++; size_t size = optsize(L, opt, &fmt); pos += gettoalign(pos, &h, opt, size); luaL_argcheck(L, size <= ld - pos, 2, "data string too short"); /* stack space for item + next position */ luaL_checkstack(L, 2, "too many results"); switch (opt) { case 'b': case 'B': case 'h': case 'H': case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ int issigned = islower(opt); lua_Number res = getinteger(data+pos, h.endian, issigned, size); lua_pushnumber(L, res); n++; break; } case 'x': { break; } case 'f': { float f; memcpy(&f, data+pos, size); correctbytes((char *)&f, sizeof(f), h.endian); lua_pushnumber(L, f); n++; break; } case 'd': { double d; memcpy(&d, data+pos, size); correctbytes((char *)&d, sizeof(d), h.endian); lua_pushnumber(L, d); n++; break; } case 'c': { if (size == 0) { if (n == 0 || !lua_isnumber(L, -1)) luaL_error(L, "format 'c0' needs a previous size"); size = lua_tonumber(L, -1); lua_pop(L, 1); n--; luaL_argcheck(L, size <= ld - pos, 2, "data string too short"); } lua_pushlstring(L, data+pos, size); n++; break; } case 's': { const char *e = (const char *)memchr(data+pos, '\0', ld - pos); if (e == NULL) luaL_error(L, "unfinished string in data"); size = (e - (data+pos)) + 1; lua_pushlstring(L, data+pos, size - 1); n++; break; } default: controloptions(L, opt, &fmt, &h); } pos += size; } lua_pushinteger(L, pos + 1); /* next position */ return n + 1; } static int b_size (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); size_t pos = 0; defaultoptions(&h); while (*fmt) { int opt = *fmt++; size_t size = optsize(L, opt, &fmt); pos += gettoalign(pos, &h, opt, size); if (opt == 's') luaL_argerror(L, 1, "option 's' has no fixed size"); else if (opt == 'c' && size == 0) luaL_argerror(L, 1, "option 'c0' has no fixed size"); if (!isalnum(opt)) controloptions(L, opt, &fmt, &h); pos += size; } lua_pushinteger(L, pos); return 1; } /* }====================================================== */ static const struct luaL_Reg thislib[] = { {"pack", b_pack}, {"unpack", b_unpack}, {"size", b_size}, {NULL, NULL} }; LUALIB_API int luaopen_struct (lua_State *L); LUALIB_API int luaopen_struct (lua_State *L) { luaL_register(L, "struct", thislib); return 1; } /****************************************************************************** * Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ struct.html0000444000175000017500000001252713275411506013213 0ustar robertoroberto

Library for Converting Data to and from C Structs for Lua 5.1/5.2

(download)

This library offers basic facilities to convert Lua values to and from C structs. Its main functions are struct.pack, which packs multiple Lua values into a struct-like string; and struct.unpack, which unpacks multiple Lua values from a given struct-like string.

The fist argument to both functions is a format string, which describes the layout of the structure. The format string is a sequence of conversion elements, which respect the current endianess and the current alignment requirements. Initially, the current endianess is the machine's native endianness and the current alignment requirement is 1 (meaning no alignment at all). You can change these settings with appropriate directives in the format string.

The elements in the format string are as follows:

  • " " (empty space) ignored.
  • "!n" flag to set the current alignment requirement to n (necessarily a power of 2); an absent n means the machine's native alignment.
  • ">" flag to set mode to big endian.
  • "<" flag to set mode to little endian.
  • "x" a padding zero byte with no corresponding Lua value.
  • "b" a signed char.
  • "B" an unsigned char.
  • "h" a signed short (native size).
  • "H" an unsigned short (native size).
  • "l" a signed long (native size).
  • "L" an unsigned long (native size).
  • "T" a size_t (native size).
  • "in" a signed integer with n bytes. An absent n means the native size of an int.
  • "In" like "in" but unsigned.
  • "f" a float (native size).
  • "d" a double (native size).
  • "s" a zero-terminated string.
  • "cn" a sequence of exactly n chars corresponding to a single Lua string. An absent n means 1. When packing, the given string must have at least n characters (extra characters are discarded).
  • "c0" this is like "cn", except that the n is given by other means: When packing, n is the length of the given string; when unpacking, n is the value of the previous unpacked value (which must be a number). In that case, this previous value is not returned.

Lua API

Examples

  • The code print(struct.size("i")) prints the size of a machine's native int.

  • To pack and unpack the structure

    struct Str {
      char b;
      int i[4];
    };
    
    you can use the string "<!4biiii".

  • If you need to code a structure with a large array, you may use string.rep to automatically generate part of the string format. For instance, for the structure

    struct Str {
      double x;
      int i[400];
    };
    
    you can build the format string with the code "d"..string.rep("i", 400).

  • To pack a string with its length coded in its first byte, use the following code:

    x = struct.pack("Bc0", string.len(s), s)
    
    To unpack that string, do as follows:
    s = struct.unpack("Bc0", x)
    
    Note that the length (read by the element "B") is not returned.

  • Suppose we have to decode a string s with an unknown number of doubles; the end is marked by a zero value. We can use the following code:

    local a = {}
    local i = 1         -- index where to read
    while true do
      local d
      d, i = struct.unpack("d", s, i)
      if d == 0 then break end
      a[#a + 1] = d
    end
    

  • To pack a string in a fixed-width field of 10 characters padded with blanks, do as follows:

    x = struct.pack("c10", s .. string.rep(" ", 10))
    

License

This package is distributed under the MIT license. See copyright notice at the end of file struct.c.

teststruct.lua0000544000175000017500000002232413277010041013714 0ustar robertoroberto#!/usr/bin/env lua5.1 -- $Id: teststruct.lua,v 1.8 2018/05/16 11:03:52 roberto Exp $ -- load library local lib = require"struct" -- Lua 5.1 x Lua 5.2 local unpack = unpack or table.unpack -- -- auxiliar function to print an hexadecimal `dump' of a given string -- (not used by the test) -- local function bp (s) s = string.gsub(s, "(.)", function(c) return string.format("\\%02x", string.byte(c)) end) print(s) end local a,b,c,d,e,f,x -- assume sizeof(int) == 4 assert(#lib.pack("I4", 67324752) == 4 and lib.size("I4", 4) == 4) assert(lib.size('bbb') == 3) assert(lib.pack('b', 10) == string.char(10)) assert(lib.pack('bbb', 10, 20, 30) == string.char(10, 20, 30)) assert(lib.size('h') == 2) -- assume sizeof(short) == 2 assert(lib.pack('h', 10) == string.char(0, 10)) assert(lib.pack('l', 10) == string.char(0):rep(x) .. string.char(10)) assert(lib.pack('h', string.char(0, 10)) == 10) assert(lib.unpack('i4', string.char(0, 1, 0, 10)) == 10 + 2^(2*8)) assert(lib.unpack('LIHT")) assert(lib.size("!4bi") > lib.size("!1bi")) x = lib.size("T") - 1 assert(lib.pack('T', 10) == string.char(0):rep(x) .. string.char(10)) assert(lib.pack('', '<'} do for _, l in pairs(lims) do local fmt = a .. l[1] assert(lib.unpack(fmt, lib.pack(fmt, l[2])) == l[2]) end end -- tests for fixed-sized ints for _, i in pairs{1,2,4} do x = lib.pack('I3", string.char(255, 255, 256 - 21)) == 2^(3*8) - 21) -- tests for long long if lib.unpack("i8", string.rep("\255", 8)) ~= -1 then print("no support for 'long long'") else local lim = 800 assert(lib.pack(">i8", 2^52) == "\0\16\0\0\0\0\0\0") local t = {}; for i = 1, lim do t[i] = 2^52 end assert(lib.pack(">" .. string.rep("i8", lim), unpack(t, 1, lim)) == string.rep("\0\16\0\0\0\0\0\0", lim)) assert(lib.pack("i8", -2^52 - 1) == "\255\239\255\255\255\255\255\255") assert(lib.pack("i8", "\255\239\255\255\255\255\255\255") == -2^52 - 1) assert(lib.unpack("i8", "\000\015\255\255\255\255\254\254") == 2^52 - 258) local fmt = ">" .. string.rep("i16", lim) local t1 = {lib.unpack(fmt, lib.pack(fmt, unpack(t)))} assert(t1[#t1] == 16*lim + 1 and #t == #t1 - 1) for i = 1, lim do assert(t[i] == t1[i]) end print'+' end -- strings assert(lib.pack("c", "alo alo") == "a") assert(lib.pack("c4", "alo alo") == "alo ") assert(lib.pack("c5", "alo alo") == "alo a") assert(lib.pack("!4b>c7", 1, "alo alo") == "\1alo alo") assert(lib.pack("!2iiiii", 1, 2, 3, 4, 5) local i = 1 local k = 1 while i < #x do local v, j = lib.unpack("!>i", x, i) assert(j == i + 4 and v == k) i = j; k = k + 1 end -- alignments are relative to 'absolute' positions x = lib.pack("!8 xd", 12) assert(lib.unpack("!8d", x, 3) == 12) a,b,c,d = lib.unpack("lBxxH", lib.pack(">lBxxH", -20, 10, 250)) assert(a == -20 and b == 10 and c == 250 and d == lib.size(">lBxxH") + 1) a,b,c,d,e = lib.unpack(">fdfH", '000'..lib.pack(">fdfH", 3.5, -24e-5, 200.5, 30000), 4) assert(a == 3.5 and b == -24e-5 and c == 200.5 and d == 30000 and e == 22) a,b,c,d,e = lib.unpack(" I2 f i4 I2 ", 10, 20, -30, 40001) assert(string.len(x) == 2 + lib.size("f") + 4 + 2) assert(lib.unpack(">f", x, 3) == 20) a,b,c,d = lib.unpack(">i2fi4I2", x) assert(a == 10 and b == 20 and c == -30 and d == 40001) local s = "hello hello" x = lib.pack(" b c0 ", string.len(s), s) assert(lib.unpack("bc0", x) == s) x = lib.pack("Lc0", string.len(s), s) assert(lib.unpack(" L c0 ", x) == s) x = lib.pack("cc3b", s, s, 0) assert(x == "hhel\0") assert(lib.unpack("xxxxb", x) == 0) assert(lib.pack("sh", "aloi", 3) == "aloi\0\0\3") assert(lib.pack(">!sh", "aloi", 3) == "aloi\0\0\0\3") x = "aloi\0\0\0\0\3\2\0\0" a, b, c = lib.unpack("!') == '') local p, x = lib.unpack('>!', '') assert(p == 1 and x == nil) -- return only the new position end -- test for weird conditions assert(lib.pack(">>>h >><<i8", 1000) == string.char(0, 0, 0, 0, 0, 0, 3, 232)) assert(lib.pack("i32", 10000000) == string.rep('\0', 29) .. string.char(0x98, 0x96, 0x80)) print'OK'