Browse Source

first implementation for string.pack/unpackfloat + try not to assume
that chars have 8 bits

Roberto Ierusalimschy 11 years ago
parent
commit
cd848cab6b
1 changed files with 97 additions and 14 deletions
  1. 97 14
      lstrlib.c

+ 97 - 14
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}
 };