Exposed event_buffer checks/get/push operations in prep for buffer_event
[luaevent.git] / src / event_buffer.c
blob7eb7260d814d95af32d197c88480ec45901734cb
1 /* LuaEvent - Copyright (C) 2007 Thomas Harning <harningt@gmail.com>
2 * Licensed as LGPL - See doc/COPYING for details */
4 #include "event_buffer.h"
5 #include "luaevent.h"
6 #include <lauxlib.h>
7 #include <malloc.h>
9 #define EVENT_BUFFER_MT "EVENT_BUFFER_MT"
11 #define BUFFER_ADD_CHECK_INPUT_FIRST 1
13 /* Obtains an le_buffer structure from a given index */
14 static le_buffer* event_buffer_get(lua_State* L, int idx) {
15 return (le_buffer*)luaL_checkudata(L, idx, EVENT_BUFFER_MT);
18 /* Obtains an le_buffer structure from a given index
19 AND checks that it hadn't been prematurely freed
21 le_buffer* event_buffer_check(lua_State* L, int idx) {
22 le_buffer* buf = (le_buffer*)luaL_checkudata(L, idx, EVENT_BUFFER_MT);
23 if(!buf->buffer)
24 luaL_argerror(L, idx, "Attempt to use closed event_buffer object");
25 return buf;
28 /* Checks if the given index contains an le_buffer object */
29 int is_event_buffer(lua_State* L, int idx) {
30 int ret;
31 lua_getmetatable(L, idx);
32 luaL_getmetatable(L, EVENT_BUFFER_MT);
33 ret = lua_rawequal(L, -2, -1);
34 lua_pop(L, 2);
35 return ret;
38 /* TODO: Use lightuserdata mapping to locate hanging object instances */
39 /* Pushes the specified evbuffer object onto the stack, attaching a metatable to it */
40 int event_buffer_push(lua_State* L, struct evbuffer* buffer) {
41 le_buffer *buf = (le_buffer*)lua_newuserdata(L, sizeof(le_buffer));
42 buf->buffer = buffer;
43 luaL_getmetatable(L, EVENT_BUFFER_MT);
44 lua_setmetatable(L, -2);
45 return 1;
48 /* LUA: new()
49 Pushes a new evbuffer instance on the stack
51 static int event_buffer_push_new(lua_State* L) {
52 return event_buffer_push(L, evbuffer_new());
55 /* LUA: __gc and buffer:close()
56 Releases the buffer resources
58 static int event_buffer_gc(lua_State* L) {
59 le_buffer* buf = event_buffer_get(L, 1);
60 if(buf->buffer) {
61 evbuffer_free(buf->buffer);
62 buf->buffer = NULL;
64 return 0;
67 /* LUA: buffer:add(...)
68 progressively adds items to the buffer
69 if arg[*] is string, treat as a string:format call
70 if arg[*] is a buffer, perform event_add_buffer
71 expects at least 1 other argument
72 returns number of bytes added
74 static int event_buffer_add(lua_State* L) {
75 le_buffer* buf = event_buffer_check(L, 1);
76 struct evbuffer* buffer = buf->buffer;
77 int oldLength = EVBUFFER_LENGTH(buffer);
78 int last = lua_gettop(L);
79 int i;
80 if(last == 1) luaL_error(L, "Not enough arguments to add: expects at least 1 additional operand");
81 for(i = 2; i <= last; i++) {
82 if(!lua_isstring(L, i) && !is_event_buffer(L, i))
83 luaL_argerror(L, i, "Argument is not a string or buffer object");
84 if(lua_equal(L, 1, i))
85 luaL_argerror(L, i, "Cannot add buffer to itself");
86 /* Optionally perform checks and data loading separately to avoid overfilling the buffer */
87 #if BUFFER_ADD_CHECK_INPUT_FIRST
89 for(i = 2; i <= last; i++) {
90 #endif
91 if(lua_isstring(L, i)) {
92 size_t len;
93 const char* data = lua_tolstring(L, i, &len);
94 if(0 != evbuffer_add(buffer, data, len))
95 luaL_error(L, "Failed to add data to the buffer");
96 } else {
97 le_buffer* buf2 = event_buffer_check(L, i);
98 if(0 != evbuffer_add_buffer(buffer, buf2->buffer))
99 luaL_error(L, "Failed to move buffer-data to the buffer");
102 lua_pushinteger(L, EVBUFFER_LENGTH(buffer) - oldLength);
103 return 1;
106 /* LUA: buffer:length()
107 Returns the length of the buffer contents
109 static int event_buffer_get_length(lua_State* L) {
110 le_buffer* buf = event_buffer_check(L, 1);
111 lua_pushinteger(L, EVBUFFER_LENGTH(buf->buffer));
112 return 1;
115 /* MAYBE: Could add caching */
116 /* LUA: buffer:get_data
117 () - Returns all data in buffer
118 (len) - Returns data up to 'len' bytes long
119 (begin,len) - Returns data beginning at 'begin' up to 'len' bytes long
120 If begin < 0, wraps at data length
121 ex: (-1, 1) returns last character
122 (-2,2) returns last 2 chars [length meaning does not get inverted]
124 static int event_buffer_get_data(lua_State* L) {
125 le_buffer* buf = event_buffer_check(L, 1);
126 int begin, len;
127 switch(lua_gettop(L)) {
128 case 1:
129 /* Obtain full data */
130 begin = 0;
131 len = EVBUFFER_LENGTH(buf->buffer);
132 break;
133 case 2:
134 begin = 0;
135 len = luaL_checkinteger(L, 2);
136 if(len > EVBUFFER_LENGTH(buf->buffer))
137 len = EVBUFFER_LENGTH(buf->buffer);
138 break;
139 case 3:
140 default:
141 /* - 1 to map it to Lua's 1-based indexing
142 * If begin < 0 add length to cause position wrapping
144 begin = luaL_checkinteger(L, 2);
145 if(begin < 0)
146 begin += EVBUFFER_LENGTH(buf->buffer);
147 else
148 begin--;
149 len = luaL_checkinteger(L, 3);
150 /* If length is less than zero, capture entire remaining string */
152 if(len < 0) len = EVBUFFER_LENGTH(buf->buffer);
153 if(begin > EVBUFFER_LENGTH(buf->buffer))
154 begin = EVBUFFER_LENGTH(buf->buffer);
155 if(begin + len > EVBUFFER_LENGTH(buf->buffer))
156 len = EVBUFFER_LENGTH(buf->buffer) - begin;
157 break;
159 lua_pushlstring(L, (const char*)EVBUFFER_DATA(buf->buffer) + begin, len);
160 return 1;
163 /* LUA: buffer:readline()
164 Returns a line terminated by either '\r\n','\n\r' or '\r' or '\n'
165 Returns nil and leaves data alone if no terminator is found
166 Newline is not present in the captured string.
168 static int event_buffer_readline(lua_State* L) {
169 le_buffer* buf = event_buffer_check(L, 1);
170 char* line = evbuffer_readline(buf->buffer);
171 if(!line)
172 return 0;
173 lua_pushstring(L, line);
174 free(line);
175 return 1;
178 /* LUA: buffer:drain(amt)
179 Drains 'amt' bytes from the buffer
180 If amt < 0, drains all data
181 (Due to auto-casting to unsigned int and automatic capping)
183 static int event_buffer_drain(lua_State* L) {
184 le_buffer* buf = event_buffer_check(L, 1);
185 size_t len = luaL_checkinteger(L, 2);
186 evbuffer_drain(buf->buffer, len);
187 return 0;
190 /* LUA: buffer:write
191 (integer/lightuserdata fd) - Attempts to write all the data out to the FD
192 (socket) - Attempts to write all the data out to the socket object
194 static int event_buffer_write(lua_State* L) {
195 le_buffer* buf = event_buffer_check(L, 1);
196 int ret;
197 if(lua_isnumber(L, 2)) {
198 ret = evbuffer_write(buf->buffer, lua_tointeger(L, 2));
199 } else if(lua_islightuserdata(L, 2)) {
200 ret = evbuffer_write(buf->buffer, (int)(long)lua_touserdata(L, 2));
201 } else if(lua_isuserdata(L, 2)) {
202 ret = evbuffer_write(buf->buffer, getSocketFd(L, 2));
203 } else {
204 luaL_argerror(L, 2, "Unexpected data type. Expects: integer/lightuserdata/socket");
206 lua_pushinteger(L, ret);
207 return 1;
210 /* LUA: buffer:read
211 (integer/lightuserdata fd, len) - Attempts to read up to 'len' out of the FD
212 (socket, len) - Attempts to read up to 'len' out of the socket object
214 static int event_buffer_read(lua_State* L) {
215 le_buffer* buf = event_buffer_check(L, 1);
216 int len = luaL_checkinteger(L, 3);
217 int ret;
218 if(lua_isnumber(L, 2)) {
219 ret = evbuffer_read(buf->buffer, lua_tointeger(L, 2), len);
220 } else if(lua_islightuserdata(L, 2)) {
221 ret = evbuffer_read(buf->buffer, (int)(long)lua_touserdata(L, 2), len);
222 } else if(lua_isuserdata(L, 2)) {
223 ret = evbuffer_read(buf->buffer, getSocketFd(L, 2), len);
224 } else {
225 luaL_argerror(L, 2, "Unexpected data type. Expects: integer/lightuserdata/socket");
227 lua_pushinteger(L, ret);
228 return 1;
230 static luaL_Reg buffer_funcs[] = {
231 {"add", event_buffer_add},
232 {"length", event_buffer_get_length},
233 {"get_data", event_buffer_get_data},
234 {"readline", event_buffer_readline},
235 {"drain", event_buffer_drain},
236 {"close", event_buffer_gc},
237 {"read", event_buffer_read},
238 {"write", event_buffer_write},
239 {NULL, NULL}
241 static luaL_Reg funcs[] = {
242 {"new", event_buffer_push_new},
243 {NULL, NULL}
246 int event_buffer_register(lua_State* L) {
247 luaL_newmetatable(L, EVENT_BUFFER_MT);
248 lua_pushcfunction(L, event_buffer_gc);
249 lua_setfield(L, -2, "__gc");
250 lua_pushcfunction(L, event_buffer_get_length);
251 lua_setfield(L, -2, "__len");
252 lua_pushcfunction(L, event_buffer_get_data);
253 lua_setfield(L, -2, "__tostring");
254 lua_newtable(L);
255 luaL_register(L, NULL, buffer_funcs);
256 lua_setfield(L, -2, "__index");
257 lua_pop(L, 1);
259 luaL_register(L, "luaevent.core.buffer", funcs);
260 return 0;