diff --git a/extra/exports b/extra/exports index c8d2ceef4678e7803b6b5016d442d2d40cf6d37c..2ecb4ef12f112fd6fc2f2ea2fa32c668ec7db5cb 100644 --- a/extra/exports +++ b/extra/exports @@ -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 diff --git a/src/lua/buffer.lua b/src/lua/buffer.lua index 182c0b015cecd25c56cef0b4702213e1c656ce3e..a93dfbc5d85cd94fb1173b7b6d12f358c9f211c6 100644 --- a/src/lua/buffer.lua +++ b/src/lua/buffer.lua @@ -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, } diff --git a/test/app-luatest/prbuf_test.lua b/test/app-luatest/prbuf_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..633eea8935dd99961ed707feae2e300f1befb7d1 --- /dev/null +++ b/test/app-luatest/prbuf_test.lua @@ -0,0 +1,44 @@ +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