From cd848cab6bf59314d6b41625bbae918103f654db Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 8 Jan 2014 16:34:34 -0200 Subject: first implementation for string.pack/unpackfloat + try not to assume that chars have 8 bits --- lstrlib.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 14 deletions(-) (limited to 'lstrlib.c') diff --git a/lstrlib.c b/lstrlib.c index 8fd1a433..0be1bf12 100644 --- a/lstrlib.c +++ b/lstrlib.c @@ -1,5 +1,5 @@ /* -** $Id: lstrlib.c,v 1.182 2013/06/20 15:06:51 roberto Exp roberto $ +** $Id: lstrlib.c,v 1.183 2014/01/05 14:05:58 roberto Exp roberto $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ @@ -948,6 +948,19 @@ static int str_format (lua_State *L) { #define MAXINTSIZE 8 +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB ones) */ +#define MC (((lua_Integer)1 << NB) - 1) + +/* mask for one character without sign ((NB - 1) ones) */ +#define SM (((lua_Integer)1 << (NB - 1)) - 1) + + +#define SZINT ((int)sizeof(lua_Integer)) + + static union { int dummy; char little; /* true iff machine is little endian */ @@ -964,7 +977,7 @@ static int getendian (lua_State *L, int arg) { static int getintsize (lua_State *L, int arg) { - int size = luaL_optint(L, arg, sizeof(lua_Integer)); + int size = luaL_optint(L, arg, SZINT); luaL_argcheck(L, 1 <= size && size <= MAXINTSIZE, arg, "integer size out of valid range"); return size; @@ -975,21 +988,21 @@ static int packint (char *buff, lua_Integer n, int littleendian, int size) { int i; if (littleendian) { for (i = 0; i < size - 1; i++) { - buff[i] = (n & 0xff); - n >>= 8; + buff[i] = (n & MC); + n >>= NB; } } else { for (i = size - 1; i > 0; i--) { - buff[i] = (n & 0xff); - n >>= 8; + buff[i] = (n & MC); + n >>= NB; } } - buff[i] = (n & 0xff); /* last byte */ + buff[i] = (n & MC); /* last byte */ /* test for overflow: OK if there are only zeros left in higher bytes, or if there are only oneÅ› left and packed number is negative (signal bit, the higher bit in last byte, is one) */ - return ((n & ~(lua_Integer)0xff) == 0 || (n | 0x7f) == ~(lua_Integer)0); + return ((n & ~MC) == 0 || (n | SM) == ~(lua_Integer)0); } @@ -1007,17 +1020,17 @@ static int packint_l (lua_State *L) { /* mask to check higher-order byte in a Lua integer */ -#define HIGHERBYTE (~((lua_Unsigned)~0 >> 8)) +#define HIGHERBYTE (MC << (NB * (SZINT - 1))) /* mask to check higher-order byte + signal bit of next byte */ -#define HIGHERBYTE1 (~((lua_Unsigned)~0 >> 9)) +#define HIGHERBYTE1 (HIGHERBYTE | (HIGHERBYTE >> 1)) static int unpackint (const char *buff, lua_Integer *res, int littleendian, int size) { lua_Integer n = 0; int i; for (i = 0; i < size; i++) { - if (i >= (int)sizeof(lua_Integer)) { /* will throw away a byte? */ + if (i >= SZINT) { /* will throw away a byte? */ /* check for overflow: it is OK to throw away leading zeros for a positive number; leading ones for a negative number; and one last leading zero to allow unsigned integers with a 1 in @@ -1027,11 +1040,11 @@ static int unpackint (const char *buff, lua_Integer *res, ((n & HIGHERBYTE) == 0 && i == size - 1))) /* last zero */ return 0; /* overflow */ } - n <<= 8; + n <<= NB; n |= (lua_Integer)(unsigned char)buff[littleendian ? size - 1 - i : i]; } - if (size < (int)sizeof(lua_Integer)) { /* need sign extension? */ - lua_Integer mask = (~(lua_Integer)0) << (size*8 - 1); + if (size < SZINT) { /* need sign extension? */ + lua_Integer mask = (~(lua_Integer)0) << (size*NB - 1); if (n & mask) /* negative value? */ n |= mask; /* signal extension */ } @@ -1056,6 +1069,74 @@ static int unpackint_l (lua_State *L) { return 1; } + +static void correctendianess (lua_State *L, char *b, int size, int endianarg) { + int endian = getendian(L, endianarg); + if (endian != nativeendian.little) { /* not native endianess? */ + int i = 0; + while (i < --size) { + char temp = b[i]; + b[i++] = b[size]; + b[size] = temp; + } + } +} + + +#define DEFAULTFLOATSIZE \ + (sizeof(lua_Number) == sizeof(float) ? "f" : "d") + +static int getfloatsize (lua_State *L, int arg) { + const char *size = luaL_optstring(L, arg, DEFAULTFLOATSIZE); + luaL_argcheck(L, *size == 'd' || *size == 'f', arg, + "size must be 'f' or 'd'"); + return (*size == 'd' ? sizeof(double) : sizeof(float)); +} + + +static int packfloat_l (lua_State *L) { + float f; double d; + char *pn; /* pointer to number */ + lua_Number n = luaL_checknumber(L, 1); + int size = getfloatsize(L, 2); + if (size == sizeof(double)) { + d = (double)n; + pn = (char*)&d; + } + else { + f = (float)n; + pn = (char*)&f; + } + correctendianess(L, pn, size, 3); + lua_pushlstring(L, pn, size); + return 1; +} + + +static int unpackfloat_l (lua_State *L) { + lua_Number res; + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer pos = posrelat(luaL_optinteger(L, 2, 1), len); + int size = getfloatsize(L, 3); + luaL_argcheck(L, 1 <= pos && (size_t)pos + size - 1 <= len, 1, + "string too short"); + if (size == sizeof(double)) { + double d; + memcpy(&d, s + pos - 1, size); + correctendianess(L, (char*)&d, size, 4); + res = (lua_Number)d; + } + else { + float f; + memcpy(&f, s + pos - 1, size); + correctendianess(L, (char*)&f, size, 4); + res = (lua_Number)f; + } + lua_pushnumber(L, res); + return 1; +} + /* }====================================================== */ @@ -1074,7 +1155,9 @@ static const luaL_Reg strlib[] = { {"reverse", str_reverse}, {"sub", str_sub}, {"upper", str_upper}, + {"packfloat", packfloat_l}, {"packint", packint_l}, + {"unpackfloat", unpackfloat_l}, {"unpackint", unpackint_l}, {NULL, NULL} }; -- cgit v1.2.3-55-g6feb