]>
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
24 * $Id: linebuf.c 26092 2008-09-19 15:13:52Z androsyn $
27 #include <librb_config.h>
29 #include <commio-int.h>
31 static rb_bh
*rb_linebuf_heap
;
33 static int bufline_count
= 0;
35 #ifndef LINEBUF_HEAP_SIZE
36 #define LINEBUF_HEAP_SIZE 2048
42 * Initialise the linebuf mechanism
46 rb_linebuf_init(size_t heap_size
)
48 rb_linebuf_heap
= rb_bh_create(sizeof(buf_line_t
), heap_size
, "librb_linebuf_heap");
52 rb_linebuf_allocate(void)
55 t
= rb_bh_alloc(rb_linebuf_heap
);
61 rb_linebuf_free(buf_line_t
* p
)
63 rb_bh_free(rb_linebuf_heap
, p
);
69 * Create a new line, and link it to the given linebuf.
70 * It will be initially empty.
73 rb_linebuf_new_line(buf_head_t
* bufhead
)
78 bufline
= rb_linebuf_allocate();
84 node
= rb_make_rb_dlink_node();
86 /* Stick it at the end of the buf list */
87 rb_dlinkAddTail(bufline
, node
, &bufhead
->list
);
90 /* And finally, update the allocated size */
99 * rb_linebuf_done_line
101 * We've finished with the given line, so deallocate it
104 rb_linebuf_done_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, rb_dlink_node
*node
)
106 /* Remove it from the linked list */
107 rb_dlinkDestroy(node
, &bufhead
->list
);
109 /* Update the allocated size */
111 bufhead
->len
-= bufline
->len
;
112 lrb_assert(bufhead
->len
>= 0);
116 lrb_assert(bufline
->refcount
>= 0);
118 if(bufline
->refcount
== 0)
120 /* and finally, deallocate the buf */
122 lrb_assert(bufline_count
>= 0);
123 rb_linebuf_free(bufline
);
129 * skip to end of line or the crlfs, return the number of bytes ..
132 rb_linebuf_skip_crlf(char *ch
, int len
)
136 /* First, skip until the first non-CRLF */
137 for(; len
; len
--, ch
++)
145 /* Then, skip until the last CRLF */
146 for(; len
; len
--, ch
++)
148 if((*ch
!= '\r') && (*ch
!= '\n'))
151 lrb_assert(orig_len
> len
);
152 return (orig_len
- len
);
160 * Initialise the new buffer
163 rb_linebuf_newbuf(buf_head_t
* bufhead
)
165 /* not much to do right now :) */
166 memset(bufhead
, 0, sizeof(buf_head_t
));
172 * Flush all the lines associated with this buffer
175 rb_linebuf_donebuf(buf_head_t
* bufhead
)
177 while(bufhead
->list
.head
!= NULL
)
179 rb_linebuf_done_line(bufhead
, (buf_line_t
*) bufhead
->list
.head
->data
,
185 * rb_linebuf_copy_line
187 * Okay..this functions comments made absolutely no sense.
189 * Basically what we do is this. Find the first chunk of text
190 * and then scan for a CRLF. If we didn't find it, but we didn't
191 * overflow our buffer..we wait for some more data.
192 * If we found a CRLF, we replace them with a \0 character.
193 * If we overflowed, we copy the most our buffer can handle, terminate
194 * it with a \0 and return.
196 * The return value is the amount of data we consumed. This could
197 * be different than the size of the linebuffer, as when we discard
198 * the overflow, we don't want to process it again.
200 * This still sucks in my opinion, but it seems to work.
205 rb_linebuf_copy_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
207 int cpylen
= 0; /* how many bytes we've copied */
208 char *ch
= data
; /* Pointer to where we are in the read data */
209 char *bufch
= bufline
->buf
+ bufline
->len
;
210 int clen
= 0; /* how many bytes we've processed,
211 and don't ever want to see again.. */
213 /* If its full or terminated, ignore it */
216 lrb_assert(bufline
->len
< BUF_DATA_SIZE
);
217 if(bufline
->terminated
== 1)
220 clen
= cpylen
= rb_linebuf_skip_crlf(ch
, len
);
224 /* This is the ~overflow case..This doesn't happen often.. */
225 if(cpylen
> (BUF_DATA_SIZE
- bufline
->len
- 1))
227 cpylen
= BUF_DATA_SIZE
- bufline
->len
- 1;
228 memcpy(bufch
, ch
, cpylen
);
229 bufline
->buf
[BUF_DATA_SIZE
- 1] = '\0';
230 bufch
= bufline
->buf
+ BUF_DATA_SIZE
- 2;
231 while(cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
237 bufline
->terminated
= 1;
238 bufline
->len
= BUF_DATA_SIZE
- 1;
239 bufhead
->len
+= BUF_DATA_SIZE
- 1;
243 memcpy(bufch
, ch
, cpylen
);
248 if(*bufch
!= '\r' && *bufch
!= '\n')
250 /* No linefeed, bail for the next time */
251 bufhead
->len
+= cpylen
;
252 bufline
->len
+= cpylen
;
253 bufline
->terminated
= 0;
257 /* Yank the CRLF off this, replace with a \0 */
258 while(cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
265 bufline
->terminated
= 1;
266 bufhead
->len
+= cpylen
;
267 bufline
->len
+= cpylen
;
272 * rb_linebuf_copy_raw
274 * Copy as much data as possible directly into a linebuf,
275 * splitting at \r\n, but without altering any data.
279 rb_linebuf_copy_raw(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
281 int cpylen
= 0; /* how many bytes we've copied */
282 char *ch
= data
; /* Pointer to where we are in the read data */
283 char *bufch
= bufline
->buf
+ bufline
->len
;
284 int clen
= 0; /* how many bytes we've processed,
285 and don't ever want to see again.. */
287 /* If its full or terminated, ignore it */
290 lrb_assert(bufline
->len
< BUF_DATA_SIZE
);
291 if(bufline
->terminated
== 1)
294 clen
= cpylen
= rb_linebuf_skip_crlf(ch
, len
);
298 /* This is the overflow case..This doesn't happen often.. */
299 if(cpylen
> (BUF_DATA_SIZE
- bufline
->len
- 1))
301 clen
= BUF_DATA_SIZE
- bufline
->len
- 1;
302 memcpy(bufch
, ch
, clen
);
303 bufline
->buf
[BUF_DATA_SIZE
- 1] = '\0';
304 bufch
= bufline
->buf
+ BUF_DATA_SIZE
- 2;
305 bufline
->terminated
= 1;
306 bufline
->len
= BUF_DATA_SIZE
- 1;
307 bufhead
->len
+= BUF_DATA_SIZE
- 1;
311 memcpy(bufch
, ch
, cpylen
);
316 if(*bufch
!= '\r' && *bufch
!= '\n')
318 /* No linefeed, bail for the next time */
319 bufhead
->len
+= cpylen
;
320 bufline
->len
+= cpylen
;
321 bufline
->terminated
= 0;
325 bufline
->terminated
= 1;
326 bufhead
->len
+= cpylen
;
327 bufline
->len
+= cpylen
;
335 * Take a given buffer and break out as many buffers as we can.
336 * If we find a CRLF, we terminate that buffer and create a new one.
337 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
338 * 'finished', so the next loop through we can continue appending ..
340 * A few notes here, which you'll need to understand before continuing.
342 * - right now I'm only dealing with single sized buffers. Later on,
343 * I might consider chaining buffers together to get longer "lines"
344 * but seriously, I don't see the advantage right now.
346 * - This *is* designed to turn into a reference-counter-protected setup
347 * to dodge copious copies.
350 rb_linebuf_parse(buf_head_t
* bufhead
, char *data
, int len
, int raw
)
356 /* First, if we have a partial buffer, try to squeze data into it */
357 if(bufhead
->list
.tail
!= NULL
)
359 /* Check we're doing the partial buffer thing */
360 bufline
= bufhead
->list
.tail
->data
;
361 /* just try, the worst it could do is *reject* us .. */
363 cpylen
= rb_linebuf_copy_line(bufhead
, bufline
, data
, len
);
365 cpylen
= rb_linebuf_copy_raw(bufhead
, bufline
, data
, len
);
371 /* If we've copied the same as what we've got, quit now */
373 return linecnt
; /* all the data done so soon? */
375 /* Skip the data and update len .. */
377 lrb_assert(len
>= 0);
384 /* We obviously need a new buffer, so .. */
385 bufline
= rb_linebuf_new_line(bufhead
);
389 cpylen
= rb_linebuf_copy_line(bufhead
, bufline
, data
, len
);
391 cpylen
= rb_linebuf_copy_raw(bufhead
, bufline
, data
, len
);
397 lrb_assert(len
>= 0);
408 * get the next buffer from our line. For the time being it will copy
409 * data into the given buffer and free the underlying linebuf.
412 rb_linebuf_get(buf_head_t
* bufhead
, char *buf
, int buflen
, int partial
, int raw
)
418 /* make sure we have a line */
419 if(bufhead
->list
.head
== NULL
)
420 return 0; /* Obviously not.. hrm. */
422 bufline
= bufhead
->list
.head
->data
;
424 /* make sure that the buffer was actually *terminated */
425 if(!(partial
|| bufline
->terminated
))
426 return 0; /* Wait for more data! */
428 if(buflen
< bufline
->len
)
431 cpylen
= bufline
->len
;
434 start
= bufline
->buf
;
436 /* if we left extraneous '\r\n' characters in the string,
437 * and we don't want to read the raw data, clean up the string.
439 if(bufline
->raw
&& !raw
)
441 /* skip leading EOL characters */
442 while(cpylen
&& (*start
== '\r' || *start
== '\n'))
447 /* skip trailing EOL characters */
448 ch
= &start
[cpylen
- 1];
449 while(cpylen
&& (*ch
== '\r' || *ch
== '\n'))
456 memcpy(buf
, start
, cpylen
);
458 /* convert CR/LF to NULL */
462 lrb_assert(cpylen
>= 0);
464 /* Deallocate the line */
465 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
467 /* return how much we copied */
474 * attach the lines in a buf_head_t to another buf_head_t
475 * without copying the data (using refcounts).
478 rb_linebuf_attach(buf_head_t
* bufhead
, buf_head_t
* new)
483 RB_DLINK_FOREACH(ptr
, new->list
.head
)
486 rb_dlinkAddTailAlloc(line
, &bufhead
->list
);
488 /* Update the allocated size */
490 bufhead
->len
+= line
->len
;
500 * Similar to rb_linebuf_put, but designed for use by send.c.
502 * prefixfmt is used as a format for the varargs, and is inserted first.
503 * Then format/va_args is appended to the buffer.
506 rb_linebuf_putmsg(buf_head_t
* bufhead
, const char *format
, va_list * va_args
,
507 const char *prefixfmt
, ...)
513 /* make sure the previous line is terminated */
515 if(bufhead
->list
.tail
)
517 bufline
= bufhead
->list
.tail
->data
;
518 lrb_assert(bufline
->terminated
);
521 /* Create a new line */
522 bufline
= rb_linebuf_new_line(bufhead
);
524 if(prefixfmt
!= NULL
)
526 va_start(prefix_args
, prefixfmt
);
527 len
= vsnprintf(bufline
->buf
, BUF_DATA_SIZE
, prefixfmt
, prefix_args
);
533 len
+= vsnprintf((bufline
->buf
+ len
), (BUF_DATA_SIZE
- len
), format
, *va_args
);
536 bufline
->terminated
= 1;
538 /* Truncate the data if required */
539 if(rb_unlikely(len
> 510))
542 bufline
->buf
[len
++] = '\r';
543 bufline
->buf
[len
++] = '\n';
545 else if(rb_unlikely(len
== 0))
547 bufline
->buf
[len
++] = '\r';
548 bufline
->buf
[len
++] = '\n';
549 bufline
->buf
[len
] = '\0';
553 /* Chop trailing CRLF's .. */
554 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n')
555 || (bufline
->buf
[len
] == '\0'))
560 bufline
->buf
[++len
] = '\r';
561 bufline
->buf
[++len
] = '\n';
562 bufline
->buf
[++len
] = '\0';
570 * rb_linebuf_putprefix
572 * Similar to rb_linebuf_put, but designed for use by send.c.
574 * prefix is inserted first, then format/va_args is appended to the buffer.
577 rb_linebuf_putprefix(buf_head_t
* bufhead
, const char *format
, va_list * va_args
,
583 /* make sure the previous line is terminated */
585 if(bufhead
->list
.tail
)
587 bufline
= bufhead
->list
.tail
->data
;
588 lrb_assert(bufline
->terminated
);
591 /* Create a new line */
592 bufline
= rb_linebuf_new_line(bufhead
);
595 len
= rb_strlcpy(bufline
->buf
, prefix
, BUF_DATA_SIZE
);
599 len
+= vsnprintf((bufline
->buf
+ len
), (BUF_DATA_SIZE
- len
), format
, *va_args
);
602 bufline
->terminated
= 1;
604 /* Truncate the data if required */
605 if(rb_unlikely(len
> 510))
608 bufline
->buf
[len
++] = '\r';
609 bufline
->buf
[len
++] = '\n';
611 else if(rb_unlikely(len
== 0))
613 bufline
->buf
[len
++] = '\r';
614 bufline
->buf
[len
++] = '\n';
615 bufline
->buf
[len
] = '\0';
619 /* Chop trailing CRLF's .. */
620 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n')
621 || (bufline
->buf
[len
] == '\0'))
626 bufline
->buf
[++len
] = '\r';
627 bufline
->buf
[++len
] = '\n';
628 bufline
->buf
[++len
] = '\0';
636 rb_linebuf_putbuf(buf_head_t
* bufhead
, const char *buffer
)
641 /* make sure the previous line is terminated */
643 if(bufhead
->list
.tail
)
645 bufline
= bufhead
->list
.tail
->data
;
646 lrb_assert(bufline
->terminated
);
649 /* Create a new line */
650 bufline
= rb_linebuf_new_line(bufhead
);
652 if(rb_unlikely(buffer
!= NULL
))
653 len
= rb_strlcpy(bufline
->buf
, buffer
, BUF_DATA_SIZE
);
655 bufline
->terminated
= 1;
657 /* Truncate the data if required */
658 if(rb_unlikely(len
> 510))
661 bufline
->buf
[len
++] = '\r';
662 bufline
->buf
[len
++] = '\n';
664 else if(rb_unlikely(len
== 0))
666 bufline
->buf
[len
++] = '\r';
667 bufline
->buf
[len
++] = '\n';
668 bufline
->buf
[len
] = '\0';
672 /* Chop trailing CRLF's .. */
673 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n')
674 || (bufline
->buf
[len
] == '\0'))
679 bufline
->buf
[++len
] = '\r';
680 bufline
->buf
[++len
] = '\n';
681 bufline
->buf
[++len
] = '\0';
691 rb_linebuf_put(buf_head_t
* bufhead
, const char *format
, ...)
697 /* make sure the previous line is terminated */
699 if(bufhead
->list
.tail
)
701 bufline
= bufhead
->list
.tail
->data
;
702 lrb_assert(bufline
->terminated
);
705 /* Create a new line */
706 bufline
= rb_linebuf_new_line(bufhead
);
708 if(rb_unlikely(format
!= NULL
))
710 va_start(args
, format
);
711 len
= vsnprintf(bufline
->buf
, BUF_DATA_SIZE
, format
, args
);
715 bufline
->terminated
= 1;
717 /* Truncate the data if required */
718 if(rb_unlikely(len
> 510))
721 bufline
->buf
[len
++] = '\r';
722 bufline
->buf
[len
++] = '\n';
724 else if(rb_unlikely(len
== 0))
726 bufline
->buf
[len
++] = '\r';
727 bufline
->buf
[len
++] = '\n';
728 bufline
->buf
[len
] = '\0';
732 /* Chop trailing CRLF's .. */
733 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n')
734 || (bufline
->buf
[len
] == '\0'))
739 bufline
->buf
[++len
] = '\r';
740 bufline
->buf
[++len
] = '\n';
741 bufline
->buf
[++len
] = '\0';
753 * Flush data to the buffer. It tries to write as much data as possible
754 * to the given socket. Any return values are passed straight through.
755 * If there is no data in the socket, EWOULDBLOCK is set as an errno
756 * rather than returning 0 (which would map to an EOF..)
758 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
759 * and tag it so that we don't re-schedule another write until
763 rb_linebuf_flush(rb_fde_t
*F
, buf_head_t
* bufhead
)
769 * autoconf checks for this..but really just want to use it if we have a
770 * native version even if libircd provides a fake version...
778 static struct rb_iovec vec
[RB_UIO_MAXIOV
];
780 memset(vec
, 0, sizeof(vec
));
781 /* Check we actually have a first buffer */
782 if(bufhead
->list
.head
== NULL
)
784 /* nope, so we return none .. */
789 ptr
= bufhead
->list
.head
;
792 if(!bufline
->terminated
)
799 vec
[x
].iov_base
= bufline
->buf
+ bufhead
->writeofs
;
800 vec
[x
++].iov_len
= bufline
->len
- bufhead
->writeofs
;
809 if(!bufline
->terminated
)
812 vec
[x
].iov_base
= bufline
->buf
;
813 vec
[x
].iov_len
= bufline
->len
;
817 while(++x
< RB_UIO_MAXIOV
);
825 xret
= retval
= rb_writev(F
, vec
, x
);
829 ptr
= bufhead
->list
.head
;
831 for(y
= 0; y
< x
; y
++)
835 if(xret
>= bufline
->len
- bufhead
->writeofs
)
837 xret
-= bufline
->len
- bufhead
->writeofs
;
839 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
840 bufhead
->writeofs
= 0;
844 bufhead
->writeofs
+= xret
;
853 /* this is the non-writev case */
855 /* Check we actually have a first buffer */
856 if(bufhead
->list
.head
== NULL
)
858 /* nope, so we return none .. */
863 bufline
= bufhead
->list
.head
->data
;
865 /* And that its actually full .. */
866 if(!bufline
->terminated
)
872 /* Now, try writing data */
873 retval
= rb_write(F
, bufline
->buf
+ bufhead
->writeofs
, bufline
->len
- bufhead
->writeofs
);
878 /* we've got data, so update the write offset */
879 bufhead
->writeofs
+= retval
;
881 /* if we've written everything *and* the CRLF, deallocate and update
883 if(bufhead
->writeofs
== bufline
->len
)
885 bufhead
->writeofs
= 0;
886 lrb_assert(bufhead
->len
>= 0);
887 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
890 /* Return line length */
897 * count linebufs for stats z
901 rb_count_rb_linebuf_memory(size_t *count
, size_t *rb_linebuf_memory_used
)
903 rb_bh_usage(rb_linebuf_heap
, count
, NULL
, rb_linebuf_memory_used
, NULL
);