Skip to content

Commit

Permalink
Merge pull request #1049 from Igalia/lua-io
Browse files Browse the repository at this point in the history
Add replacement implementation of Lua IO
  • Loading branch information
wingo authored May 3, 2018
2 parents 9836231 + 38f1ad4 commit 39fc947
Show file tree
Hide file tree
Showing 23 changed files with 1,326 additions and 518 deletions.
3 changes: 2 additions & 1 deletion src/apps/lwaftr/binding_table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -277,14 +277,15 @@ end
function selftest()
print('selftest: binding_table')
local function load_str(str)
local mem = require("lib.stream.mem")
local yang = require('lib.yang.yang')
local data = require('lib.yang.data')
local schema = yang.load_schema_by_name('snabb-softwire-v2')
local grammar = data.config_grammar_from_schema(schema)
local subgrammar = assert(grammar.members['softwire-config'])
local subgrammar = assert(subgrammar.members['binding-table'])
local parse = data.data_parser_from_grammar(subgrammar)
return load(parse(str, '[test suite]'))
return load(parse(mem.open_input_string(str)))
end
local map = load_str([[
softwire { ipv4 178.79.150.233; psid 80; b4-ipv6 127:2:3:4:5:6:7:128; br-address 8:9:a:b:c:d:e:f; port-set { psid-length 16; }}
Expand Down
28 changes: 0 additions & 28 deletions src/apps/lwaftr/rangemap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -153,34 +153,6 @@ function RangeMap:iterate()
return next_entry
end

local range_map_header_t = ffi.typeof[[
struct {
uint32_t size;
uint32_t entry_size;
}
]]

function RangeMap:save(stream)
local entry_size = ffi.sizeof(self.entry_type)
stream:write_ptr(range_map_header_t(self.size, entry_size),
range_map_header_t)
stream:write_array(self.entries, self.entry_type, self.size)
end

function load(stream, value_type)
local map = {}
map.value_type = value_type
map.entry_type = make_entry_type(map.value_type)
map.type = make_entries_type(map.entry_type)

local header = stream:read_ptr(range_map_header_t)
assert(header.entry_size == ffi.sizeof(map.entry_type))
map.size = header.size
map.entries = stream:read_array(map.entry_type, map.size)
map.binary_search = binary_search.gen(map.size, map.entry_type)
return setmetatable(map, { __index = RangeMap })
end

function selftest()
local builder = RangeMapBuilder.new(ffi.typeof('uint8_t'))
builder:add(0, 1)
Expand Down
161 changes: 161 additions & 0 deletions src/lib/buffer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
-- Use of this source code is governed by the Apache 2.0 license; see COPYING.

-- Ring buffer for bytes

module(...,package.seeall)

local lib = require("core.lib")
local ffi = require("ffi")
local bit = require("bit")

local band = bit.band

local buffer_t = ffi.typeof[[
struct {
uint32_t read_idx, write_idx;
uint32_t size;
uint8_t buf[?];
} __attribute__((packed))
]]

local function to_uint32(n)
return ffi.new('uint32_t[1]', n)[0]
end

function new(size)
local ret = buffer_t(size)
ret:init(size)
return ret
end

local buffer = {}
buffer.__index = buffer

function buffer:init(size)
assert(size ~= 0 and band(size, size - 1) == 0, "size not power of two")
self.size = size
return self
end

function buffer:reset()
self.write_idx, self.read_idx = 0, 0
end

function buffer:is_empty()
return self.write_idx == self.read_idx
end
function buffer:read_avail()
return to_uint32(self.write_idx - self.read_idx)
end
function buffer:is_full()
return self:read_avail() == self.size
end
function buffer:write_avail()
return self.size - self:read_avail()
end

function buffer:write_pos()
return band(self.write_idx, self.size - 1)
end
function buffer:rewrite_pos(offset)
return band(self.read_idx + offset, self.size - 1)
end
function buffer:read_pos()
return band(self.read_idx, self.size - 1)
end

function buffer:advance_write(count)
self.write_idx = self.write_idx + count
end
function buffer:advance_read(count)
self.read_idx = self.read_idx + count
end

function buffer:write(bytes, count)
if count > self:write_avail() then error('write xrun') end
local pos = self:write_pos()
local count1 = math.min(self.size - pos, count)
ffi.copy(self.buf + pos, bytes, count1)
ffi.copy(self.buf, bytes + count1, count - count1)
self:advance_write(count)
end

function buffer:rewrite(offset, bytes, count)
if offset + count > self:read_avail() then error('rewrite xrun') end
local pos = self:rewrite_pos(offset)
local count1 = math.min(self.size - pos, count)
ffi.copy(self.buf + pos, bytes, count1)
ffi.copy(self.buf, bytes + count1, count - count1)
end

function buffer:read(bytes, count)
if count > self:read_avail() then error('read xrun') end
local pos = self:read_pos()
local count1 = math.min(self.size - pos, count)
ffi.copy(bytes, self.buf + pos, count1)
ffi.copy(bytes + count1, self.buf, count - count1)
self:advance_read(count)
end

function buffer:drop(count)
if count > self:read_avail() then error('read xrun') end
self:advance_read(count)
end

function buffer:peek()
local pos = self:read_pos()
return self.buf + pos, math.min(self:read_avail(), self.size - pos)
end

buffer_t = ffi.metatype(buffer_t, buffer)

function selftest()
print('selftest: lib.buffer')
local function assert_throws(f, ...)
local success, ret = pcall(f, ...)
assert(not success, "expected failure but got "..tostring(ret))
end
local function assert_avail(b, readable, writable)
assert(b:read_avail() == readable)
assert(b:write_avail() == writable)
end
local function write_str(b, str)
local scratch = ffi.new('uint8_t[?]', #str)
ffi.copy(scratch, str, #str)
b:write(scratch, #str)
end
local function read_str(b, count)
local scratch = ffi.new('uint8_t[?]', count)
b:read(scratch, count)
return ffi.string(scratch, count)
end

assert_throws(new, 10)
local b = new(16)
assert_avail(b, 0, 16)
for i = 1,10 do
local s = '0123456789'
write_str(b, s)
assert_avail(b, #s, 16-#s)
assert(read_str(b, #s) == s)
assert_avail(b, 0, 16)
end

local ptr, avail = b:peek()
assert(avail == 0)
write_str(b, "foo")
local ptr, avail = b:peek()
assert(avail > 0)

-- Test wrap of indices.
local s = "overflow"
b.read_idx = to_uint32(3 - #s)
b.write_idx = b.read_idx
assert_avail(b, 0, 16)
write_str(b, s)
assert_avail(b, #s, 16-#s)
assert(read_str(b, #s) == s)
assert_avail(b, 0, 16)

print('selftest: ok')
end
52 changes: 13 additions & 39 deletions src/lib/ctable.lua
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ struct {
]]

function load(stream, params)
local header = stream:read_ptr(header_t)
local header = stream:read_struct(nil, header_t)
local params_copy = {}
for k,v in pairs(params) do params_copy[k] = v end
params_copy.initial_size = header.size
Expand All @@ -262,19 +262,19 @@ function load(stream, params)
-- Slurp the entries directly into the ctable's backing store.
-- This ensures that the ctable is in hugepages.
C.memcpy(ctab.entries,
stream:read_array(ctab.entry_type, entry_count),
stream:read_array(nil, ctab.entry_type, entry_count),
ffi.sizeof(ctab.entry_type) * entry_count)

return ctab
end

function CTable:save(stream)
stream:write_ptr(header_t(self.size, self.occupancy, self.max_displacement,
self.hash_seed, self.max_occupancy_rate,
self.min_occupancy_rate),
header_t)
stream:write_array(self.entries,
self.entry_type,
stream:write_struct(header_t,
header_t(self.size, self.occupancy, self.max_displacement,
self.hash_seed, self.max_occupancy_rate,
self.min_occupancy_rate))
stream:write_array(self.entry_type,
self.entries,
self.size + self.max_displacement)
end

Expand Down Expand Up @@ -662,42 +662,16 @@ function selftest()
-- Save the table out to disk, reload it, and run the same
-- checks.
local tmp = os.tmpname()
local file = require("lib.stream.file")
do
local file = io.open(tmp, 'wb')
local function write(ptr, size)
file:write(ffi.string(ptr, size))
end
local stream = {}
function stream:write_ptr(ptr, type)
assert(ffi.sizeof(ptr) == ffi.sizeof(type))
write(ptr, ffi.sizeof(type))
end
function stream:write_array(ptr, type, count)
write(ptr, ffi.sizeof(type) * count)
end
local stream = file.open(tmp, 'wb')
ctab:save(stream)
file:close()
stream:close()
end
do
local file = io.open(tmp, 'rb')
-- keep references to avoid GCing too early
local handle = {}
local function read(size)
local buf = ffi.new('uint8_t[?]', size)
ffi.copy(buf, file:read(size), size)
table.insert(handle, buf)
return buf
end
local stream = {}
function stream:read_ptr(type)
return ffi.cast(ffi.typeof('$*', type), read(ffi.sizeof(type)))
end
function stream:read_array(type, count)
return ffi.cast(ffi.typeof('$*', type),
read(ffi.sizeof(type) * count))
end
local stream = file.open(tmp, 'rb')
ctab = load(stream, params)
file:close()
stream:close()
end
os.remove(tmp)
end
Expand Down
Loading

0 comments on commit 39fc947

Please sign in to comment.