]>
jfr.im git - solanum.git/blob - librb/src/linebuf.c
2 * ircd-ratbox: A slightly useful ircd.
3 * linebuf.c: Maintains linebuffers.
5 * Copyright (C) 2001-2002 Adrian Chadd <adrian@creative.net.au>
6 * Copyright (C) 2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
26 #include <librb_config.h>
28 #include <commio-int.h>
30 static rb_bh
*rb_linebuf_heap
;
32 static int bufline_count
= 0;
37 * Initialise the linebuf mechanism
41 rb_linebuf_init(size_t heap_size
)
43 rb_linebuf_heap
= rb_bh_create(sizeof(buf_line_t
), heap_size
, "librb_linebuf_heap");
47 rb_linebuf_allocate(void)
50 t
= rb_bh_alloc(rb_linebuf_heap
);
56 rb_linebuf_free(buf_line_t
* p
)
58 rb_bh_free(rb_linebuf_heap
, p
);
64 * Create a new line, and link it to the given linebuf.
65 * It will be initially empty.
68 rb_linebuf_new_line(buf_head_t
* bufhead
)
72 bufline
= rb_linebuf_allocate();
77 /* Stick it at the end of the buf list */
78 rb_dlinkAddTailAlloc(bufline
, &bufhead
->list
);
81 /* And finally, update the allocated size */
90 * rb_linebuf_done_line
92 * We've finished with the given line, so deallocate it
95 rb_linebuf_done_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, rb_dlink_node
*node
)
97 /* Remove it from the linked list */
98 rb_dlinkDestroy(node
, &bufhead
->list
);
100 /* Update the allocated size */
102 bufhead
->len
-= bufline
->len
;
103 lrb_assert(bufhead
->len
>= 0);
107 lrb_assert(bufline
->refcount
>= 0);
109 if(bufline
->refcount
== 0)
111 /* and finally, deallocate the buf */
113 lrb_assert(bufline_count
>= 0);
114 rb_linebuf_free(bufline
);
120 * skip to end of line or the crlfs, return the number of bytes ..
123 rb_linebuf_skip_crlf(char *ch
, int len
)
127 /* First, skip until the first non-CRLF */
128 for(; len
; len
--, ch
++)
136 /* Then, skip until the last CRLF */
137 for(; len
; len
--, ch
++)
139 if((*ch
!= '\r') && (*ch
!= '\n'))
142 lrb_assert(orig_len
> len
);
143 return (orig_len
- len
);
151 * Initialise the new buffer
154 rb_linebuf_newbuf(buf_head_t
* bufhead
)
156 /* not much to do right now :) */
157 memset(bufhead
, 0, sizeof(buf_head_t
));
163 * Flush all the lines associated with this buffer
166 rb_linebuf_donebuf(buf_head_t
* bufhead
)
168 while(bufhead
->list
.head
!= NULL
)
170 rb_linebuf_done_line(bufhead
, (buf_line_t
*) bufhead
->list
.head
->data
,
176 * rb_linebuf_copy_line
178 * Okay..this functions comments made absolutely no sense.
180 * Basically what we do is this. Find the first chunk of text
181 * and then scan for a CRLF. If we didn't find it, but we didn't
182 * overflow our buffer..we wait for some more data.
183 * If we found a CRLF, we replace them with a \0 character.
184 * If we overflowed, we copy the most our buffer can handle, terminate
185 * it with a \0 and return.
187 * The return value is the amount of data we consumed. This could
188 * be different than the size of the linebuffer, as when we discard
189 * the overflow, we don't want to process it again.
191 * This still sucks in my opinion, but it seems to work.
196 rb_linebuf_copy_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
198 int cpylen
= 0; /* how many bytes we've copied */
199 char *ch
= data
; /* Pointer to where we are in the read data */
200 char *bufch
= bufline
->buf
+ bufline
->len
;
201 int clen
= 0; /* how many bytes we've processed,
202 and don't ever want to see again.. */
204 /* If its full or terminated, ignore it */
207 lrb_assert(bufline
->len
<= LINEBUF_SIZE
);
208 if(bufline
->terminated
== 1)
211 clen
= cpylen
= rb_linebuf_skip_crlf(ch
, len
);
215 /* This is the ~overflow case..This doesn't happen often.. */
216 if(cpylen
> (LINEBUF_SIZE
- bufline
->len
))
218 cpylen
= LINEBUF_SIZE
- bufline
->len
;
219 memcpy(bufch
, ch
, cpylen
);
220 bufline
->buf
[LINEBUF_SIZE
] = '\0';
221 bufch
= bufline
->buf
+ LINEBUF_SIZE
- 1;
222 while(cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
228 bufline
->terminated
= 1;
229 bufline
->len
= LINEBUF_SIZE
;
230 bufhead
->len
+= LINEBUF_SIZE
;
234 memcpy(bufch
, ch
, cpylen
);
239 if(*bufch
!= '\r' && *bufch
!= '\n')
241 /* No linefeed, bail for the next time */
242 bufhead
->len
+= cpylen
;
243 bufline
->len
+= cpylen
;
244 bufline
->terminated
= 0;
248 /* Yank the CRLF off this, replace with a \0 */
249 while(cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
256 bufline
->terminated
= 1;
257 bufhead
->len
+= cpylen
;
258 bufline
->len
+= cpylen
;
263 * rb_linebuf_copy_raw
265 * Copy as much data as possible directly into a linebuf,
266 * splitting at \r\n, but without altering any data.
270 rb_linebuf_copy_raw(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
272 int cpylen
= 0; /* how many bytes we've copied */
273 char *ch
= data
; /* Pointer to where we are in the read data */
274 char *bufch
= bufline
->buf
+ bufline
->len
;
275 int clen
= 0; /* how many bytes we've processed,
276 and don't ever want to see again.. */
278 /* If its full or terminated, ignore it */
281 lrb_assert(bufline
->len
<= LINEBUF_SIZE
);
282 if(bufline
->terminated
== 1)
285 clen
= cpylen
= rb_linebuf_skip_crlf(ch
, len
);
289 /* This is the overflow case..This doesn't happen often.. */
290 if(cpylen
> (LINEBUF_SIZE
- bufline
->len
))
292 clen
= LINEBUF_SIZE
- bufline
->len
;
293 memcpy(bufch
, ch
, clen
);
294 bufline
->buf
[LINEBUF_SIZE
] = '\0';
295 bufline
->terminated
= 1;
296 bufline
->len
= LINEBUF_SIZE
;
297 bufhead
->len
+= LINEBUF_SIZE
;
301 memcpy(bufch
, ch
, cpylen
);
306 if(*bufch
!= '\r' && *bufch
!= '\n')
308 /* No linefeed, bail for the next time */
309 bufhead
->len
+= cpylen
;
310 bufline
->len
+= cpylen
;
311 bufline
->terminated
= 0;
315 bufline
->terminated
= 1;
316 bufhead
->len
+= cpylen
;
317 bufline
->len
+= cpylen
;
325 * Take a given buffer and break out as many buffers as we can.
326 * If we find a CRLF, we terminate that buffer and create a new one.
327 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
328 * 'finished', so the next loop through we can continue appending ..
330 * A few notes here, which you'll need to understand before continuing.
332 * - right now I'm only dealing with single sized buffers. Later on,
333 * I might consider chaining buffers together to get longer "lines"
334 * but seriously, I don't see the advantage right now.
336 * - This *is* designed to turn into a reference-counter-protected setup
337 * to dodge copious copies.
340 rb_linebuf_parse(buf_head_t
* bufhead
, char *data
, int len
, int raw
)
346 /* First, if we have a partial buffer, try to squeze data into it */
347 if(bufhead
->list
.tail
!= NULL
)
349 /* Check we're doing the partial buffer thing */
350 bufline
= bufhead
->list
.tail
->data
;
351 /* just try, the worst it could do is *reject* us .. */
353 cpylen
= rb_linebuf_copy_line(bufhead
, bufline
, data
, len
);
355 cpylen
= rb_linebuf_copy_raw(bufhead
, bufline
, data
, len
);
361 /* If we've copied the same as what we've got, quit now */
363 return linecnt
; /* all the data done so soon? */
365 /* Skip the data and update len .. */
367 lrb_assert(len
>= 0);
374 /* We obviously need a new buffer, so .. */
375 bufline
= rb_linebuf_new_line(bufhead
);
379 cpylen
= rb_linebuf_copy_line(bufhead
, bufline
, data
, len
);
381 cpylen
= rb_linebuf_copy_raw(bufhead
, bufline
, data
, len
);
387 lrb_assert(len
>= 0);
398 * get the next buffer from our line. For the time being it will copy
399 * data into the given buffer and free the underlying linebuf.
402 rb_linebuf_get(buf_head_t
* bufhead
, char *buf
, int buflen
, int partial
, int raw
)
408 /* make sure we have a line */
409 if(bufhead
->list
.head
== NULL
)
410 return 0; /* Obviously not.. hrm. */
412 bufline
= bufhead
->list
.head
->data
;
414 /* make sure that the buffer was actually *terminated */
415 if(!(partial
|| bufline
->terminated
))
416 return 0; /* Wait for more data! */
418 if(buflen
< bufline
->len
)
421 cpylen
= bufline
->len
;
424 start
= bufline
->buf
;
426 /* if we left extraneous '\r\n' characters in the string,
427 * and we don't want to read the raw data, clean up the string.
429 if(bufline
->raw
&& !raw
)
431 /* skip leading EOL characters */
432 while(cpylen
&& (*start
== '\r' || *start
== '\n'))
437 /* skip trailing EOL characters */
438 ch
= &start
[cpylen
- 1];
439 while(cpylen
&& (*ch
== '\r' || *ch
== '\n'))
446 memcpy(buf
, start
, cpylen
);
448 /* convert CR/LF to NULL */
452 lrb_assert(cpylen
>= 0);
454 /* Deallocate the line */
455 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
457 /* return how much we copied */
464 * attach the lines in a buf_head_t to another buf_head_t
465 * without copying the data (using refcounts).
468 rb_linebuf_attach(buf_head_t
* bufhead
, buf_head_t
* new)
473 RB_DLINK_FOREACH(ptr
, new->list
.head
)
476 rb_dlinkAddTailAlloc(line
, &bufhead
->list
);
478 /* Update the allocated size */
480 bufhead
->len
+= line
->len
;
490 * linked list of strings are appended in order.
493 rb_linebuf_put(buf_head_t
*bufhead
, const rb_strf_t
*strings
)
499 /* make sure the previous line is terminated */
500 if (bufhead
->list
.tail
) {
501 bufline
= bufhead
->list
.tail
->data
;
502 lrb_assert(bufline
->terminated
);
505 /* create a new line */
506 bufline
= rb_linebuf_new_line(bufhead
);
508 ret
= rb_fsnprint(bufline
->buf
, LINEBUF_SIZE
+ 1, strings
);
512 if (len
> LINEBUF_SIZE
)
515 /* add trailing CRLF */
516 bufline
->buf
[len
++] = '\r';
517 bufline
->buf
[len
++] = '\n';
518 bufline
->buf
[len
] = '\0';
520 bufline
->terminated
= 1;
529 * Flush data to the buffer. It tries to write as much data as possible
530 * to the given socket. Any return values are passed straight through.
531 * If there is no data in the socket, EWOULDBLOCK is set as an errno
532 * rather than returning 0 (which would map to an EOF..)
534 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
535 * and tag it so that we don't re-schedule another write until
539 rb_linebuf_flush(rb_fde_t
*F
, buf_head_t
* bufhead
)
545 * autoconf checks for this..but really just want to use it if we have a
546 * native version even if libircd provides a fake version...
554 static struct rb_iovec vec
[RB_UIO_MAXIOV
];
556 memset(vec
, 0, sizeof(vec
));
557 /* Check we actually have a first buffer */
558 if(bufhead
->list
.head
== NULL
)
560 /* nope, so we return none .. */
565 ptr
= bufhead
->list
.head
;
568 if(!bufline
->terminated
)
575 vec
[x
].iov_base
= bufline
->buf
+ bufhead
->writeofs
;
576 vec
[x
++].iov_len
= bufline
->len
- bufhead
->writeofs
;
585 if(!bufline
->terminated
)
588 vec
[x
].iov_base
= bufline
->buf
;
589 vec
[x
].iov_len
= bufline
->len
;
593 while(++x
< RB_UIO_MAXIOV
);
601 xret
= retval
= rb_writev(F
, vec
, x
);
605 ptr
= bufhead
->list
.head
;
607 for(y
= 0; y
< x
; y
++)
611 if(xret
>= bufline
->len
- bufhead
->writeofs
)
613 xret
-= bufline
->len
- bufhead
->writeofs
;
615 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
616 bufhead
->writeofs
= 0;
620 bufhead
->writeofs
+= xret
;
629 /* this is the non-writev case */
631 /* Check we actually have a first buffer */
632 if(bufhead
->list
.head
== NULL
)
634 /* nope, so we return none .. */
639 bufline
= bufhead
->list
.head
->data
;
641 /* And that its actually full .. */
642 if(!bufline
->terminated
)
648 /* Now, try writing data */
649 retval
= rb_write(F
, bufline
->buf
+ bufhead
->writeofs
, bufline
->len
- bufhead
->writeofs
);
654 /* we've got data, so update the write offset */
655 bufhead
->writeofs
+= retval
;
657 /* if we've written everything *and* the CRLF, deallocate and update
659 if(bufhead
->writeofs
== bufline
->len
)
661 bufhead
->writeofs
= 0;
662 lrb_assert(bufhead
->len
>= 0);
663 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
666 /* Return line length */
673 * count linebufs for stats z
677 rb_count_rb_linebuf_memory(size_t *count
, size_t *rb_linebuf_memory_used
)
679 rb_bh_usage(rb_linebuf_heap
, count
, NULL
, rb_linebuf_memory_used
, NULL
);