Skip to content
Snippets Groups Projects
Commit c53a45ec authored by Nikita Pettik's avatar Nikita Pettik Committed by Vladimir Davydov
Browse files

lua: introduce interface for prbuf

Simply add Lua wrappers for recently introduced prbuf.

Introduce new buffer (in addition to ibuf) - prbuf. "pr" stands for
"partitioned ring-". It save all metadata in the same memory chunk
provided for storage, so it can be completely restored from the 'raw'
memory. API:

```
-- mem is a chunk of raw (char *) memory, of size mem_size.
-- It is used for data storage. Note that available space is of less
-- size due to prbuf metadata overhead.
-- Returns handle to prbuf.
--
require('buffer').prbuf_create(mem, mem_size)

-- mem is a chunk of memory, which contains already created prbuf.
-- It implies that once prbuf_create() was called with the same memory.
-- If mem does not contain valid buffer - raise an appropriate error.
require('buffer').prbuf_open(mem)

-- Returns continuous chunk of memory with given size. May return nil
-- in case if requested chunk is too large. Note that after copying
-- object to returned chunk, it should be committed with prbuf:commit();
-- otherwise :prepare() may return the same chunk twice.
prbuf:prepare(size)

-- Commits the last prepared chunk. Undefined behaviour in case of
-- committing the same chunk twice.
prbuf:commit()

-- Create and return prbuf_iterator. Does not fail. Created iterator
-- points to nowhere - it should be adjusted with :next() call to
-- the first entry.
prbuf:iterator_create()

-- Advance iterator position. Returns prbuf_entry or nil in case
-- iterator has reached the end. Entry consists of two members:
-- size and ptr. The last one is an array of characters of given size.
iterator:next()
```

 Usage examples:

```

local ibuf = buffer.ibuf()
local prbuf_size = 100
local memory = ibuf:alloc(prbuf_size)
local prbuf = buffer.prbuf_create(memory, prbuf_size)

local sample_size = 4
local raw = prbuf:prepare(4)
if raw == nil then
    -- allocation size is too large, try smaller.
end
raw[0] = ...
...
raw[3] = ...
prbuf:commit()
local prbuf_recovered = buffer.prbuf_open(memory)
local iter = prbuf_recovered:iterator_create()
local entry = iter:next()
assert(tonumber(entry.size) == 4)
-- Check values stored in the buffer.
assert(entry.ptr[0] == ...)
entry = iter:next()
-- Our buffer has only one entry.
assert(entry == nil)

```

NO_DOC=<Feature for internal usage>
NO_CHANGELOG=<Feature for internal usage>
parent 4d14961d
No related branches found
No related tags found
No related merge requests found
......@@ -368,6 +368,12 @@ PMurHash32
PMurHash32_Process
PMurHash32_Result
port_destroy
prbuf_create
prbuf_open
prbuf_prepare
prbuf_commit
prbuf_iterator_create
prbuf_iterator_next
random_bytes
say_logger_init
say_logger_initialized
......
......@@ -41,6 +41,45 @@ ibuf_reinit(struct ibuf *ibuf);
void *
ibuf_reserve_slow(struct ibuf *ibuf, size_t size);
/*
* prbuf iterface; see src/lib/core/prbuf.c for details.
*/
struct prbuf_header;
struct prbuf_record;
struct prbuf {
struct prbuf_header *header;
};
struct prbuf_entry {
size_t size;
char *ptr;
};
struct prbuf_iterator {
struct prbuf *buf;
struct prbuf_record *current;
};
void
prbuf_create(struct prbuf *buf, void *mem, size_t size);
int
prbuf_open(struct prbuf *buf, void *mem);
void *
prbuf_prepare(struct prbuf *buf, size_t size);
void
prbuf_commit(struct prbuf *buf);
void
prbuf_iterator_create(struct prbuf *buf, struct prbuf_iterator *iter);
int
prbuf_iterator_next(struct prbuf_iterator *iter, struct prbuf_entry *res);
]]
local builtin = ffi.C
......@@ -182,6 +221,96 @@ local function ibuf_new(arg)
errorf('Usage: ibuf([size])')
end
local prbuf_t = ffi.typeof('struct prbuf')
local prbuf_iterator_t = ffi.typeof('struct prbuf_iterator')
local prbuf_entry_t = ffi.typeof('struct prbuf_entry')
local function prbuf_open(mem)
if not ffi.istype(ffi.typeof('char *'), mem) then
errorf('Attempt to prbuf_open() with argument of wrong type %s',
ffi.typeof(mem))
end
local buf = ffi.new(prbuf_t)
local rc = builtin.prbuf_open(buf, mem)
if rc ~= 0 then
errorf("Failed to open prbuf")
end
return buf
end
local function prbuf_create(mem, size)
if not ffi.istype(ffi.typeof('char *'), mem) then
errorf('Attempt to prbuf_open() with argument of wrong type %s',
ffi.typeof(mem))
end
local buf = ffi.new(prbuf_t)
builtin.prbuf_create(buf, mem, size)
return buf
end
local function prbuf_prepare(buf, size)
if not ffi.istype(prbuf_t, buf) then
errorf('Attempt to call method without object, use prbuf:prepare()')
end
local ptr = builtin.prbuf_prepare(buf, size)
if ptr == nil then return nil end
return ffi.cast('char *', ptr)
end
local function prbuf_commit(buf)
if not ffi.istype(prbuf_t, buf) then
errorf('Attempt to call method without object, use prbuf:commit()')
end
builtin.prbuf_commit(buf)
end
local function prbuf_iterator_create(buf)
if not ffi.istype(prbuf_t, buf) then
errorf('Attempt to call method without object, use prbuf:create()')
end
local iterator = ffi.new(prbuf_iterator_t)
builtin.prbuf_iterator_create(buf, iterator)
return iterator
end
local prbuf_methods = {
prepare = prbuf_prepare;
commit = prbuf_commit;
iterator_create = prbuf_iterator_create;
}
local function prbuf_iterator_next(iterator)
if not ffi.istype(prbuf_iterator_t, iterator) then
errorf('Attempt to iterator:next() without object, use iterator:next()')
end
local entry = ffi.new(prbuf_entry_t)
local rc = builtin.prbuf_iterator_next(iterator, entry)
if rc ~= 0 then return nil end
return entry
end
local prbuf_iterator_methods = {
next = prbuf_iterator_next;
}
local prbuf_iterator_mt = {
__index = prbuf_iterator_methods;
}
ffi.metatype(prbuf_iterator_t, prbuf_iterator_mt);
local function prbuf_tostring(self)
return '<prbuf>'
end
local prbuf_mt = {
__index = prbuf_methods;
__tostring = prbuf_tostring;
};
ffi.metatype(prbuf_t, prbuf_mt);
--
-- Stash keeps an FFI object for re-usage and helps to ensure the proper
-- ownership. Is supposed to be used in yield-free code when almost always it is
......@@ -273,6 +402,8 @@ local internal = {
return {
internal = internal,
ibuf = ibuf_new;
prbuf_open = prbuf_open;
prbuf_create = prbuf_create;
READAHEAD = READAHEAD;
ffi_stash_new = ffi_stash_new,
}
local buffer = require('buffer')
local t = require('luatest')
local g = t.group()
local function fill_memory(memory, size)
for i = 0, size - 1 do
memory[i] = i
end
end
local function check_memory(memory, size)
for i = 0, size - 1 do
if memory[i] ~= i then return false end
end
return true
end
g.test_object_misc = function()
local ibuf = buffer.ibuf()
local prbuf_size = 100
local memory = ibuf:alloc(prbuf_size)
local prbuf = buffer.prbuf_create(memory, prbuf_size)
local sample_size = 4
local entry_count = 5
for _ = 1, entry_count do
local raw = prbuf:prepare(sample_size)
t.assert_equals(raw ~= nil, true)
fill_memory(raw, sample_size)
prbuf:commit()
end
local prbuf_recovered = buffer.prbuf_open(memory)
local iter = prbuf_recovered:iterator_create()
entry_count = 0
local entry = iter:next()
while entry ~= nil do
entry_count = entry_count + 1
t.assert_equals(entry.size, sample_size)
t.assert_equals(check_memory(entry.ptr, tonumber(entry.size)), true)
entry = iter:next()
end
t.assert_equals(entry_count, 5)
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment