浏览代码

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

Roberto Ierusalimschy 11 年之前
父节点
当前提交
cd848cab6b
共有 1 个文件被更改,包括 97 次插入14 次删除
  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
 ** Standard library for string operations and pattern-matching
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -948,6 +948,19 @@ static int str_format (lua_State *L) {
 #define MAXINTSIZE	8
 #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 {
 static union {
   int dummy;
   int dummy;
   char little;  /* true iff machine is little endian */
   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) {
 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,
   luaL_argcheck(L, 1 <= size && size <= MAXINTSIZE, arg,
                    "integer size out of valid range");
                    "integer size out of valid range");
   return size;
   return size;
@@ -975,21 +988,21 @@ static int packint (char *buff, lua_Integer n, int littleendian, int size) {
   int i;
   int i;
   if (littleendian) {
   if (littleendian) {
     for (i = 0; i < size - 1; i++) {
     for (i = 0; i < size - 1; i++) {
-      buff[i] = (n & 0xff);
-      n >>= 8;
+      buff[i] = (n & MC);
+      n >>= NB;
     }
     }
   }
   }
   else {
   else {
     for (i = size - 1; i > 0; i--) {
     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,
   /* 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
      or if there are only oneś left and packed number is negative (signal
      bit, the higher bit in last byte, is one) */
      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 */
 /* 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 */
 /* 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,
 static int unpackint (const char *buff, lua_Integer *res,
                       int littleendian, int size) {
                       int littleendian, int size) {
   lua_Integer n = 0;
   lua_Integer n = 0;
   int i;
   int i;
   for (i = 0; i < size; 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
       /* check for overflow: it is OK to throw away leading zeros for a
          positive number; leading ones for a negative number; and one
          positive number; leading ones for a negative number; and one
          last leading zero to allow unsigned integers with a 1 in
          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 */
           ((n & HIGHERBYTE) == 0 && i == size - 1)))  /* last zero */
         return 0;  /* overflow */
         return 0;  /* overflow */
     }
     }
-    n <<= 8;
+    n <<= NB;
     n |= (lua_Integer)(unsigned char)buff[littleendian ? size - 1 - i : i];
     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? */
     if (n & mask)  /* negative value? */
       n |= mask;  /* signal extension */
       n |= mask;  /* signal extension */
   }
   }
@@ -1056,6 +1069,74 @@ static int unpackint_l (lua_State *L) {
   return 1;
   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},
   {"reverse", str_reverse},
   {"sub", str_sub},
   {"sub", str_sub},
   {"upper", str_upper},
   {"upper", str_upper},
+  {"packfloat", packfloat_l},
   {"packint", packint_l},
   {"packint", packint_l},
+  {"unpackfloat", unpackfloat_l},
   {"unpackint", unpackint_l},
   {"unpackint", unpackint_l},
   {NULL, NULL}
   {NULL, NULL}
 };
 };