From 0030c0bc4fc7585118b2cdc8cf559fbccd2eee7a Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 16 Mar 2018 15:05:40 -0700 Subject: [PATCH] Misc changes. --- limmutable.c | 180 ++++++++++++++++++++------------------------------- tests.lua | 11 ++++ 2 files changed, 80 insertions(+), 111 deletions(-) diff --git a/limmutable.c b/limmutable.c index 3a7cec4..4f9d80d 100644 --- a/limmutable.c +++ b/limmutable.c @@ -48,59 +48,6 @@ typedef struct { size_t len; } 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) { size_t n_args = lua_gettop(L)-1; @@ -166,9 +113,7 @@ static int Lcreate_instance(lua_State *L) // Find bucket lua_rawgeti(L, -1, hash); // Stack: [buckets, bucket] - int created_bucket = 0; if (lua_isnil(L, -1)) { - created_bucket = 1; // Make a new bucket // Stack: [buckets, nil] lua_pop(L, 1); @@ -184,36 +129,33 @@ static int Lcreate_instance(lua_State *L) // Stack: [buckets, bucket, bucket] lua_rawseti(L, -3, hash); // Stack: [buckets, bucket] - } - // Stack: [buckets, bucket] - if (! created_bucket) { + } else { + // Stack: [buckets, bucket] // scan bucket lua_pushnil(L); while (lua_next(L, -2) != 0) { // for hash_collider_inst, hash_collider in pairs(bucket) do // Stack: [buckets, bucket, hash_collider_inst, hash_collider] - int bucket_item_matches = 1; // Shallow equality check: lua_pushnil(L); 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] 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] lua_pop(L, 3); // Stack: [buckets, bucket, hash_collider_inst] - break; // go to next item in the bucket + goto next_bucket_item; } else { // Stack: [buckets, bucket, hash_collider_inst, hash_collider, i, value] lua_pop(L, 1); // Stack: [buckets, bucket, hash_collider_inst, hash_collider, i] } } - if (bucket_item_matches) { - // Stack: [buckets, bucket, hash_collider_inst, hash_collider] - lua_pop(L, 1); - // Found matching pre-existing instance - return 1; - } + // bucket item matches + // Stack: [buckets, bucket, hash_collider_inst, hash_collider] + lua_pop(L, 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] lua_settable(L, -4); // buckets[inst_userdata] = inst_table // Stack [buckets, bucket, inst_userdata] - lua_gc(L, LUA_GCSTEP, 1); + lua_gc(L, LUA_GCSTEP, 3); return 1; } @@ -344,6 +286,9 @@ static int Lindex(lua_State *L) lua_getfield(L, -1, "__indices"); // Stack: [mt, indices] if (lua_isnil(L, -1)) { + if (! lua_isinteger(L, 2)) { + goto class_fallback; + } lua_pop(L, 1); // Stack: [mt] lua_getfield(L, -1, "__instances"); @@ -359,6 +304,9 @@ static int Lindex(lua_State *L) // Stack: [mt, buckets, bucket, inst_table] int i = luaL_checkinteger(L, 2); lua_rawgeti(L, -1, i); + if (lua_isnil(L, -1)) { + goto class_fallback; + } return 1; } @@ -375,32 +323,30 @@ static int Lindex(lua_State *L) lua_pushvalue(L, 2); } } - if (! lua_isnil(L, -1)) { // Found the field name - // 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; - } 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; + if (lua_isnil(L, -1)) { // Didn't find the field name + goto class_fallback; } + // 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) @@ -446,15 +392,12 @@ static int Ltostring(lua_State *L) lua_pop(L, 1); // Stack: [mt, buckets, bucket, inst_table, tostring] immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1); - int n = info->len; - int needs_comma = 0; - for (int i = 1; i <= n; i++) { + int n = info->len, i = 1; + goto first_list_item; + while (++i <= n) { // Stack: [mt, buckets, bucket, inst_table, tostring, ???] - if (needs_comma) { - luaL_addstring(&b, ", "); - } else { - needs_comma = 1; - } + luaL_addstring(&b, ", "); + first_list_item: lua_pushvalue(L, tostring_index); // Stack: [mt, buckets, bucket, inst_table, tostring, ???, tostring] lua_rawgeti(L, inst_table_index, i); @@ -465,16 +408,13 @@ static int Ltostring(lua_State *L) // Stack: [mt, buckets, bucket, inst_table, tostring, ???] } } else { - int num_fields = lua_objlen(L, -1); int fields_index = lua_gettop(L); - int needs_comma = 0; - for (int i = 1; i <= num_fields; i++) { + int i = 1, num_fields = lua_objlen(L, -1); + goto first_table_item; + while (++i <= num_fields) { // Stack: [mt, buckets, bucket, inst_table, tostring, fields, ???] - if (needs_comma) { - luaL_addstring(&b, ", "); - } else { - needs_comma = 1; - } + luaL_addstring(&b, ", "); + first_table_item: lua_pushvalue(L, tostring_index); // Stack: [mt, buckets, bucket, inst_table, tostring, fields, ???, tostring] lua_rawgeti(L, fields_index, i); @@ -618,6 +558,25 @@ static int Lpairs(lua_State *L) 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[] = { { "__len", Llen}, @@ -627,6 +586,7 @@ static const luaL_Reg Rinstance_metamethods[] = { "__pairs", Lpairs}, { "from_table", Lfrom_table}, { "is_instance", Lis_instance}, + { "table", Ltable}, { NULL, NULL} }; @@ -753,7 +713,5 @@ LUALIB_API int luaopen_immutable(lua_State *L) lua_settable(L, LUA_REGISTRYINDEX); lua_pushcfunction(L, Lmake_class); - lua_pushcfunction(L, Lhash); - lua_setglobal(L, "extract_hash"); return 1; } diff --git a/tests.lua b/tests.lua index 17d0c1b..303071f 100644 --- a/tests.lua +++ b/tests.lua @@ -252,6 +252,17 @@ test("Testing giant immutable table", function() pcall(function() T:from_table(values) 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 print(green.."All tests passed!"..reset) else