lua支持init64

5.7k 词

lua5.3提供了很多很好的特性例如string.pack unpack这样的好东西,同时还支持ini64.在lua之前的版本中number只有一种类型double.对于init64基本按照int来处理. 对于64位的解决方案有很多种,基本的思路都是使用8byte的string或者lightuserdata或者userdata修改元表来实现.

luajit使用userdata重载元表运算符实现

在 luajit 中,是定义了一个 userdata 并重载其运算符完成的。用 ffi.cast("int64_t",0) 来构造一个 64bit 的 0 .userdata的做法存在额外开销问题,当 64bit 的 cdata 做 table 的 key 的时候,相同值的 int64 并不是同一个 key .

lightuserdata 设置 metatable实现

用 lightuserdata 无损失的表示一个 int64 ,lightuserdata是一个轻量级的cdata,通过给 lightuserdata 设置 metatable ,我们可以重载它的数据运算。存在的问题:比较一个 int64 和普通的 lua number 是否相等时,lua 不能隐式的做转换。目前使用这个方案的实现的已经有了,github上有https://github.com/bytemode/lua-int64.git这个库是云风大神实现的.

这个库只提供了一个显式的 api ,即构造一个 int64 数字。可以从 lua number 构造,也支持从一个 8 字节宽的小头的字符串来构造。实际在内存储存的是一个 lightuserdata 即一个 64bit 指针(所以这个库不适用于 32 位平台)。你也可以通过 C 接口 lua_pushlightuserdata 来把一个 64bit 整数压入堆栈。把 int64 转换为普通的 lua number 借用了 # 操作符。

#include <lua.h>
#include <lauxlib.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>

static int64_t
_int64(lua_State *L, int index) {
int type = lua_type(L,index);
int64_t n = 0;
switch(type) {
case LUA_TNUMBER: {
lua_Number d = lua_tonumber(L,index);
n = (int64_t)d;
break;
}
case LUA_TSTRING: {
size_t len = 0;
const uint8_t * str = (const uint8_t )lua_tolstring(L, index, &len);
if (len>8) {
return luaL_error(L, "The string (length = %d) is not an int64 string", len);
}
int i = 0;
uint64_t n64 = 0;
for (i=0;i<(int)len;i++) {
n64 |= (uint64_t)str[i] << (i
8);
}
n = (int64_t)n64;
break;
}
case LUA_TLIGHTUSERDATA: {
void * p = lua_touserdata(L,index);
n = (intptr_t)p;
break;
}
default:
return luaL_error(L, "argument %d error type %s", index, lua_typename(L,type));
}
return n;
}

static inline void
_pushint64(lua_State *L, int64_t n) {
void * p = (void *)(intptr_t)n;
lua_pushlightuserdata(L,p);
}

static int
int64_add(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
_pushint64(L, a+b);

return 1;

}

static int
int64_sub(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
_pushint64(L, a-b);

return 1;

}

static int
int64_mul(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
_pushint64(L, a * b);

return 1;

}

static int
int64_div(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
if (b == 0) {
return luaL_error(L, "div by zero");
}
_pushint64(L, a / b);

return 1;

}

static int
int64_mod(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
if (b == 0) {
return luaL_error(L, "mod by zero");
}
_pushint64(L, a % b);

return 1;

}

static int64_t
_pow64(int64_t a, int64_t b) {
if (b == 1) {
return a;
}
int64_t a2 = a * a;
if (b % 2 == 1) {
return _pow64(a2, b/2) * a;
} else {
return _pow64(a2, b/2);
}
}

static int
int64_pow(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
int64_t p;
if (b > 0) {
p = _pow64(a,b);
} else if (b == 0) {
p = 1;
} else {
return luaL_error(L, "pow by nagtive number %d",(int)b);
}
_pushint64(L, p);

return 1;

}

static int
int64_unm(lua_State *L) {
int64_t a = _int64(L,1);
_pushint64(L, -a);
return 1;
}

static int
int64_new(lua_State *L) {
int top = lua_gettop(L);
int64_t n;
switch(top) {
case 0 :
lua_pushlightuserdata(L,NULL);
break;
case 1 :
n = _int64(L,1);
_pushint64(L,n);
break;
default: {
int base = luaL_checkinteger(L,2);
if (base < 2) {
luaL_error(L, "base must be >= 2");
}
const char * str = luaL_checkstring(L, 1);
n = strtoll(str, NULL, base);
_pushint64(L,n);
break;
}
}
return 1;
}

static int
int64_eq(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
printf("%s %sn",lua_typename(L,1),lua_typename(L,2));
printf("%ld %ldn",a,b);
lua_pushboolean(L,a == b);
return 1;
}

static int
int64_lt(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
lua_pushboolean(L,a < b);
return 1;
}

static int
int64_le(lua_State *L) {
int64_t a = _int64(L,1);
int64_t b = _int64(L,2);
lua_pushboolean(L,a <= b);
return 1;
}

static int
int64_len(lua_State *L) {
int64_t a = _int64(L,1);
lua_pushnumber(L,(lua_Number)a);
return 1;
}

static int
tostring(lua_State L) {
static char hex[16] = "0123456789ABCDEF";
uintptr_t n = (uintptr_t)lua_touserdata(L,1);
if (lua_gettop(L) == 1) {
luaL_Buffer b;
luaL_buffinitsize(L , &b , 28);
luaL_addstring(&b, "int64: 0x");
int i;
bool strip = true;
for (i=15;i>=0;i–) {
int c = (n >> (i
4)) & 0xf;
if (strip && c ==0) {
continue;
}
strip = false;
luaL_addchar(&b, hex[c]);
}
if (strip) {
luaL_addchar(&b , '0');
}
luaL_pushresult(&b);
} else {
int base = luaL_checkinteger(L,2);
int shift , mask;
switch(base) {
case 0: {
unsigned char buffer[8];
int i;
for (i=0;i<8;i++) {
buffer[i] = (n >> (i*8)) & 0xff;
}
lua_pushlstring(L,(const char *)buffer, 8);
return 1;
}
case 10: {
int64_t dec = (int64_t)n;
luaL_Buffer b;
luaL_buffinitsize(L , &b , 28);
if (dec<0) {
luaL_addchar(&b, '-');
dec = -dec;
}
int buffer[32];
int i;
for (i=0;i<32;i++) {
buffer[i] = dec%10;
dec /= 10;
if (dec == 0)
break;
}
while (i>=0) {
luaL_addchar(&b, hex[buffer[i]]);
–i;
}
luaL_pushresult(&b);
return 1;
}
case 2:
shift = 1;
mask = 1;
break;
case 8:
shift = 3;
mask = 7;
break;
case 16:
shift = 4;
mask = 0xf;
break;
default:
luaL_error(L, "Unsupport base %d",base);
break;
}
int i;
char buffer[64];
for (i=0;i<64;i+=shift) {
buffer[i/shift] = hex[(n>>(64-shift-i)) & mask];
}
lua_pushlstring(L, buffer, 64 / shift);
}
return 1;
}

static void
make_mt(lua_State *L) {
luaL_Reg lib[] = {
{ "__add", int64_add },
{ "__sub", int64_sub },
{ "__mul", int64_mul },
{ "__div", int64_div },
{ "__mod", int64_mod },
{ "__unm", int64_unm },
{ "__pow", int64_pow },
{ "__eq", int64_eq },
{ "__lt", int64_lt },
{ "__le", int64_le },
{ "__len", int64_len },
{ "__tostring", tostring },
{ NULL, NULL },
};
luaL_newlib(L,lib);
}

int
luaopen_int64(lua_State *L) {
if (sizeof(intptr_t)!=sizeof(int64_t)) {
return luaL_error(L, "Only support 64bit architecture");
}
lua_pushlightuserdata(L,NULL);
make_mt(L);
lua_setmetatable(L,-2);
lua_pop(L,1);

lua_newtable(L);
lua_pushcfunction(L, int64_new);
lua_setfield(L, -2, &#34;new&#34;);
lua_pushcfunction(L, tostring);
lua_setfield(L, -2, &#34;tostring&#34;);

return 1;

}