Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge PR #1059 (lib.cltable: hash table w/ FFI keys and Lua vals) int…
…o max-next
- Loading branch information
Showing
4 changed files
with
169 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
### cltable (lib.cltable) | ||
|
||
Ever been annoyed that you can't create a hash table where the keys are | ||
FFI values, like raw IPv4 addresses, but the values are Lua objects? | ||
Well of course you can key a normal Lua table by any Lua value, but the | ||
key is looked up by identity and not by value, which is rarely what you | ||
want. `foo[lib.protocol.ipv4:pton('1.2.3.4')]` will not be the same as | ||
`foo[lib.protocol.ipv4:pton('1.2.3.4')]`, as the `pton` call produces a | ||
fresh value every time. What you usually want with FFI-keyed tables is | ||
to be able to look up the entry by value, not by identity. | ||
|
||
Well never fear, *cltable* is here. A cltable is a data type that | ||
associates FFI keys with any old Lua value. When you look up a key in a | ||
cltable, the key is matched by-value. | ||
|
||
Externally, a cltable provides the same interface as a Lua table, with | ||
the exception that to iterate over the table's values, you need to use | ||
`cltable.pairs` function instead of `pairs`. | ||
|
||
Internally, cltable uses a [`ctable`](./README.ctable.md) to map the key | ||
to an index, then if an entry is found, looks up that index in a side | ||
table of Lua objects. See the ctable documentation for more performance | ||
characteristics. | ||
|
||
To create a cltable, use pass an appropriate parameter table to | ||
`cltable.new`, like this: | ||
|
||
```lua | ||
local cltable = require('lib.cltable') | ||
local ffi = require('ffi') | ||
local params = { key_type = ffi.typeof('uint8_t[4]') } | ||
local cltab = cltable.new(params) | ||
``` | ||
|
||
— Function **cltable.new** *parameters* | ||
|
||
Create a new cltable. *parameters* is a table of key/value pairs. The | ||
following key is required: | ||
|
||
* `key_type`: An FFI type (LuaJIT "ctype") for keys in this table. | ||
|
||
Optional entries that may be present in the *parameters* table are | ||
`hash_fn`, `initial_size`, `max_occupancy_rate`, and | ||
`min_occupancy_rate`. See the ctable documentation for their meanings. | ||
|
||
— Function **cltable.build** *keys* *values* | ||
|
||
Given the ctable *keys* that maps keys to indexes, and a corresponding | ||
Lua table *values* containing the index->value associations, return a | ||
cltable. | ||
|
||
— Property **.keys** | ||
|
||
A cltable's `keys` property holds the table's keys, as a ctable. If you | ||
modify it, you get to keep both pieces. | ||
|
||
— Property **.values** | ||
|
||
Likewise, a cltable's `values` property holds the table's values, as a | ||
Lua array (table). If you break it, you buy it! | ||
|
||
— Function **cltable.pairs** *cltable* | ||
|
||
Return an iterator over the keys and values in *cltable*. Use this when | ||
you would use `pairs` on a regular Lua table. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
module(..., package.seeall) | ||
|
||
local ffi = require("ffi") | ||
local ctable = require("lib.ctable") | ||
|
||
function build(keys, values) | ||
return setmetatable({ keys = keys, values = values }, | ||
{__index=get, __newindex=set}) | ||
end | ||
|
||
function new(params) | ||
local ctable_params = {} | ||
for k,v in _G.pairs(params) do ctable_params[k] = v end | ||
assert(not ctable_params.value_type) | ||
ctable_params.value_type = ffi.typeof('uint32_t') | ||
return build(ctable.new(ctable_params), {}) | ||
end | ||
|
||
function get(cltable, key) | ||
local entry = cltable.keys:lookup_ptr(key) | ||
if not entry then return nil end | ||
return cltable.values[entry.value] | ||
end | ||
|
||
function set(cltable, key, value) | ||
local entry = cltable.keys:lookup_ptr(key) | ||
if entry then | ||
cltable.values[entry.value] = value | ||
if value == nil then cltable.keys:remove_ptr(entry) end | ||
elseif value ~= nil then | ||
table.insert(cltable.values, value) | ||
cltable.keys:add(key, #cltable.values) | ||
end | ||
end | ||
|
||
function pairs(cltable) | ||
local ctable_next, ctable_max, ctable_entry = cltable.keys:iterate() | ||
return function() | ||
ctable_entry = ctable_next(ctable_max, ctable_entry) | ||
if not ctable_entry then return end | ||
return ctable_entry.key, cltable.values[ctable_entry.value] | ||
end | ||
end | ||
|
||
function selftest() | ||
print("selftest: cltable") | ||
|
||
local ipv4 = require('lib.protocol.ipv4') | ||
local params = { key_type = ffi.typeof('uint8_t[4]') } | ||
local cltab = new(params) | ||
|
||
for i=0,255 do | ||
local addr = ipv4:pton('1.2.3.'..i) | ||
cltab[addr] = 'hello, '..i | ||
end | ||
|
||
for i=0,255 do | ||
local addr = ipv4:pton('1.2.3.'..i) | ||
assert(cltab[addr] == 'hello, '..i) | ||
end | ||
|
||
for i=0,255 do | ||
-- Remove value that is present. | ||
cltab[ipv4:pton('1.2.3.'..i)] = nil | ||
-- Remove value that is not present. | ||
cltab[ipv4:pton('2.3.4.'..i)] = nil | ||
end | ||
|
||
for k,v in pairs(cltab) do error('not reachable') end | ||
|
||
print("selftest: ok") | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters