Misc changes.
This commit is contained in:
parent
f4c8b30d51
commit
0030c0bc4f
180
limmutable.c
180
limmutable.c
@ -48,59 +48,6 @@ typedef struct {
|
|||||||
size_t len;
|
size_t len;
|
||||||
} immutable_info_t;
|
} immutable_info_t;
|
||||||
|
|
||||||
static int Lhash(lua_State *L)
|
|
||||||
{
|
|
||||||
lua_Integer hash = 0x9a937c4d; // Seed
|
|
||||||
lua_Integer n = luaL_checkinteger(L, 1);
|
|
||||||
for (lua_Integer i=2; i <= lua_gettop(L); i++) {
|
|
||||||
lua_Integer item_hash;
|
|
||||||
int type = i > n ? LUA_TNIL : lua_type(L, i);
|
|
||||||
switch (type) {
|
|
||||||
case LUA_TNIL: case LUA_TNONE:
|
|
||||||
// Arbitrarily chosen value
|
|
||||||
item_hash = 0x97167da9;
|
|
||||||
break;
|
|
||||||
case LUA_TNUMBER:
|
|
||||||
{
|
|
||||||
// Cast float bits to integer
|
|
||||||
lua_Number num = lua_tonumber(L, i);
|
|
||||||
item_hash = *((lua_Integer*)&num);
|
|
||||||
if (item_hash == 0) {
|
|
||||||
item_hash = 0x2887c992;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LUA_TBOOLEAN:
|
|
||||||
// Arbitrarily chosen values
|
|
||||||
item_hash = lua_toboolean(L, i)? 0x82684f71 : 0x88d66f2a;
|
|
||||||
break;
|
|
||||||
case LUA_TTABLE:
|
|
||||||
case LUA_TFUNCTION:
|
|
||||||
case LUA_TUSERDATA:
|
|
||||||
case LUA_TTHREAD:
|
|
||||||
case LUA_TLIGHTUSERDATA:
|
|
||||||
item_hash = (1000003 * type) ^ (lua_Integer)lua_topointer(L, i);
|
|
||||||
break;
|
|
||||||
case LUA_TSTRING:
|
|
||||||
{
|
|
||||||
// Algorithm taken from Lua 5.3's implementation
|
|
||||||
size_t len;
|
|
||||||
const char *str = lua_tolstring(L, i, &len);
|
|
||||||
item_hash = len ^ 0xd2e9e9ac; // Arbitrary seed
|
|
||||||
size_t step = (len >> LUAI_HASHLIMIT) + 1;
|
|
||||||
for (; len >= step; len -= step)
|
|
||||||
item_hash ^= ((item_hash<<5) + (item_hash>>2) + (unsigned char)(str[len - 1]));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
item_hash = 0;
|
|
||||||
}
|
|
||||||
hash = (1000003 * hash) ^ item_hash;
|
|
||||||
}
|
|
||||||
lua_pushinteger(L, hash);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Lcreate_instance(lua_State *L)
|
static int Lcreate_instance(lua_State *L)
|
||||||
{
|
{
|
||||||
size_t n_args = lua_gettop(L)-1;
|
size_t n_args = lua_gettop(L)-1;
|
||||||
@ -166,9 +113,7 @@ static int Lcreate_instance(lua_State *L)
|
|||||||
// Find bucket
|
// Find bucket
|
||||||
lua_rawgeti(L, -1, hash);
|
lua_rawgeti(L, -1, hash);
|
||||||
// Stack: [buckets, bucket]
|
// Stack: [buckets, bucket]
|
||||||
int created_bucket = 0;
|
|
||||||
if (lua_isnil(L, -1)) {
|
if (lua_isnil(L, -1)) {
|
||||||
created_bucket = 1;
|
|
||||||
// Make a new bucket
|
// Make a new bucket
|
||||||
// Stack: [buckets, nil]
|
// Stack: [buckets, nil]
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
@ -184,36 +129,33 @@ static int Lcreate_instance(lua_State *L)
|
|||||||
// Stack: [buckets, bucket, bucket]
|
// Stack: [buckets, bucket, bucket]
|
||||||
lua_rawseti(L, -3, hash);
|
lua_rawseti(L, -3, hash);
|
||||||
// Stack: [buckets, bucket]
|
// Stack: [buckets, bucket]
|
||||||
}
|
} else {
|
||||||
// Stack: [buckets, bucket]
|
// Stack: [buckets, bucket]
|
||||||
if (! created_bucket) {
|
|
||||||
// scan bucket
|
// scan bucket
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
while (lua_next(L, -2) != 0) { // for hash_collider_inst, hash_collider in pairs(bucket) do
|
while (lua_next(L, -2) != 0) { // for hash_collider_inst, hash_collider in pairs(bucket) do
|
||||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider]
|
// Stack: [buckets, bucket, hash_collider_inst, hash_collider]
|
||||||
int bucket_item_matches = 1;
|
|
||||||
// Shallow equality check:
|
// Shallow equality check:
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
while (lua_next(L, -2) != 0) { // for i, collider_value in pairs(hash_collider) do
|
while (lua_next(L, -2) != 0) { // for i, collider_value in pairs(hash_collider) do
|
||||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||||
if (!lua_rawequal(L, -1, 1+lua_tonumber(L, -2))) { // If the i'th entry doesn't match the i'th arg
|
if (!lua_rawequal(L, -1, 1+lua_tonumber(L, -2))) { // If the i'th entry doesn't match the i'th arg
|
||||||
bucket_item_matches = 0;
|
|
||||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||||
lua_pop(L, 3);
|
lua_pop(L, 3);
|
||||||
// Stack: [buckets, bucket, hash_collider_inst]
|
// Stack: [buckets, bucket, hash_collider_inst]
|
||||||
break; // go to next item in the bucket
|
goto next_bucket_item;
|
||||||
} else {
|
} else {
|
||||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value]
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i]
|
// Stack: [buckets, bucket, hash_collider_inst, hash_collider, i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bucket_item_matches) {
|
// bucket item matches
|
||||||
// Stack: [buckets, bucket, hash_collider_inst, hash_collider]
|
// Stack: [buckets, bucket, hash_collider_inst, hash_collider]
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
// Found matching pre-existing instance
|
return 1;
|
||||||
return 1;
|
|
||||||
}
|
next_bucket_item: ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +200,7 @@ static int Lcreate_instance(lua_State *L)
|
|||||||
// Stack [buckets, bucket, inst_userdata, inst_userdata, inst_table]
|
// Stack [buckets, bucket, inst_userdata, inst_userdata, inst_table]
|
||||||
lua_settable(L, -4); // buckets[inst_userdata] = inst_table
|
lua_settable(L, -4); // buckets[inst_userdata] = inst_table
|
||||||
// Stack [buckets, bucket, inst_userdata]
|
// Stack [buckets, bucket, inst_userdata]
|
||||||
lua_gc(L, LUA_GCSTEP, 1);
|
lua_gc(L, LUA_GCSTEP, 3);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +286,9 @@ static int Lindex(lua_State *L)
|
|||||||
lua_getfield(L, -1, "__indices");
|
lua_getfield(L, -1, "__indices");
|
||||||
// Stack: [mt, indices]
|
// Stack: [mt, indices]
|
||||||
if (lua_isnil(L, -1)) {
|
if (lua_isnil(L, -1)) {
|
||||||
|
if (! lua_isinteger(L, 2)) {
|
||||||
|
goto class_fallback;
|
||||||
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
// Stack: [mt]
|
// Stack: [mt]
|
||||||
lua_getfield(L, -1, "__instances");
|
lua_getfield(L, -1, "__instances");
|
||||||
@ -359,6 +304,9 @@ static int Lindex(lua_State *L)
|
|||||||
// Stack: [mt, buckets, bucket, inst_table]
|
// Stack: [mt, buckets, bucket, inst_table]
|
||||||
int i = luaL_checkinteger(L, 2);
|
int i = luaL_checkinteger(L, 2);
|
||||||
lua_rawgeti(L, -1, i);
|
lua_rawgeti(L, -1, i);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
goto class_fallback;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,32 +323,30 @@ static int Lindex(lua_State *L)
|
|||||||
lua_pushvalue(L, 2);
|
lua_pushvalue(L, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! lua_isnil(L, -1)) { // Found the field name
|
if (lua_isnil(L, -1)) { // Didn't find the field name
|
||||||
// Stack: [mt, indices, i]
|
goto class_fallback;
|
||||||
lua_getfield(L, -3, "__instances");
|
|
||||||
// Stack: [mt, indices, i, buckets]
|
|
||||||
lua_rawgeti(L, -1, info->hash);
|
|
||||||
// Stack: [mt, indices, i, buckets, bucket]
|
|
||||||
if (lua_isnil(L, -1)) {
|
|
||||||
luaL_error(L, "Failed to find hash bucket for hash: %p", (void*)info->hash);
|
|
||||||
}
|
|
||||||
lua_pushvalue(L, 1);
|
|
||||||
// Stack: [mt, indices, i, buckets, bucket, inst_udata]
|
|
||||||
lua_rawget(L, -2);
|
|
||||||
// Stack: [mt, indices, i, buckets, bucket, inst_table]
|
|
||||||
int i = luaL_checkinteger(L, -4);
|
|
||||||
lua_rawgeti(L, -1, i);
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
// Fall back to class:
|
|
||||||
// Stack: [mt, indices, nil]
|
|
||||||
lua_pop(L, 2);
|
|
||||||
// Stack: [mt]
|
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
// Stack: [mt, key]
|
|
||||||
lua_gettable(L, -2);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
// Stack: [mt, indices, i]
|
||||||
|
lua_getfield(L, -3, "__instances");
|
||||||
|
// Stack: [mt, indices, i, buckets]
|
||||||
|
lua_rawgeti(L, -1, info->hash);
|
||||||
|
// Stack: [mt, indices, i, buckets, bucket]
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
luaL_error(L, "Failed to find hash bucket for hash: %p", (void*)info->hash);
|
||||||
|
}
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
// Stack: [mt, indices, i, buckets, bucket, inst_udata]
|
||||||
|
lua_rawget(L, -2);
|
||||||
|
// Stack: [mt, indices, i, buckets, bucket, inst_table]
|
||||||
|
int i = luaL_checkinteger(L, -4);
|
||||||
|
lua_rawgeti(L, -1, i);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Fall back to class:
|
||||||
|
class_fallback:
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Ltostring(lua_State *L)
|
static int Ltostring(lua_State *L)
|
||||||
@ -446,15 +392,12 @@ static int Ltostring(lua_State *L)
|
|||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
// Stack: [mt, buckets, bucket, inst_table, tostring]
|
// Stack: [mt, buckets, bucket, inst_table, tostring]
|
||||||
immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1);
|
immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1);
|
||||||
int n = info->len;
|
int n = info->len, i = 1;
|
||||||
int needs_comma = 0;
|
goto first_list_item;
|
||||||
for (int i = 1; i <= n; i++) {
|
while (++i <= n) {
|
||||||
// Stack: [mt, buckets, bucket, inst_table, tostring, ???]
|
// Stack: [mt, buckets, bucket, inst_table, tostring, ???]
|
||||||
if (needs_comma) {
|
luaL_addstring(&b, ", ");
|
||||||
luaL_addstring(&b, ", ");
|
first_list_item:
|
||||||
} else {
|
|
||||||
needs_comma = 1;
|
|
||||||
}
|
|
||||||
lua_pushvalue(L, tostring_index);
|
lua_pushvalue(L, tostring_index);
|
||||||
// Stack: [mt, buckets, bucket, inst_table, tostring, ???, tostring]
|
// Stack: [mt, buckets, bucket, inst_table, tostring, ???, tostring]
|
||||||
lua_rawgeti(L, inst_table_index, i);
|
lua_rawgeti(L, inst_table_index, i);
|
||||||
@ -465,16 +408,13 @@ static int Ltostring(lua_State *L)
|
|||||||
// Stack: [mt, buckets, bucket, inst_table, tostring, ???]
|
// Stack: [mt, buckets, bucket, inst_table, tostring, ???]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int num_fields = lua_objlen(L, -1);
|
|
||||||
int fields_index = lua_gettop(L);
|
int fields_index = lua_gettop(L);
|
||||||
int needs_comma = 0;
|
int i = 1, num_fields = lua_objlen(L, -1);
|
||||||
for (int i = 1; i <= num_fields; i++) {
|
goto first_table_item;
|
||||||
|
while (++i <= num_fields) {
|
||||||
// Stack: [mt, buckets, bucket, inst_table, tostring, fields, ???]
|
// Stack: [mt, buckets, bucket, inst_table, tostring, fields, ???]
|
||||||
if (needs_comma) {
|
luaL_addstring(&b, ", ");
|
||||||
luaL_addstring(&b, ", ");
|
first_table_item:
|
||||||
} else {
|
|
||||||
needs_comma = 1;
|
|
||||||
}
|
|
||||||
lua_pushvalue(L, tostring_index);
|
lua_pushvalue(L, tostring_index);
|
||||||
// Stack: [mt, buckets, bucket, inst_table, tostring, fields, ???, tostring]
|
// Stack: [mt, buckets, bucket, inst_table, tostring, fields, ???, tostring]
|
||||||
lua_rawgeti(L, fields_index, i);
|
lua_rawgeti(L, fields_index, i);
|
||||||
@ -618,6 +558,25 @@ static int Lpairs(lua_State *L)
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int Ltable(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_getmetatable(L, 1);
|
||||||
|
// Stack: [mt]
|
||||||
|
lua_getfield(L, -1, "__instances");
|
||||||
|
// Stack: [mt, buckets]
|
||||||
|
immutable_info_t *info = (immutable_info_t *)lua_touserdata(L, 1);
|
||||||
|
lua_rawgeti(L, -1, info->hash);
|
||||||
|
// Stack: [mt, buckets, bucket]
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
luaL_error(L, "Failed to find hash bucket for hash: %p", (void*)info->hash);
|
||||||
|
}
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
// Stack: [mt, buckets, bucket, inst_udata]
|
||||||
|
lua_rawget(L, -2);
|
||||||
|
// Stack: [mt, buckets, bucket, inst_table]
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg Rinstance_metamethods[] =
|
static const luaL_Reg Rinstance_metamethods[] =
|
||||||
{
|
{
|
||||||
{ "__len", Llen},
|
{ "__len", Llen},
|
||||||
@ -627,6 +586,7 @@ static const luaL_Reg Rinstance_metamethods[] =
|
|||||||
{ "__pairs", Lpairs},
|
{ "__pairs", Lpairs},
|
||||||
{ "from_table", Lfrom_table},
|
{ "from_table", Lfrom_table},
|
||||||
{ "is_instance", Lis_instance},
|
{ "is_instance", Lis_instance},
|
||||||
|
{ "table", Ltable},
|
||||||
{ NULL, NULL}
|
{ NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -753,7 +713,5 @@ LUALIB_API int luaopen_immutable(lua_State *L)
|
|||||||
lua_settable(L, LUA_REGISTRYINDEX);
|
lua_settable(L, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
lua_pushcfunction(L, Lmake_class);
|
lua_pushcfunction(L, Lmake_class);
|
||||||
lua_pushcfunction(L, Lhash);
|
|
||||||
lua_setglobal(L, "extract_hash");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
11
tests.lua
11
tests.lua
@ -252,6 +252,17 @@ test("Testing giant immutable table", function()
|
|||||||
pcall(function() T:from_table(values) end)
|
pcall(function() T:from_table(values) end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
test("Testing tuple iteration", function()
|
||||||
|
local T = immutable(nil)
|
||||||
|
local t = T(1,4,9,16)
|
||||||
|
local checks = {1,4,9,16}
|
||||||
|
for i,v in ipairs(t) do
|
||||||
|
assert(checks[i] == v)
|
||||||
|
checks[i] = nil
|
||||||
|
end
|
||||||
|
assert(next(checks) == nil)
|
||||||
|
end)
|
||||||
|
|
||||||
if num_errors == 0 then
|
if num_errors == 0 then
|
||||||
print(green.."All tests passed!"..reset)
|
print(green.."All tests passed!"..reset)
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user