Merge 0.10->trunk
[prosody.git] / util-src / ringbuffer.c
1
2
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdio.h>
7
8 #include <lua.h>
9 #include <lauxlib.h>
10
11 #define MIN(a, b) ((a)>(b)?(b):(a))
12 #define MAX(a, b) ((a)>(b)?(a):(b))
13
14 typedef struct {
15         size_t rpos; /* read position */
16         size_t wpos; /* write position */
17         size_t alen; /* allocated size */
18         size_t blen; /* current content size */
19         char* buffer;
20 } ringbuffer;
21
22 char readchar(ringbuffer* b) {
23         b->blen--;
24         return b->buffer[(b->rpos++) % b->alen];
25 }
26
27 void writechar(ringbuffer* b, char c) {
28         b->blen++;
29         b->buffer[(b->wpos++) % b->alen] = c;
30 }
31
32 /* make sure position counters stay within the allocation */
33 void modpos(ringbuffer* b) {
34         b->rpos = b->rpos % b->alen;
35         b->wpos = b->wpos % b->alen;
36 }
37
38 int find(ringbuffer* b, const char* s, int l) {
39         size_t i, j;
40         int m;
41
42         if(b->rpos == b->wpos) { /* empty */
43                 return 0;
44         }
45
46         for(i = 0; i <= b->blen - l; i++) {
47                 if(b->buffer[(b->rpos + i) % b->alen] == *s) {
48                         m = 1;
49
50                         for(j = 1; j < l; j++)
51                                 if(b->buffer[(b->rpos + i + j) % b->alen] != s[j]) {
52                                         m = 0;
53                                         break;
54                                 }
55
56                         if(m) {
57                                 return i + l;
58                         }
59                 }
60         }
61
62         return 0;
63 }
64
65 int rb_find(lua_State* L) {
66         size_t l, m;
67         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
68         const char* s = luaL_checklstring(L, 2, &l);
69         m = find(b, s, l);
70
71         if(m > 0) {
72                 lua_pushinteger(L, m);
73                 return 1;
74         }
75
76         return 0;
77 }
78
79
80 int rb_read(lua_State* L) {
81         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
82         int r = luaL_checkinteger(L, 2);
83         int peek = lua_toboolean(L, 3);
84
85         if(r > b->blen) {
86                 lua_pushnil(L);
87                 return 1;
88         }
89
90         if((b->rpos + r) > b->alen) {
91                 lua_pushlstring(L, &b->buffer[b->rpos], b->alen - b->rpos);
92                 lua_pushlstring(L, b->buffer, r - (b->alen - b->rpos));
93                 lua_concat(L, 2);
94         } else {
95                 lua_pushlstring(L, &b->buffer[b->rpos], r);
96         }
97
98         if(!peek) {
99                 b->blen -= r;
100                 b->rpos += r;
101                 modpos(b);
102         }
103
104         return 1;
105 }
106
107
108 int rb_readuntil(lua_State* L) {
109         size_t l, m;
110         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
111         const char* s = luaL_checklstring(L, 2, &l);
112         m = find(b, s, l);
113
114         if(m > 0) {
115                 lua_settop(L, 1);
116                 lua_pushinteger(L, m);
117                 return rb_read(L);
118         }
119
120         return 0;
121 }
122
123 int rb_write(lua_State* L) {
124         size_t l, w = 0;
125         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
126         const char* s = luaL_checklstring(L, 2, &l);
127
128         /* Does `l` bytes fit? */
129         if((l + b->blen) > b->alen) {
130                 lua_pushnil(L);
131                 return 1;
132         }
133
134         while(l-- > 0) {
135                 writechar(b, *s++);
136                 w++;
137         }
138
139         modpos(b);
140
141         lua_pushinteger(L, w);
142
143         return 1;
144 }
145
146 int rb_tostring(lua_State* L) {
147         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
148         lua_pushfstring(L, "ringbuffer: %p->%p %d/%d", b, b->buffer, b->blen, b->alen);
149         return 1;
150 }
151
152 int rb_length(lua_State* L) {
153         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
154         lua_pushinteger(L, b->blen);
155         return 1;
156 }
157
158 int rb_size(lua_State* L) {
159         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
160         lua_pushinteger(L, b->alen);
161         return 1;
162 }
163
164 int rb_free(lua_State* L) {
165         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
166         lua_pushinteger(L, b->alen - b->blen);
167         return 1;
168 }
169
170 int rb_new(lua_State* L) {
171         size_t size = luaL_optinteger(L, 1, sysconf(_SC_PAGESIZE));
172         ringbuffer* b = lua_newuserdata(L, sizeof(ringbuffer));
173         b->rpos = 0;
174         b->wpos = 0;
175         b->alen = size;
176         b->blen = 0;
177         b->buffer = malloc(size);
178
179         if(b->buffer == NULL) {
180                 return 0;
181         }
182
183         luaL_getmetatable(L, "ringbuffer_mt");
184         lua_setmetatable(L, -2);
185
186         return 1;
187 }
188
189 int rb_gc(lua_State* L) {
190         ringbuffer* b = luaL_checkudata(L, 1, "ringbuffer_mt");
191
192         if(b->buffer != NULL) {
193                 free(b->buffer);
194         }
195
196         return 0;
197 }
198
199 int luaopen_util_ringbuffer(lua_State* L) {
200         if(luaL_newmetatable(L, "ringbuffer_mt")) {
201                 lua_pushcfunction(L, rb_tostring);
202                 lua_setfield(L, -2, "__tostring");
203                 lua_pushcfunction(L, rb_length);
204                 lua_setfield(L, -2, "__len");
205                 lua_pushcfunction(L, rb_gc);
206                 lua_setfield(L, -2, "__gc");
207
208                 lua_newtable(L); /* __index */
209                 {
210                         lua_pushcfunction(L, rb_find);
211                         lua_setfield(L, -2, "find");
212                         lua_pushcfunction(L, rb_read);
213                         lua_setfield(L, -2, "read");
214                         lua_pushcfunction(L, rb_readuntil);
215                         lua_setfield(L, -2, "readuntil");
216                         lua_pushcfunction(L, rb_write);
217                         lua_setfield(L, -2, "write");
218                         lua_pushcfunction(L, rb_size);
219                         lua_setfield(L, -2, "size");
220                         lua_pushcfunction(L, rb_length);
221                         lua_setfield(L, -2, "length");
222                         lua_pushcfunction(L, rb_free);
223                         lua_setfield(L, -2, "free");
224                 }
225                 lua_setfield(L, -2, "__index");
226         }
227
228         lua_newtable(L);
229         lua_pushcfunction(L, rb_new);
230         lua_setfield(L, -2, "new");
231         return 1;
232 }