moonbridge
view moonbridge_io.c @ 85:ef552870f1b6
Implementation of read and read_nb in I/O module
author | jbe |
---|---|
date | Mon Apr 06 22:58:51 2015 +0200 (2015-04-06) |
parents | 8a6e2a80fcad |
children | 9fa3a36733ff |
line source
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <stdint.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <time.h>
9 #include <sys/time.h>
10 #include <sys/socket.h>
11 #include <sys/select.h>
12 #include <fcntl.h>
14 #include <lua.h>
15 #include <lauxlib.h>
16 #include <lualib.h>
18 #define MOONBR_IO_MAXSTRERRORLEN 80
19 #define MOONBR_IO_READBUFLEN 4096
20 #define MOONBR_IO_WRITEBUFLEN 4096
22 #define moonbr_io_errmsg() \
23 char errmsg[MOONBR_IO_MAXSTRERRORLEN]; \
24 strerror_r(errno, errmsg, MOONBR_IO_MAXSTRERRORLEN)
27 #define MOONBR_IO_HANDLE_MT_REGKEY "moonbridge_io_handle"
28 #define MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY "moonbridge_io_handle_public"
30 typedef struct {
31 int fd;
32 int issocket;
33 int nonblocking;
34 int readerr;
35 int readbufcnt;
36 int writeerr;
37 size_t writeleft;
38 #if LUA_VERSION_NUM >= 503
39 lua_Integer writeqin;
40 lua_Integer writeqout;
41 #else
42 int writeqin;
43 int writeqout;
44 #endif
45 size_t writeqoff;
46 int writebufcnt;
47 char readbuf[MOONBR_IO_READBUFLEN];
48 char writebuf[MOONBR_IO_WRITEBUFLEN];
49 } moonbr_io_handle_t;
51 static void moonbr_io_handle_set_nonblocking(lua_State *L, moonbr_io_handle_t *handle, int nonblocking) {
52 if (handle->nonblocking != nonblocking) {
53 int flags;
54 flags = fcntl(handle->fd, F_GETFL, 0);
55 if (flags == -1) {
56 moonbr_io_errmsg();
57 luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
58 }
59 if (nonblocking) flags |= O_NONBLOCK;
60 else flags &= ~O_NONBLOCK;
61 if (fcntl(handle->fd, F_SETFL, flags) == -1) {
62 moonbr_io_errmsg();
63 luaL_error(L, "Unexpected error in fcntl call: %s", errmsg);
64 }
65 }
66 }
68 static int moonbr_io_read_impl(lua_State *L, int nonblocking) {
69 moonbr_io_handle_t *handle;
70 lua_Integer maxread;
71 const char *terminatorstr;
72 size_t terminatorlen;
73 char terminator;
74 luaL_Buffer luabuf;
75 size_t luabufcnt = 0;
76 int restcnt;
77 char *terminatorpos;
78 ssize_t result;
79 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
80 maxread = luaL_optinteger(L, 2, 0);
81 terminatorstr = luaL_optlstring(L, 3, "", &terminatorlen);
82 if (terminatorlen) {
83 luaL_argcheck(L, terminatorlen == 1, 3, "single byte expected");
84 terminator = terminatorstr[0];
85 }
86 if (handle->fd < 0) luaL_error(L, "Attempt to read from a closed I/O handle");
87 if (handle->readerr) {
88 lua_pushnil(L);
89 lua_pushliteral(L, "Previous read error");
90 return 2;
91 }
92 moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
93 luaL_buffinit(L, &luabuf);
94 while (1) {
95 restcnt = -1;
96 if (maxread > 0 && handle->readbufcnt >= maxread - luabufcnt) {
97 restcnt = maxread - luabufcnt;
98 } else if (terminatorlen) {
99 terminatorpos = memchr(handle->readbuf, terminator, handle->readbufcnt);
100 if (terminatorpos) restcnt = 1 + (terminatorpos - handle->readbuf);
101 }
102 if (restcnt >= 0) {
103 luaL_addlstring(&luabuf, handle->readbuf, restcnt);
104 handle->readbufcnt -= restcnt;
105 memmove(handle->readbuf, handle->readbuf + restcnt, handle->readbufcnt);
106 luaL_pushresult(&luabuf);
107 return 1;
108 }
109 luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt);
110 luabufcnt += handle->readbufcnt;
111 handle->readbufcnt = 0;
112 do {
113 result = read(handle->fd, handle->readbuf, MOONBR_IO_READBUFLEN);
114 } while (result < 0 && (errno == EINTR));
115 if (result == 0 || (nonblocking && result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) break;
116 if (result < 0) {
117 moonbr_io_errmsg();
118 handle->readerr = 1;
119 lua_pushnil(L);
120 lua_pushstring(L, errmsg);
121 return 2;
122 }
123 handle->readbufcnt += result;
124 }
125 luaL_addlstring(&luabuf, handle->readbuf, handle->readbufcnt);
126 handle->readbufcnt = 0;
127 luaL_pushresult(&luabuf);
128 return 1;
129 }
131 static int moonbr_io_read(lua_State *L) {
132 return moonbr_io_read_impl(L, 0);
133 }
135 static int moonbr_io_read_nb(lua_State *L) {
136 return moonbr_io_read_impl(L, 1);
137 }
139 static int moonbr_io_write_impl(lua_State *L, int nonblocking, int flush) {
140 moonbr_io_handle_t *handle;
141 int i, top;
142 const char *str;
143 size_t strlen, strpos;
144 size_t written;
145 ssize_t result;
146 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
147 if (handle->fd < 0) luaL_error(L, "Attempt to write to a closed I/O handle");
148 if (handle->writeerr) {
149 lua_pushnil(L);
150 lua_pushliteral(L, "Previous write error");
151 return 2;
152 }
153 moonbr_io_handle_set_nonblocking(L, handle, nonblocking);
154 top = lua_gettop(L);
155 lua_getuservalue(L, 1);
156 lua_getfield(L, -1, "writebuf");
157 for (i=2; i<=top; i++) {
158 luaL_checklstring(L, i, &strlen);
159 lua_pushvalue(L, i);
160 lua_rawseti(L, -2, handle->writeqin++);
161 handle->writeleft += strlen;
162 }
163 while (handle->writeqout != handle->writeqin) {
164 lua_rawgeti(L, -1, handle->writeqout);
165 str = lua_tolstring(L, -1, &strlen);
166 strpos = handle->writeqoff;
167 while (strpos < strlen) {
168 if (strlen - strpos < MOONBR_IO_WRITEBUFLEN - handle->writebufcnt) {
169 memcpy(handle->writebuf + handle->writebufcnt, str + strpos, strlen - strpos);
170 handle->writebufcnt += strlen - strpos;
171 break;
172 } else {
173 written = 0;
174 memcpy(handle->writebuf + handle->writebufcnt, str + strpos, MOONBR_IO_WRITEBUFLEN - handle->writebufcnt);
175 strpos += MOONBR_IO_WRITEBUFLEN - handle->writebufcnt;
176 while (written < MOONBR_IO_WRITEBUFLEN) {
177 result = write(handle->fd, handle->writebuf + written, MOONBR_IO_WRITEBUFLEN - written);
178 if (result < 0) {
179 if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
180 if (written) {
181 handle->writebufcnt -= written;
182 memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt);
183 }
184 handle->writeqoff = strpos;
185 goto moonbr_io_write_impl_block;
186 } else if (errno != EINTR) {
187 moonbr_io_errmsg();
188 handle->writeerr = 1;
189 lua_pushnil(L);
190 lua_pushstring(L, errmsg);
191 return 2;
192 }
193 } else {
194 written += result;
195 handle->writeleft -= result;
196 }
197 }
198 handle->writebufcnt = 0;
199 }
200 }
201 handle->writeqoff = 0;
202 lua_pop(L, 1);
203 lua_pushnil(L);
204 lua_rawseti(L, -2, handle->writeqout++);
205 }
206 if (flush) {
207 written = 0;
208 while (written < handle->writebufcnt) {
209 result = write(handle->fd, handle->writebuf + written, handle->writebufcnt - written);
210 if (result < 0) {
211 if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
212 if (written) {
213 handle->writebufcnt -= written;
214 memmove(handle->writebuf, handle->writebuf + written, handle->writebufcnt);
215 }
216 goto moonbr_io_write_impl_block;
217 } else if (errno != EINTR) {
218 moonbr_io_errmsg();
219 handle->writeerr = -1;
220 lua_pushnil(L);
221 lua_pushstring(L, errmsg);
222 return 2;
223 }
224 } else {
225 written += result;
226 handle->writeleft -= result;
227 }
228 }
229 handle->writebufcnt = 0;
230 if (nonblocking) lua_pushinteger(L, 0);
231 } else {
232 if (nonblocking) lua_pushinteger(L, handle->writeleft - handle->writebufcnt);
233 }
234 if (!nonblocking) lua_pushvalue(L, 1);
235 return 1;
236 moonbr_io_write_impl_block:
237 if (flush) lua_pushinteger(L, handle->writeleft);
238 else lua_pushinteger(L, handle->writeleft - handle->writebufcnt);
239 return 1;
240 }
242 static int moonbr_io_write(lua_State *L) {
243 return moonbr_io_write_impl(L, 0, 0);
244 }
246 static int moonbr_io_write_nb(lua_State *L) {
247 return moonbr_io_write_impl(L, 1, 0);
248 }
250 static int moonbr_io_flush(lua_State *L) {
251 return moonbr_io_write_impl(L, 0, 1);
252 }
254 static int moonbr_io_flush_nb(lua_State *L) {
255 return moonbr_io_write_impl(L, 1, 1);
256 }
258 static int moonbr_io_close_impl(lua_State *L, int clean) {
259 moonbr_io_handle_t *handle;
260 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
261 if (handle->fd < 0) luaL_error(L, "Attempt to close a closed I/O handle");
262 if (clean && handle->writeleft) {
263 lua_pushcfunction(L, moonbr_io_flush);
264 lua_pushvalue(L, 1);
265 lua_call(L, 1, 2);
266 if (!lua_toboolean(L, -2)) {
267 close(handle->fd);
268 handle->fd = -1;
269 return 2;
270 }
271 }
272 if (close(handle->fd)) {
273 moonbr_io_errmsg();
274 handle->fd = -1;
275 lua_pushnil(L);
276 lua_pushstring(L, errmsg);
277 return 2;
278 }
279 handle->fd = -1;
280 lua_pushboolean(L, 1);
281 return 1;
283 }
285 static int moonbr_io_reset(lua_State *L) {
286 return moonbr_io_close_impl(L, 0);
287 }
289 static int moonbr_io_close(lua_State *L) {
290 return moonbr_io_close_impl(L, 1);
291 }
293 void moonbr_io_pushhandle(lua_State *L, int fd, int issocket) {
294 moonbr_io_handle_t *handle;
295 handle = lua_newuserdata(L, sizeof(moonbr_io_handle_t));
296 handle->fd = fd;
297 handle->issocket = issocket;
298 handle->nonblocking = -1;
299 handle->readerr = 0;
300 handle->readbufcnt = 0;
301 handle->writeerr = 0;
302 handle->writeleft = 0;
303 handle->writeqin = 0;
304 handle->writeqout = 0;
305 handle->writeqoff = 0;
306 handle->writebufcnt = 0;
307 luaL_getmetatable(L, MOONBR_IO_HANDLE_MT_REGKEY);
308 lua_setmetatable(L, -2);
309 lua_newtable(L); // uservalue
310 lua_newtable(L);
311 lua_setfield(L, -2, "writebuf");
312 lua_newtable(L); // public
313 luaL_getmetatable(L, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
314 lua_setmetatable(L, -2);
315 lua_setfield(L, -2, "public");
316 lua_setuservalue(L, -2);
317 }
319 static int moonbr_io_handleindex(lua_State *L) {
320 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
321 lua_getuservalue(L, 1);
322 lua_getfield(L, -1, "public");
323 lua_pushvalue(L, 2);
324 lua_gettable(L, -2);
325 return 1;
326 }
328 static int moonbr_io_handlenewindex(lua_State *L) {
329 luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
330 lua_getuservalue(L, 1);
331 lua_getfield(L, -1, "public");
332 lua_pushvalue(L, 2);
333 lua_pushvalue(L, 3);
334 lua_settable(L, -3);
335 return 0;
336 }
338 static int moonbr_io_handlegc(lua_State *L) {
339 moonbr_io_handle_t *handle;
340 handle = luaL_checkudata(L, 1, MOONBR_IO_HANDLE_MT_REGKEY);
341 if (handle->fd >= 0) {
342 lua_getuservalue(L, 1);
343 lua_getfield(L, -1, "gc");
344 lua_pushvalue(L, 1);
345 lua_call(L, 1, 0);
346 }
347 return 0;
348 }
350 static int moonbr_io_getdummy(lua_State *L) {
351 moonbr_io_pushhandle(L, 0, 0);
352 return 1;
353 }
355 static const struct luaL_Reg moonbr_io_handle_methods[] = {
356 {"read", moonbr_io_read},
357 {"read_nb", moonbr_io_read_nb},
358 {"write", moonbr_io_write},
359 {"write_nb", moonbr_io_write_nb},
360 {"flush", moonbr_io_flush},
361 {"flush_nb", moonbr_io_flush_nb},
362 {"reset", moonbr_io_reset},
363 {"close", moonbr_io_close},
364 {NULL, NULL}
365 };
367 static const struct luaL_Reg moonbr_io_handle_metamethods[] = {
368 {"__index", moonbr_io_handleindex},
369 {"__newindex", moonbr_io_handlenewindex},
370 {"__gc", moonbr_io_handlegc},
371 {NULL, NULL}
372 };
374 static const struct luaL_Reg moonbr_io_module_funcs[] = {
375 {"getdummy", moonbr_io_getdummy},
376 {NULL, NULL}
377 };
379 int luaopen_moonbridge_io(lua_State *L) {
381 lua_newtable(L); // module
383 lua_newtable(L); // public metatable
384 lua_newtable(L); // handle methods
385 luaL_setfuncs(L, moonbr_io_handle_methods, 0);
386 lua_pushvalue(L, -1);
387 lua_setfield(L, -4, "handle");
388 lua_setfield(L, -2, "__index");
389 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_PUBLIC_MT_REGKEY);
391 lua_newtable(L); // handle metatable
392 luaL_setfuncs(L, moonbr_io_handle_metamethods, 0);
393 lua_setfield(L, LUA_REGISTRYINDEX, MOONBR_IO_HANDLE_MT_REGKEY);
395 luaL_setfuncs(L, moonbr_io_module_funcs, 0);
396 return 1;
398 }