Skip to content

Commit

Permalink
ctable: hash_fn param is optional
Browse files Browse the repository at this point in the history
If no hash_fn is given, ctable will provide one.
  • Loading branch information
wingo committed Nov 2, 2016
1 parent 7ffbce7 commit 580d8c7
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 3 deletions.
10 changes: 9 additions & 1 deletion src/lib/README.ctable.md
Expand Up @@ -62,7 +62,6 @@ following keys are required:

* `key_type`: An FFI type (LuaJIT "ctype") for keys in this table.
* `value_type`: An FFI type (LuaJT "ctype") for values in this table.
* `hash_fn`: A function that takes a key and returns a hash value.

Hash values are unsigned 32-bit integers in the range `[0,
0xFFFFFFFF)`. That is to say, `0xFFFFFFFF` is the only unsigned 32-bit
Expand All @@ -71,6 +70,9 @@ hash value in the correct range.

Optional entries that may be present in the *parameters* table include:

* `hash_fn`: A function that takes a key and returns a hash value.
If not given, defaults to the result of calling `compute_hash_fn`
on the key type.
* `initial_size`: The initial size of the hash table, including free
space. Defaults to 8 slots.
* `max_occupancy_rate`: The maximum ratio of `occupancy/size`, where
Expand Down Expand Up @@ -198,3 +200,9 @@ Hash the first 48 bits of a byte sequence.
— Function **ctable.hashv_64** *ptr*

Hash the first 64 bits of a byte sequence.

— Function **ctable.compute_hash_fn** *ctype*

Return a `hashv_`-like hash function over the bytes in instances of
*ctype*. Note that the same reservations apply as for `hash_32`
above.
25 changes: 23 additions & 2 deletions src/lib/ctable.lua
Expand Up @@ -91,8 +91,9 @@ end
-- FIXME: For now the value_type option is required, but in the future
-- we should allow for a nil value type to create a set instead of a
-- map.
local required_params = set('key_type', 'value_type', 'hash_fn')
local required_params = set('key_type', 'value_type')
local optional_params = {
hash_fn = false,
initial_size = 8,
max_occupancy_rate = 0.9,
min_occupancy_rate = 0.0
Expand All @@ -103,7 +104,7 @@ function new(params)
local params = parse_params(params, required_params, optional_params)
ctab.entry_type = make_entry_type(params.key_type, params.value_type)
ctab.type = make_entries_type(ctab.entry_type)
ctab.hash_fn = params.hash_fn
ctab.hash_fn = params.hash_fn or compute_hash_fn(params.key_type)
ctab.equal_fn = make_equal_fn(params.key_type)
ctab.size = 0
ctab.occupancy = 0
Expand Down Expand Up @@ -391,6 +392,7 @@ function hash_32(i32)
return uint32_cast[0]
end

local cast = ffi.cast
function hashv_32(key)
return hash_32(cast(uint32_ptr_t, key)[0])
end
Expand All @@ -410,6 +412,22 @@ function hashv_64(key)
return hash_32(bxor(hi, hash_32(lo)))
end

local hash_fns_by_size = { [4]=hashv_32, [8]=hashv_64 }
function compute_hash_fn(ctype)
local size = ffi.sizeof(ctype)
if not hash_fns_by_size[size] then
hash_fns_by_size[size] = function(key)
local h = 0
local words = cast(uint32_ptr_t, key)
local bytes = cast('uint8_t*', key)
for i=0,size/4 do h = hash_32(bxor(h, words[i])) end
for i=1,size%4 do h = hash_32(bxor(h, bytes[size-i])) end
return h
end
end
return hash_fns_by_size[size]
end

function selftest()
print("selftest: ctable")

Expand Down Expand Up @@ -457,8 +475,11 @@ function selftest()

local function check_bytes_equal(type, a, b)
local equal_fn = make_equal_fn(type)
local hash_fn = compute_hash_fn(type)
assert(equal_fn(ffi.new(type, a), ffi.new(type, a)))
assert(not equal_fn(ffi.new(type, a), ffi.new(type, b)))
assert(hash_fn(ffi.new(type, a)) == hash_fn(ffi.new(type, a)))
assert(hash_fn(ffi.new(type, a)) ~= hash_fn(ffi.new(type, b)))
end
check_bytes_equal(ffi.typeof('uint16_t[1]'), {1}, {2}) -- 2 byte
check_bytes_equal(ffi.typeof('uint32_t[1]'), {1}, {2}) -- 4 byte
Expand Down

0 comments on commit 580d8c7

Please sign in to comment.