src/buffer.c

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
}