Merge branch 'feature/travis' into develop
[luaevent.git] / src / event_buffer.c
blob833b54b7eeb01d12530787800ed50644cbef5736
1 /* LuaEvent
2 Copyright (C) 2007,2012,2013 Thomas Harning <harningt@gmail.com>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
23 #include <stdlib.h>
24 #include "event_buffer.h"
25 #include <lauxlib.h>
27 #define EVENT_BUFFER_MT "EVENT_BUFFER_MT"
29 #define BUFFER_ADD_CHECK_INPUT_FIRST 1
31 /* Obtains an le_buffer structure from a given index */
32 static le_buffer* event_buffer_get(lua_State* L, int idx) {
33 return (le_buffer*)luaL_checkudata(L, idx, EVENT_BUFFER_MT);
36 /* Obtains an le_buffer structure from a given index
37 AND checks that it hadn't been prematurely freed
39 le_buffer* event_buffer_check(lua_State* L, int idx) {
40 le_buffer* buf = (le_buffer*)luaL_checkudata(L, idx, EVENT_BUFFER_MT);
41 if(!buf->buffer)
42 luaL_argerror(L, idx, "Attempt to use closed event_buffer object");
43 return buf;
46 /* Checks if the given index contains an le_buffer object */
47 int is_event_buffer(lua_State* L, int idx) {
48 int ret;
49 lua_getmetatable(L, idx);
50 luaL_getmetatable(L, EVENT_BUFFER_MT);
51 ret = lua_rawequal(L, -2, -1);
52 lua_pop(L, 2);
53 return ret;
56 /* TODO: Use lightuserdata mapping to locate hanging object instances */
57 /* Pushes the specified evbuffer object onto the stack, attaching a metatable to it */
58 int event_buffer_push(lua_State* L, struct evbuffer* buffer) {
59 le_buffer *buf = (le_buffer*)lua_newuserdata(L, sizeof(le_buffer));
60 buf->buffer = buffer;
61 luaL_getmetatable(L, EVENT_BUFFER_MT);
62 lua_setmetatable(L, -2);
63 return 1;
66 /* LUA: new()
67 Pushes a new evbuffer instance on the stack
69 static int event_buffer_push_new(lua_State* L) {
70 return event_buffer_push(L, evbuffer_new());
73 /* LUA: __gc and buffer:close()
74 Releases the buffer resources
76 static int event_buffer_gc(lua_State* L) {
77 le_buffer* buf = event_buffer_get(L, 1);
78 if(buf->buffer) {
79 evbuffer_free(buf->buffer);
80 buf->buffer = NULL;
82 return 0;
85 /* LUA: buffer:add(...)
86 progressively adds items to the buffer
87 if arg[*] is string, treat as a string:format call
88 if arg[*] is a buffer, perform event_add_buffer
89 expects at least 1 other argument
90 returns number of bytes added
92 static int event_buffer_add(lua_State* L) {
93 le_buffer* buf = event_buffer_check(L, 1);
94 struct evbuffer* buffer = buf->buffer;
95 int oldLength = EVBUFFER_LENGTH(buffer);
96 int last = lua_gettop(L);
97 int i;
98 if(last == 1) luaL_error(L, "Not enough arguments to add: expects at least 1 additional operand");
99 for(i = 2; i <= last; i++) {
100 if(!lua_isstring(L, i) && !is_event_buffer(L, i))
101 luaL_argerror(L, i, "Argument is not a string or buffer object");
102 if(lua_equal(L, 1, i))
103 luaL_argerror(L, i, "Cannot add buffer to itself");
104 /* Optionally perform checks and data loading separately to avoid overfilling the buffer */
105 #if BUFFER_ADD_CHECK_INPUT_FIRST
107 for(i = 2; i <= last; i++) {
108 #endif
109 if(lua_isstring(L, i)) {
110 size_t len;
111 const char* data = lua_tolstring(L, i, &len);
112 if(0 != evbuffer_add(buffer, data, len))
113 luaL_error(L, "Failed to add data to the buffer");
114 } else {
115 le_buffer* buf2 = event_buffer_check(L, i);
116 if(0 != evbuffer_add_buffer(buffer, buf2->buffer))
117 luaL_error(L, "Failed to move buffer-data to the buffer");
120 lua_pushinteger(L, EVBUFFER_LENGTH(buffer) - oldLength);
121 return 1;
124 /* LUA: buffer:length()
125 Returns the length of the buffer contents
127 static int event_buffer_get_length(lua_State* L) {
128 le_buffer* buf = event_buffer_check(L, 1);
129 lua_pushinteger(L, EVBUFFER_LENGTH(buf->buffer));
130 return 1;
133 /* MAYBE: Could add caching */
134 /* LUA: buffer:get_data
135 () - Returns all data in buffer
136 (len) - Returns data up to 'len' bytes long
137 (begin,len) - Returns data beginning at 'begin' up to 'len' bytes long
138 If begin < 0, wraps at data length
139 ex: (-1, 1) returns last character
140 (-2,2) returns last 2 chars [length meaning does not get inverted]
142 static int event_buffer_get_data(lua_State* L) {
143 le_buffer* buf = event_buffer_check(L, 1);
144 int begin, len;
145 switch(lua_gettop(L)) {
146 case 1:
147 /* Obtain full data */
148 begin = 0;
149 len = EVBUFFER_LENGTH(buf->buffer);
150 break;
151 case 2:
152 begin = 0;
153 len = luaL_checkinteger(L, 2);
154 if(len > EVBUFFER_LENGTH(buf->buffer))
155 len = EVBUFFER_LENGTH(buf->buffer);
156 break;
157 case 3:
158 default:
159 /* - 1 to map it to Lua's 1-based indexing
160 * If begin < 0 add length to cause position wrapping
162 begin = luaL_checkinteger(L, 2);
163 if(begin < 0)
164 begin += EVBUFFER_LENGTH(buf->buffer);
165 else
166 begin--;
167 len = luaL_checkinteger(L, 3);
168 /* If length is less than zero, capture entire remaining string */
170 if(len < 0) len = EVBUFFER_LENGTH(buf->buffer);
171 if(begin > EVBUFFER_LENGTH(buf->buffer))
172 begin = EVBUFFER_LENGTH(buf->buffer);
173 if(begin + len > EVBUFFER_LENGTH(buf->buffer))
174 len = EVBUFFER_LENGTH(buf->buffer) - begin;
175 break;
177 lua_pushlstring(L, (const char*)EVBUFFER_DATA(buf->buffer) + begin, len);
178 return 1;
181 /* LUA: buffer:readline()
182 Returns a line terminated by either '\r\n','\n\r' or '\r' or '\n'
183 Returns nil and leaves data alone if no terminator is found
184 Newline is not present in the captured string.
186 static int event_buffer_readline(lua_State* L) {
187 le_buffer* buf = event_buffer_check(L, 1);
188 char* line = evbuffer_readline(buf->buffer);
189 if(!line)
190 return 0;
191 lua_pushstring(L, line);
192 free(line);
193 return 1;
196 /* LUA: buffer:drain(amt)
197 Drains 'amt' bytes from the buffer
198 If amt < 0, drains all data
199 (Due to auto-casting to unsigned int and automatic capping)
201 static int event_buffer_drain(lua_State* L) {
202 le_buffer* buf = event_buffer_check(L, 1);
203 size_t len = luaL_checkinteger(L, 2);
204 evbuffer_drain(buf->buffer, len);
205 return 0;
208 /* LUA: buffer:write
209 (integer/lightuserdata fd) - Attempts to write all the data out to the FD
210 (socket) - Attempts to write all the data out to the socket object
212 static int event_buffer_write(lua_State* L) {
213 le_buffer* buf = event_buffer_check(L, 1);
214 int ret;
215 if(lua_isnumber(L, 2)) {
216 ret = evbuffer_write(buf->buffer, lua_tointeger(L, 2));
217 } else if(lua_islightuserdata(L, 2)) {
218 ret = evbuffer_write(buf->buffer, (int)(long)lua_touserdata(L, 2));
219 } else if(lua_isuserdata(L, 2)) {
220 ret = evbuffer_write(buf->buffer, getSocketFd(L, 2));
221 } else {
222 ret = 0; /* Shush uninitialized warning */
223 luaL_argerror(L, 2, "Unexpected data type. Expects: integer/lightuserdata/socket");
225 lua_pushinteger(L, ret);
226 return 1;
229 /* LUA: buffer:read
230 (integer/lightuserdata fd, len) - Attempts to read up to 'len' out of the FD
231 (socket, len) - Attempts to read up to 'len' out of the socket object
233 static int event_buffer_read(lua_State* L) {
234 le_buffer* buf = event_buffer_check(L, 1);
235 int len = luaL_checkinteger(L, 3);
236 int ret;
237 if(lua_isnumber(L, 2)) {
238 ret = evbuffer_read(buf->buffer, lua_tointeger(L, 2), len);
239 } else if(lua_islightuserdata(L, 2)) {
240 ret = evbuffer_read(buf->buffer, (int)(long)lua_touserdata(L, 2), len);
241 } else if(lua_isuserdata(L, 2)) {
242 ret = evbuffer_read(buf->buffer, getSocketFd(L, 2), len);
243 } else {
244 ret = 0; /* Shush uninitialized warning */
245 luaL_argerror(L, 2, "Unexpected data type. Expects: integer/lightuserdata/socket");
247 lua_pushinteger(L, ret);
248 return 1;
250 static luaL_Reg buffer_funcs[] = {
251 {"add", event_buffer_add},
252 {"length", event_buffer_get_length},
253 {"get_data", event_buffer_get_data},
254 {"readline", event_buffer_readline},
255 {"drain", event_buffer_drain},
256 {"close", event_buffer_gc},
257 {"read", event_buffer_read},
258 {"write", event_buffer_write},
259 {NULL, NULL}
261 static luaL_Reg funcs[] = {
262 {"new", event_buffer_push_new},
263 {NULL, NULL}
266 void event_buffer_register(lua_State* L, int coreIndex) {
267 luaL_newmetatable(L, EVENT_BUFFER_MT);
268 lua_pushcfunction(L, event_buffer_gc);
269 lua_setfield(L, -2, "__gc");
270 lua_pushcfunction(L, event_buffer_get_length);
271 lua_setfield(L, -2, "__len");
272 lua_pushcfunction(L, event_buffer_get_data);
273 lua_setfield(L, -2, "__tostring");
274 lua_newtable(L);
275 luaL_register(L, NULL, buffer_funcs);
276 lua_setfield(L, -2, "__index");
277 lua_pop(L, 1);
279 lua_newtable(L);
280 luaL_register(L, NULL, funcs);
281 lua_setfield(L, coreIndex, "buffer");