1 /*=========================================================================*\
2 * Input/Output interface for Lua programs
3 * LuaSocket toolkit
4 *
5 * RCS ID: $Id: buffer.c,v 1.28 2007/06/11 23:44:54 diego Exp $
6 \*=========================================================================*/
7 #include "lua.h"
8 #include "lauxlib.h"
9 
10 #include "buffer.h"
11 
12 /*=========================================================================*\
13 * Internal function prototypes
14 \*=========================================================================*/
15 static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);
16 static int recvline(p_buffer buf, luaL_Buffer *b);
17 static int recvall(p_buffer buf, luaL_Buffer *b);
18 static int buffer_get(p_buffer buf, const char **data, size_t *count);
19 static void buffer_skip(p_buffer buf, size_t count);
20 static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent);
21 
22 /* min and max macros */
23 #ifndef MIN
24 #define MIN(x, y) ((x) < (y) ? x : y)
25 #endif
26 #ifndef MAX
27 #define MAX(x, y) ((x) > (y) ? x : y)
28 #endif
29 
30 /*=========================================================================*\
31 * Exported functions
32 \*=========================================================================*/
33 /*-------------------------------------------------------------------------*\
34 * Initializes module
35 \*-------------------------------------------------------------------------*/
buffer_open(lua_State * L)36 int buffer_open(lua_State *L) {
37     (void) L;
38     return 0;
39 }
40 
41 /*-------------------------------------------------------------------------*\
42 * Initializes C structure
43 \*-------------------------------------------------------------------------*/
buffer_init(p_buffer buf,p_io io,p_timeout tm)44 void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
45 	buf->first = buf->last = 0;
46     buf->io = io;
47     buf->tm = tm;
48     buf->received = buf->sent = 0;
49     buf->birthday = timeout_gettime();
50 }
51 
52 /*-------------------------------------------------------------------------*\
53 * object:getstats() interface
54 \*-------------------------------------------------------------------------*/
buffer_meth_getstats(lua_State * L,p_buffer buf)55 int buffer_meth_getstats(lua_State *L, p_buffer buf) {
56     lua_pushnumber(L, buf->received);
57     lua_pushnumber(L, buf->sent);
58     lua_pushnumber(L, timeout_gettime() - buf->birthday);
59     return 3;
60 }
61 
62 /*-------------------------------------------------------------------------*\
63 * object:setstats() interface
64 \*-------------------------------------------------------------------------*/
buffer_meth_setstats(lua_State * L,p_buffer buf)65 int buffer_meth_setstats(lua_State *L, p_buffer buf) {
66     buf->received = (long) luaL_optnumber(L, 2, buf->received);
67     buf->sent = (long) luaL_optnumber(L, 3, buf->sent);
68     if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);
69     lua_pushnumber(L, 1);
70     return 1;
71 }
72 
73 /*-------------------------------------------------------------------------*\
74 * object:send() interface
75 \*-------------------------------------------------------------------------*/
buffer_meth_send(lua_State * L,p_buffer buf)76 int buffer_meth_send(lua_State *L, p_buffer buf) {
77     int top = lua_gettop(L);
78     int err = IO_DONE;
79     size_t size = 0, sent = 0;
80     const char *data = luaL_checklstring(L, 2, &size);
81     long start = (long) luaL_optnumber(L, 3, 1);
82     long end = (long) luaL_optnumber(L, 4, -1);
83     p_timeout tm = timeout_markstart(buf->tm);
84     if (start < 0) start = (long) (size+start+1);
85     if (end < 0) end = (long) (size+end+1);
86     if (start < 1) start = (long) 1;
87     if (end > (long) size) end = (long) size;
88     if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent);
89     /* check if there was an error */
90     if (err != IO_DONE) {
91         lua_pushnil(L);
92         lua_pushstring(L, buf->io->error(buf->io->ctx, err));
93         lua_pushnumber(L, sent+start-1);
94     } else {
95         lua_pushnumber(L, sent+start-1);
96         lua_pushnil(L);
97         lua_pushnil(L);
98     }
99 #ifdef LUASOCKET_DEBUG
100     /* push time elapsed during operation as the last return value */
101     lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
102 #endif
103     return lua_gettop(L) - top;
104 }
105 
106 /*-------------------------------------------------------------------------*\
107 * object:receive() interface
108 \*-------------------------------------------------------------------------*/
buffer_meth_receive(lua_State * L,p_buffer buf)109 int buffer_meth_receive(lua_State *L, p_buffer buf) {
110     int err = IO_DONE, top = lua_gettop(L);
111     luaL_Buffer b;
112     size_t size;
113     const char *part = luaL_optlstring(L, 3, "", &size);
114     p_timeout tm = timeout_markstart(buf->tm);
115     /* initialize buffer with optional extra prefix
116      * (useful for concatenating previous partial results) */
117     luaL_buffinit(L, &b);
118     luaL_addlstring(&b, part, size);
119     /* receive new patterns */
120     if (!lua_isnumber(L, 2)) {
121         const char *p= luaL_optstring(L, 2, "*l");
122         if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
123         else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
124         else luaL_argcheck(L, 0, 2, "invalid receive pattern");
125         /* get a fixed number of bytes (minus what was already partially
126          * received) */
127     } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b);
128     /* check if there was an error */
129     if (err != IO_DONE) {
130         /* we can't push anyting in the stack before pushing the
131          * contents of the buffer. this is the reason for the complication */
132         luaL_pushresult(&b);
133         lua_pushstring(L, buf->io->error(buf->io->ctx, err));
134         lua_pushvalue(L, -2);
135         lua_pushnil(L);
136         lua_replace(L, -4);
137     } else {
138         luaL_pushresult(&b);
139         lua_pushnil(L);
140         lua_pushnil(L);
141     }
142 #ifdef LUASOCKET_DEBUG
143     /* push time elapsed during operation as the last return value */
144     lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
145 #endif
146     return lua_gettop(L) - top;
147 }
148 
149 /*-------------------------------------------------------------------------*\
150 * Determines if there is any data in the read buffer
151 \*-------------------------------------------------------------------------*/
buffer_isempty(p_buffer buf)152 int buffer_isempty(p_buffer buf) {
153     return buf->first >= buf->last;
154 }
155 
156 /*=========================================================================*\
157 * Internal functions
158 \*=========================================================================*/
159 /*-------------------------------------------------------------------------*\
160 * Sends a block of data (unbuffered)
161 \*-------------------------------------------------------------------------*/
162 #define STEPSIZE 8192
sendraw(p_buffer buf,const char * data,size_t count,size_t * sent)163 static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {
164     p_io io = buf->io;
165     p_timeout tm = buf->tm;
166     size_t total = 0;
167     int err = IO_DONE;
168     while (total < count && err == IO_DONE) {
169         size_t done;
170         size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE;
171         err = io->send(io->ctx, data+total, step, &done, tm);
172         total += done;
173     }
174     *sent = total;
175     buf->sent += total;
176     return err;
177 }
178 
179 /*-------------------------------------------------------------------------*\
180 * Reads a fixed number of bytes (buffered)
181 \*-------------------------------------------------------------------------*/
recvraw(p_buffer buf,size_t wanted,luaL_Buffer * b)182 static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
183     int err = IO_DONE;
184     size_t total = 0;
185     while (err == IO_DONE) {
186         size_t count; const char *data;
187         err = buffer_get(buf, &data, &count);
188         count = MIN(count, wanted - total);
189         luaL_addlstring(b, data, count);
190         buffer_skip(buf, count);
191         total += count;
192         if (total >= wanted) break;
193     }
194     return err;
195 }
196 
197 /*-------------------------------------------------------------------------*\
198 * Reads everything until the connection is closed (buffered)
199 \*-------------------------------------------------------------------------*/
recvall(p_buffer buf,luaL_Buffer * b)200 static int recvall(p_buffer buf, luaL_Buffer *b) {
201     int err = IO_DONE;
202     size_t total = 0;
203     while (err == IO_DONE) {
204         const char *data; size_t count;
205         err = buffer_get(buf, &data, &count);
206         total += count;
207         luaL_addlstring(b, data, count);
208         buffer_skip(buf, count);
209     }
210     if (err == IO_CLOSED) {
211         if (total > 0) return IO_DONE;
212         else return IO_CLOSED;
213     } else return err;
214 }
215 
216 /*-------------------------------------------------------------------------*\
217 * Reads a line terminated by a CR LF pair or just by a LF. The CR and LF
218 * are not returned by the function and are discarded from the buffer
219 \*-------------------------------------------------------------------------*/
recvline(p_buffer buf,luaL_Buffer * b)220 static int recvline(p_buffer buf, luaL_Buffer *b) {
221     int err = IO_DONE;
222     while (err == IO_DONE) {
223         size_t count, pos; const char *data;
224         err = buffer_get(buf, &data, &count);
225         pos = 0;
226         while (pos < count && data[pos] != '\n') {
227             /* we ignore all \r's */
228             if (data[pos] != '\r') luaL_putchar(b, data[pos]);
229             pos++;
230         }
231         if (pos < count) { /* found '\n' */
232             buffer_skip(buf, pos+1); /* skip '\n' too */
233             break; /* we are done */
234         } else /* reached the end of the buffer */
235             buffer_skip(buf, pos);
236     }
237     return err;
238 }
239 
240 /*-------------------------------------------------------------------------*\
241 * Skips a given number of bytes from read buffer. No data is read from the
242 * transport layer
243 \*-------------------------------------------------------------------------*/
buffer_skip(p_buffer buf,size_t count)244 static void buffer_skip(p_buffer buf, size_t count) {
245     buf->received += count;
246     buf->first += count;
247     if (buffer_isempty(buf))
248         buf->first = buf->last = 0;
249 }
250 
251 /*-------------------------------------------------------------------------*\
252 * Return any data available in buffer, or get more data from transport layer
253 * if buffer is empty
254 \*-------------------------------------------------------------------------*/
buffer_get(p_buffer buf,const char ** data,size_t * count)255 static int buffer_get(p_buffer buf, const char **data, size_t *count) {
256     int err = IO_DONE;
257     p_io io = buf->io;
258     p_timeout tm = buf->tm;
259     if (buffer_isempty(buf)) {
260         size_t got;
261         err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);
262         buf->first = 0;
263         buf->last = got;
264     }
265     *count = buf->last - buf->first;
266     *data = buf->data + buf->first;
267     return err;
268 }
269