Misc changes.

This commit is contained in:
Bruce Hill 2018-03-16 15:05:40 -07:00
parent f4c8b30d51
commit 0030c0bc4f
2 changed files with 80 additions and 111 deletions

View File

@ -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;
} }

View File

@ -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