diff options
author | Kim Alvefur <zash@zash.se> | 2016-02-02 20:24:27 +0100 |
---|---|---|
committer | Kim Alvefur <zash@zash.se> | 2016-02-02 20:24:27 +0100 |
commit | b6caeb1f9326c5ced1b3f7ef15fea8fbfd5dffc5 (patch) | |
tree | 03bc62ffb8dffaa08d4ea7c991f09460458197d8 /util-src/ringbuffer.c | |
parent | d0c5e1571fcd0d0242815c1076d56ecf917eec13 (diff) | |
download | prosody-b6caeb1f9326c5ced1b3f7ef15fea8fbfd5dffc5.tar.gz prosody-b6caeb1f9326c5ced1b3f7ef15fea8fbfd5dffc5.zip |
Add util.ringbuffer, a ringbuffer with a file handle like interface
Diffstat (limited to 'util-src/ringbuffer.c')
-rw-r--r-- | util-src/ringbuffer.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/util-src/ringbuffer.c b/util-src/ringbuffer.c new file mode 100644 index 00000000..f5fa136b --- /dev/null +++ b/util-src/ringbuffer.c @@ -0,0 +1,232 @@ + + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#include <lua.h> +#include <lauxlib.h> + +#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; +} |