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