# HG changeset patch # User Kim Alvefur # Date 1454441067 -3600 # Node ID 2b4432cc9c290a43cbc4d23ccbc7b83ad15cf58d # Parent 805d068d2fd533fa0d88150e5a09d13364e31aef Add util.ringbuffer, a ringbuffer with a file handle like interface diff -r 805d068d2fd5 -r 2b4432cc9c29 util-src/Makefile --- a/util-src/Makefile Mon Feb 01 21:26:15 2016 +0000 +++ b/util-src/Makefile Tue Feb 02 20:24:27 2016 +0100 @@ -1,12 +1,12 @@ include ../config.unix -CFLAGS+=-ggdb -I$(LUA_INCDIR) +CFLAGS+=-ggdb -Wall -pedantic -I$(LUA_INCDIR) INSTALL_DATA=install -m644 TARGET?=../util/ -ALL=encodings.so hashes.so net.so pposix.so signal.so table.so +ALL=encodings.so hashes.so net.so pposix.so signal.so table.so ringbuffer.so .PHONY: all install clean .SUFFIXES: .c .o .so diff -r 805d068d2fd5 -r 2b4432cc9c29 util-src/ringbuffer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util-src/ringbuffer.c Tue Feb 02 20:24:27 2016 +0100 @@ -0,0 +1,232 @@ + + +#include +#include +#include +#include + +#include +#include + +#define MIN(a, b) ((a)>(b)?(b):(a)) +#define MAX(a, b) ((a)>(b)?(a):(b)) + +typedef struct { + size_t rpos; /* read position */ + size_t wpos; /* write position */ + size_t alen; /* allocated size */ + size_t blen; /* current content size */ + char* buffer; +} ringbuffer; + +char readchar(ringbuffer* b) { + b->blen--; + return b->buffer[(b->rpos++) % b->alen]; +} + +void writechar(ringbuffer* b, char c) { + b->blen++; + b->buffer[(b->wpos++) % b->alen] = c; +} + +/* make sure position counters stay within the allocation */ +void modpos(ringbuffer* b) { + b->rpos = b->rpos % b->alen; + b->wpos = b->wpos % b->alen; +} + +int find(ringbuffer* b, const char* s, int l) { + size_t i, j; + int m; + + if(b->rpos == b->wpos) { /* empty */ + return 0; + } + + for(i = 0; i <= b->blen - l; i++) { + if(b->buffer[(b->rpos + i) % b->alen] == *s) { + m = 1; + + for(j = 1; j < l; j++) + if(b->buffer[(b->rpos + i + j) % b->alen] != s[j]) { + m = 0; + break; + } + + if(m) { + return i + l; + } + } + } + + return 0; +} + +int rb_find(lua_State* L) { + size_t l, m; + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + const char* s = luaL_checklstring(L, 2, &l); + m = find(b, s, l); + + if(m > 0) { + lua_pushinteger(L, m); + return 1; + } + + return 0; +} + + +int rb_read(lua_State* L) { + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + int r = luaL_checkinteger(L, 2); + int peek = lua_toboolean(L, 3); + + if(r > b->blen) { + lua_pushnil(L); + return 1; + } + + if((b->rpos + r) > b->alen) { + lua_pushlstring(L, &b->buffer[b->rpos], b->alen - b->rpos); + lua_pushlstring(L, b->buffer, r - (b->alen - b->rpos)); + lua_concat(L, 2); + } else { + lua_pushlstring(L, &b->buffer[b->rpos], r); + } + + if(!peek) { + b->blen -= r; + b->rpos += r; + modpos(b); + } + + return 1; +} + + +int rb_readuntil(lua_State* L) { + size_t l, m; + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + const char* s = luaL_checklstring(L, 2, &l); + m = find(b, s, l); + + if(m > 0) { + lua_settop(L, 1); + lua_pushinteger(L, m); + return rb_read(L); + } + + return 0; +} + +int rb_write(lua_State* L) { + size_t l, w = 0; + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + const char* s = luaL_checklstring(L, 2, &l); + + /* Does `l` bytes fit? */ + if((l + b->blen) > b->alen) { + lua_pushnil(L); + return 1; + } + + while(l-- > 0) { + writechar(b, *s++); + w++; + } + + modpos(b); + + lua_pushinteger(L, w); + + return 1; +} + +int rb_tostring(lua_State* L) { + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + lua_pushfstring(L, "ringbuffer: %p->%p %d/%d", b, b->buffer, b->blen, b->alen); + return 1; +} + +int rb_length(lua_State* L) { + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + lua_pushinteger(L, b->blen); + return 1; +} + +int rb_size(lua_State* L) { + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + lua_pushinteger(L, b->alen); + return 1; +} + +int rb_free(lua_State* L) { + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + lua_pushinteger(L, b->alen - b->blen); + return 1; +} + +int rb_new(lua_State* L) { + size_t size = luaL_optinteger(L, 1, sysconf(_SC_PAGESIZE)); + ringbuffer* b = lua_newuserdata(L, sizeof(ringbuffer)); + b->rpos = 0; + b->wpos = 0; + b->alen = size; + b->blen = 0; + b->buffer = malloc(size); + + if(b->buffer == NULL) { + return 0; + } + + luaL_getmetatable(L, "ringbuffer_mt"); + lua_setmetatable(L, -2); + + return 1; +} + +int rb_gc(lua_State* L) { + ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt"); + + if(b->buffer != NULL) { + free(b->buffer); + } + + return 0; +} + +int luaopen_util_ringbuffer(lua_State* L) { + if(luaL_newmetatable(L, "ringbuffer_mt")) { + lua_pushcfunction(L, rb_tostring); + lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, rb_length); + lua_setfield(L, -2, "__len"); + lua_pushcfunction(L, rb_gc); + lua_setfield(L, -2, "__gc"); + + lua_newtable(L); /* __index */ + { + lua_pushcfunction(L, rb_find); + lua_setfield(L, -2, "find"); + lua_pushcfunction(L, rb_read); + lua_setfield(L, -2, "read"); + lua_pushcfunction(L, rb_readuntil); + lua_setfield(L, -2, "readuntil"); + lua_pushcfunction(L, rb_write); + lua_setfield(L, -2, "write"); + lua_pushcfunction(L, rb_size); + lua_setfield(L, -2, "size"); + lua_pushcfunction(L, rb_length); + lua_setfield(L, -2, "length"); + lua_pushcfunction(L, rb_free); + lua_setfield(L, -2, "free"); + } + lua_setfield(L, -2, "__index"); + } + + lua_newtable(L); + lua_pushcfunction(L, rb_new); + lua_setfield(L, -2, "new"); + return 1; +}