lib: Added bufio I/O-less copy function.
[ashd.git] / lib / bufio.c
1 /*
2     ashd - A Sane HTTP Daemon
3     Copyright (C) 2008  Fredrik Tolf <fredrik@dolda2000.com>
4
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include <utils.h>
27 #include <bufio.h>
28
29 struct bufio *bioopen(void *pdata, struct bufioops *ops)
30 {
31     struct bufio *bio;
32     
33     omalloc(bio);
34     bio->pdata = pdata;
35     bio->ops = ops;
36     bio->bufhint = 4096;
37     return(bio);
38 }
39
40 int bioclose(struct bufio *bio)
41 {
42     int rv;
43     
44     bioflush(bio);
45     if(bio->ops->close)
46         rv = bio->ops->close(bio->pdata);
47     else
48         rv = 0;
49     buffree(bio->rbuf);
50     buffree(bio->wbuf);
51     free(bio);
52     return(rv);
53 }
54
55 size_t biordata(struct bufio *bio)
56 {
57     return(bio->rbuf.d - bio->rh);
58 }
59
60 size_t biorspace(struct bufio *bio)
61 {
62     if((bio->rbuf.d - bio->rh) >= bio->bufhint)
63         return(0);
64     return(bio->bufhint - (bio->rbuf.d - bio->rh));
65 }
66
67 int bioeof(struct bufio *bio)
68 {
69     return(bio->eof && (bio->rh >= bio->rbuf.d));
70 }
71
72 static ssize_t biofill(struct bufio *bio)
73 {
74     size_t ns;
75     ssize_t ret;
76     
77     if(!bio->ops->read) {
78         bio->eof = 1;
79         return(0);
80     }
81     if(bio->eof)
82         return(0);
83     if(bio->rh == bio->rbuf.d)
84         bio->rh = bio->rbuf.d = 0;
85     if(bio->rbuf.d == bio->rbuf.s) {
86         if(bio->rh > 0) {
87             memmove(bio->rbuf.b, bio->rbuf.b + bio->rh, bio->rbuf.d -= bio->rh);
88             bio->rh = 0;
89         } else {
90             if((ns = bio->rbuf.s * 2) < bio->bufhint)
91                 ns = bio->bufhint;
92             sizebuf(bio->rbuf, ns);
93         }
94     }
95     if((bio->rbuf.s > bio->bufhint) && (bio->rbuf.d < bio->bufhint))
96         bio->rbuf.b = srealloc(bio->rbuf.b, bio->rbuf.s = bio->bufhint);
97     ret = bio->ops->read(bio->pdata, bio->rbuf.b + bio->rbuf.d, bio->rbuf.s - bio->rbuf.d);
98     if(ret < 0) {
99         bio->err = errno;
100         return(-1);
101     } else if(ret == 0) {
102         bio->eof = 1;
103         return(0);
104     }
105     bio->rbuf.d += ret;
106     return(bio->rbuf.d - bio->rh);
107 }
108
109 ssize_t biorensure(struct bufio *bio, size_t bytes)
110 {
111     ssize_t ret;
112     
113     while(bio->rbuf.d - bio->rh < bytes) {
114         if((ret = biofill(bio)) <= 0)
115             return(ret);
116     }
117     return(bio->rbuf.d - bio->rh);
118 }
119
120 ssize_t biofillsome(struct bufio *bio)
121 {
122     return(biofill(bio));
123 }
124
125 int biogetc(struct bufio *bio)
126 {
127     ssize_t ret;
128     
129     while(bio->rbuf.d <= bio->rh) {
130         if((ret = biofill(bio)) <= 0)
131             return(EOF);
132     }
133     return((unsigned char)bio->rbuf.b[bio->rh++]);
134 }
135
136 ssize_t bioreadsome(struct bufio *bio, void *buf, size_t len)
137 {
138     ssize_t ret;
139     
140     if((bio->rh >= bio->rbuf.d) && ((ret = biofill(bio)) <= 0))
141         return(ret);
142     ret = min(len, bio->rbuf.d - bio->rh);
143     memcpy(buf, bio->rbuf.b + bio->rh, ret);
144     bio->rh += ret;
145     return(ret);
146 }
147
148 size_t biowdata(struct bufio *bio)
149 {
150     return(bio->wbuf.d - bio->wh);
151 }
152
153 size_t biowspace(struct bufio *bio)
154 {
155     if((bio->wbuf.d - bio->wh) >= bio->bufhint)
156         return(0);
157     return(bio->bufhint - (bio->wbuf.d - bio->wh));
158 }
159
160 int bioflush(struct bufio *bio)
161 {
162     ssize_t ret;
163     
164     while(bio->wh < bio->wbuf.d) {
165         ret = bio->ops->write(bio->pdata, bio->wbuf.b + bio->wh, bio->wbuf.d - bio->wh);
166         if(ret < 0) {
167             bio->err = errno;
168             return(-1);
169         }
170         bio->wh += ret;
171     }
172     return(0);
173 }
174
175 int bioflushsome(struct bufio *bio)
176 {
177     ssize_t ret;
178     
179     if(bio->wh < bio->wbuf.d) {
180         ret = bio->ops->write(bio->pdata, bio->wbuf.b + bio->wh, bio->wbuf.d - bio->wh);
181         if(ret < 0) {
182             bio->err = errno;
183             return(-1);
184         }
185         bio->wh += ret;
186         return(1);
187     } else {
188         return(0);
189     }
190 }
191
192 ssize_t biowensure(struct bufio *bio, size_t bytes)
193 {
194     if(bio->wbuf.s - bio->wbuf.d < bytes) {
195         if(!bio->ops->write) {
196             errno = bio->err = EPIPE;
197             return(-1);
198         }
199         if(bioflush(bio) < 0)
200             return(-1);
201         bio->wh = bio->wbuf.d = 0;
202         if((bio->wbuf.s > bio->bufhint) && (bytes <= bio->bufhint))
203             bio->wbuf.b = srealloc(bio->wbuf.b, bio->wbuf.s = bio->bufhint);
204         else
205             sizebuf(bio->wbuf, (bytes < bio->bufhint)?bio->bufhint:bytes);
206     }
207     return(0);
208 }
209
210 int bioputc(struct bufio *bio, int c)
211 {
212     if(biowensure(bio, 1) < 0)
213         return(-1);
214     bio->wbuf.b[bio->wbuf.d++] = c;
215     return(0);
216 }
217
218 ssize_t biowrite(struct bufio *bio, const void *data, size_t len)
219 {
220     ssize_t wb, ret;
221     
222     wb = 0;
223     while(len > 0) {
224         if(biowensure(bio, min(len, bio->bufhint)) < 0) {
225             if(wb > 0)
226                 return(wb);
227             return(-1);
228         }
229         if(len < bio->wbuf.s - bio->wbuf.d) {
230             memcpy(bio->wbuf.b + bio->wbuf.d, data, len);
231             bio->wbuf.d += len;
232             wb += len;
233             len = 0;
234         } else {
235             if(bioflush(bio) < 0) {
236                 if(wb > 0)
237                     return(wb);
238                 return(-1);
239             }
240             bio->wh = bio->wbuf.d = 0;
241             ret = bio->ops->write(bio->pdata, data, len);
242             if(ret < 0) {
243                 if(wb > 0)
244                     return(wb);
245                 bio->err = errno;
246                 return(-1);
247             }
248             data += ret; len -= ret; wb += ret;
249         }
250     }
251     return(wb);
252 }
253
254 ssize_t biowritesome(struct bufio *bio, const void *data, size_t len)
255 {
256     ssize_t ret;
257     
258     sizebuf(bio->wbuf, bio->bufhint);
259     if(bio->wh == bio->wbuf.d)
260         bio->wh = bio->wbuf.d = 0;
261     if(bio->wbuf.d == bio->wbuf.s) {
262         if(bio->wh > 0) {
263             memmove(bio->wbuf.b, bio->wbuf.b + bio->wh, bio->wbuf.d -= bio->wh);
264             bio->wh = 0;
265         }
266     }
267     ret = min(len, bio->wbuf.s - bio->wbuf.d);
268     memcpy(bio->wbuf.b + bio->wbuf.d, data, ret);
269     bio->wbuf.d += ret;
270     if(bioflushsome(bio) < 0) {
271         if(ret == 0)
272             return(-1);
273         if(ret < bio->wbuf.d - bio->wh) { /* Should never be false */
274             bio->wbuf.d -= ret;
275             return(-1);
276         }
277     }
278     return(ret);
279 }
280
281 int bioprintf(struct bufio *bio, const char *format, ...)
282 {
283     va_list args;
284     int ret;
285     
286     if(biowensure(bio, strlen(format)) < 0)
287         return(-1);
288     while(1) {
289         va_start(args, format);
290         ret = vsnprintf(bio->wbuf.b + bio->wbuf.d, bio->wbuf.s - bio->wbuf.d, format, args);
291         va_end(args);
292         if(ret <= bio->wbuf.s - bio->wbuf.d) {
293             bio->wbuf.d += ret;
294             return(0);
295         }
296         if(biowensure(bio, ret) < 0)
297             return(-1);
298     }
299 }
300
301 ssize_t biocopysome(struct bufio *dst, struct bufio *src)
302 {
303     ssize_t ret;
304     
305     if(src->rh >= src->rbuf.d)
306         return(0);
307     if((ret = biowritesome(dst, src->rbuf.b + src->rh, src->rbuf.d - src->rh)) < 0)
308         return(-1);
309     src->rh += ret;
310     return(ret);
311 }
312
313 ssize_t biocopybuf(struct bufio *dst, struct bufio *src)
314 {
315     ssize_t ret;
316     
317     sizebuf(dst->wbuf, dst->bufhint);
318     if(dst->wbuf.d == dst->wbuf.s) {
319         if(dst->wh > 0) {
320             memmove(dst->wbuf.b, dst->wbuf.b + dst->wh, dst->wbuf.d -= dst->wh);
321             dst->wh = 0;
322         }
323     }
324     ret = min(src->rbuf.d - src->rh, dst->wbuf.s - dst->wbuf.d);
325     memcpy(dst->wbuf.b + dst->wbuf.d, src->rbuf.b + src->rh, ret);
326     src->rh += ret;
327     dst->wbuf.d += ret;
328     return(ret);
329 }