]>
jfr.im git - solanum.git/blob - libratbox/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 25038 2008-01-23 16:03:08Z androsyn $
27 #include <libratbox_config.h>
28 #include <ratbox_lib.h>
29 #include <commio-int.h>
32 static rb_bh
*rb_linebuf_heap
;
35 static int bufline_count
= 0;
37 #ifndef LINEBUF_HEAP_SIZE
38 #define LINEBUF_HEAP_SIZE 2048
44 * Initialise the linebuf mechanism
48 rb_linebuf_init(size_t heap_size
)
51 rb_linebuf_heap
= rb_bh_create(sizeof(buf_line_t
), heap_size
, "librb_linebuf_heap");
56 rb_linebuf_allocate(void)
60 t
= rb_bh_alloc(rb_linebuf_heap
);
62 t
= rb_malloc(sizeof(buf_line_t
));
69 rb_linebuf_free(buf_line_t
* p
)
72 rb_bh_free(rb_linebuf_heap
, p
);
81 * Create a new line, and link it to the given linebuf.
82 * It will be initially empty.
85 rb_linebuf_new_line(buf_head_t
* bufhead
)
90 bufline
= rb_linebuf_allocate();
96 node
= rb_make_rb_dlink_node();
98 /* Stick it at the end of the buf list */
99 rb_dlinkAddTail(bufline
, node
, &bufhead
->list
);
102 /* And finally, update the allocated size */
111 * rb_linebuf_done_line
113 * We've finished with the given line, so deallocate it
116 rb_linebuf_done_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, rb_dlink_node
* node
)
118 /* Remove it from the linked list */
119 rb_dlinkDestroy(node
, &bufhead
->list
);
121 /* Update the allocated size */
123 bufhead
->len
-= bufline
->len
;
124 lrb_assert(bufhead
->len
>= 0);
128 lrb_assert(bufline
->refcount
>= 0);
130 if(bufline
->refcount
== 0)
132 /* and finally, deallocate the buf */
134 lrb_assert(bufline_count
>= 0);
135 rb_linebuf_free(bufline
);
141 * skip to end of line or the crlfs, return the number of bytes ..
144 rb_linebuf_skip_crlf(char *ch
, int len
)
148 /* First, skip until the first non-CRLF */
149 for(; len
; len
--, ch
++)
157 /* Then, skip until the last CRLF */
158 for(; len
; len
--, ch
++)
160 if((*ch
!= '\r') && (*ch
!= '\n'))
163 lrb_assert(orig_len
> len
);
164 return (orig_len
- len
);
172 * Initialise the new buffer
175 rb_linebuf_newbuf(buf_head_t
* bufhead
)
177 /* not much to do right now :) */
178 memset(bufhead
, 0, sizeof(buf_head_t
));
184 * Flush all the lines associated with this buffer
187 rb_linebuf_donebuf(buf_head_t
* bufhead
)
189 while(bufhead
->list
.head
!= NULL
)
191 rb_linebuf_done_line(bufhead
, (buf_line_t
*) bufhead
->list
.head
->data
, bufhead
->list
.head
);
196 * rb_linebuf_copy_line
198 * Okay..this functions comments made absolutely no sense.
200 * Basically what we do is this. Find the first chunk of text
201 * and then scan for a CRLF. If we didn't find it, but we didn't
202 * overflow our buffer..we wait for some more data.
203 * If we found a CRLF, we replace them with a \0 character.
204 * If we overflowed, we copy the most our buffer can handle, terminate
205 * it with a \0 and return.
207 * The return value is the amount of data we consumed. This could
208 * be different than the size of the linebuffer, as when we discard
209 * the overflow, we don't want to process it again.
211 * This still sucks in my opinion, but it seems to work.
216 rb_linebuf_copy_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
218 int cpylen
= 0; /* how many bytes we've copied */
219 char *ch
= data
; /* Pointer to where we are in the read data */
220 char *bufch
= bufline
->buf
+ bufline
->len
;
221 int clen
= 0; /* how many bytes we've processed,
222 and don't ever want to see again.. */
224 /* If its full or terminated, ignore it */
227 lrb_assert(bufline
->len
< BUF_DATA_SIZE
);
228 if(bufline
->terminated
== 1)
231 clen
= cpylen
= rb_linebuf_skip_crlf(ch
, len
);
235 /* This is the ~overflow case..This doesn't happen often.. */
236 if(cpylen
> (BUF_DATA_SIZE
- bufline
->len
- 1))
238 memcpy(bufch
, ch
, (BUF_DATA_SIZE
- bufline
->len
- 1));
239 bufline
->buf
[BUF_DATA_SIZE
- 1] = '\0';
240 bufch
= bufline
->buf
+ BUF_DATA_SIZE
- 2;
241 while(cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
247 bufline
->terminated
= 1;
248 bufline
->len
= BUF_DATA_SIZE
- 1;
249 bufhead
->len
+= BUF_DATA_SIZE
- 1;
253 memcpy(bufch
, ch
, cpylen
);
258 if(*bufch
!= '\r' && *bufch
!= '\n')
260 /* No linefeed, bail for the next time */
261 bufhead
->len
+= cpylen
;
262 bufline
->len
+= cpylen
;
263 bufline
->terminated
= 0;
267 /* Yank the CRLF off this, replace with a \0 */
268 while(cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
275 bufline
->terminated
= 1;
276 bufhead
->len
+= cpylen
;
277 bufline
->len
+= cpylen
;
282 * rb_linebuf_copy_raw
284 * Copy as much data as possible directly into a linebuf,
285 * splitting at \r\n, but without altering any data.
289 rb_linebuf_copy_raw(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
291 int cpylen
= 0; /* how many bytes we've copied */
292 char *ch
= data
; /* Pointer to where we are in the read data */
293 char *bufch
= bufline
->buf
+ bufline
->len
;
294 int clen
= 0; /* how many bytes we've processed,
295 and don't ever want to see again.. */
297 /* If its full or terminated, ignore it */
300 lrb_assert(bufline
->len
< BUF_DATA_SIZE
);
301 if(bufline
->terminated
== 1)
304 clen
= cpylen
= rb_linebuf_skip_crlf(ch
, len
);
308 /* This is the overflow case..This doesn't happen often.. */
309 if(cpylen
> (BUF_DATA_SIZE
- bufline
->len
- 1))
311 clen
= BUF_DATA_SIZE
- bufline
->len
- 1;
312 memcpy(bufch
, ch
, clen
);
313 bufline
->buf
[BUF_DATA_SIZE
- 1] = '\0';
314 bufch
= bufline
->buf
+ BUF_DATA_SIZE
- 2;
315 bufline
->terminated
= 1;
316 bufline
->len
= BUF_DATA_SIZE
- 1;
317 bufhead
->len
+= BUF_DATA_SIZE
- 1;
321 memcpy(bufch
, ch
, cpylen
);
326 if(*bufch
!= '\r' && *bufch
!= '\n')
328 /* No linefeed, bail for the next time */
329 bufhead
->len
+= cpylen
;
330 bufline
->len
+= cpylen
;
331 bufline
->terminated
= 0;
335 bufline
->terminated
= 1;
336 bufhead
->len
+= cpylen
;
337 bufline
->len
+= cpylen
;
345 * Take a given buffer and break out as many buffers as we can.
346 * If we find a CRLF, we terminate that buffer and create a new one.
347 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
348 * 'finished', so the next loop through we can continue appending ..
350 * A few notes here, which you'll need to understand before continuing.
352 * - right now I'm only dealing with single sized buffers. Later on,
353 * I might consider chaining buffers together to get longer "lines"
354 * but seriously, I don't see the advantage right now.
356 * - This *is* designed to turn into a reference-counter-protected setup
357 * to dodge copious copies.
360 rb_linebuf_parse(buf_head_t
* bufhead
, char *data
, int len
, int raw
)
366 /* First, if we have a partial buffer, try to squeze data into it */
367 if(bufhead
->list
.tail
!= NULL
)
369 /* Check we're doing the partial buffer thing */
370 bufline
= bufhead
->list
.tail
->data
;
371 /* just try, the worst it could do is *reject* us .. */
373 cpylen
= rb_linebuf_copy_line(bufhead
, bufline
, data
, len
);
375 cpylen
= rb_linebuf_copy_raw(bufhead
, bufline
, data
, len
);
381 /* If we've copied the same as what we've got, quit now */
383 return linecnt
; /* all the data done so soon? */
385 /* Skip the data and update len .. */
387 lrb_assert(len
>= 0);
394 /* We obviously need a new buffer, so .. */
395 bufline
= rb_linebuf_new_line(bufhead
);
399 cpylen
= rb_linebuf_copy_line(bufhead
, bufline
, data
, len
);
401 cpylen
= rb_linebuf_copy_raw(bufhead
, bufline
, data
, len
);
407 lrb_assert(len
>= 0);
418 * get the next buffer from our line. For the time being it will copy
419 * data into the given buffer and free the underlying linebuf.
422 rb_linebuf_get(buf_head_t
* bufhead
, char *buf
, int buflen
, int partial
, int raw
)
428 /* make sure we have a line */
429 if(bufhead
->list
.head
== NULL
)
430 return 0; /* Obviously not.. hrm. */
432 bufline
= bufhead
->list
.head
->data
;
434 /* make sure that the buffer was actually *terminated */
435 if(!(partial
|| bufline
->terminated
))
436 return 0; /* Wait for more data! */
438 if(buflen
< bufline
->len
)
441 cpylen
= bufline
->len
;
444 start
= bufline
->buf
;
446 /* if we left extraneous '\r\n' characters in the string,
447 * and we don't want to read the raw data, clean up the string.
449 if(bufline
->raw
&& !raw
)
451 /* skip leading EOL characters */
452 while(cpylen
&& (*start
== '\r' || *start
== '\n'))
457 /* skip trailing EOL characters */
458 ch
= &start
[cpylen
- 1];
459 while(cpylen
&& (*ch
== '\r' || *ch
== '\n'))
466 memcpy(buf
, start
, cpylen
);
468 /* convert CR/LF to NULL */
472 lrb_assert(cpylen
>= 0);
474 /* Deallocate the line */
475 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
477 /* return how much we copied */
484 * attach the lines in a buf_head_t to another buf_head_t
485 * without copying the data (using refcounts).
488 rb_linebuf_attach(buf_head_t
* bufhead
, buf_head_t
* new)
493 RB_DLINK_FOREACH(ptr
, new->list
.head
)
496 rb_dlinkAddTailAlloc(line
, &bufhead
->list
);
498 /* Update the allocated size */
500 bufhead
->len
+= line
->len
;
512 * Similar to rb_linebuf_put, but designed for use by send.c.
514 * prefixfmt is used as a format for the varargs, and is inserted first.
515 * Then format/va_args is appended to the buffer.
518 rb_linebuf_putmsg(buf_head_t
* bufhead
, const char *format
, va_list * va_args
, const char *prefixfmt
, ...)
524 /* make sure the previous line is terminated */
526 if(bufhead
->list
.tail
)
528 bufline
= bufhead
->list
.tail
->data
;
529 lrb_assert(bufline
->terminated
);
532 /* Create a new line */
533 bufline
= rb_linebuf_new_line(bufhead
);
535 if(prefixfmt
!= NULL
)
537 va_start(prefix_args
, prefixfmt
);
538 len
= rb_vsnprintf(bufline
->buf
, BUF_DATA_SIZE
, prefixfmt
, prefix_args
);
544 len
+= rb_vsnprintf((bufline
->buf
+ len
), (BUF_DATA_SIZE
- len
), format
, *va_args
);
547 bufline
->terminated
= 1;
549 /* Truncate the data if required */
550 if(unlikely(len
> 510))
553 bufline
->buf
[len
++] = '\r';
554 bufline
->buf
[len
++] = '\n';
556 else if(unlikely(len
== 0))
558 bufline
->buf
[len
++] = '\r';
559 bufline
->buf
[len
++] = '\n';
560 bufline
->buf
[len
] = '\0';
564 /* Chop trailing CRLF's .. */
565 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n') || (bufline
->buf
[len
] == '\0'))
570 bufline
->buf
[++len
] = '\r';
571 bufline
->buf
[++len
] = '\n';
572 bufline
->buf
[++len
] = '\0';
580 rb_linebuf_putbuf(buf_head_t
*bufhead
, const char *buffer
)
585 /* make sure the previous line is terminated */
587 if(bufhead
->list
.tail
)
589 bufline
= bufhead
->list
.tail
->data
;
590 lrb_assert(bufline
->terminated
);
593 /* Create a new line */
594 bufline
= rb_linebuf_new_line(bufhead
);
596 if(unlikely(buffer
!= NULL
))
597 len
= rb_strlcpy(bufline
->buf
, buffer
, BUF_DATA_SIZE
);
599 bufline
->terminated
= 1;
601 /* Truncate the data if required */
602 if(unlikely(len
> 510))
605 bufline
->buf
[len
++] = '\r';
606 bufline
->buf
[len
++] = '\n';
608 else if(unlikely(len
== 0))
610 bufline
->buf
[len
++] = '\r';
611 bufline
->buf
[len
++] = '\n';
612 bufline
->buf
[len
] = '\0';
616 /* Chop trailing CRLF's .. */
617 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n') || (bufline
->buf
[len
] == '\0'))
622 bufline
->buf
[++len
] = '\r';
623 bufline
->buf
[++len
] = '\n';
624 bufline
->buf
[++len
] = '\0';
634 rb_linebuf_put(buf_head_t
* bufhead
, const char *format
, ...)
640 /* make sure the previous line is terminated */
642 if(bufhead
->list
.tail
)
644 bufline
= bufhead
->list
.tail
->data
;
645 lrb_assert(bufline
->terminated
);
648 /* Create a new line */
649 bufline
= rb_linebuf_new_line(bufhead
);
651 if(unlikely(format
!= NULL
))
653 va_start(args
, format
);
654 len
= rb_vsnprintf(bufline
->buf
, BUF_DATA_SIZE
, format
, args
);
658 bufline
->terminated
= 1;
660 /* Truncate the data if required */
661 if(unlikely(len
> 510))
664 bufline
->buf
[len
++] = '\r';
665 bufline
->buf
[len
++] = '\n';
667 else if(unlikely(len
== 0))
669 bufline
->buf
[len
++] = '\r';
670 bufline
->buf
[len
++] = '\n';
671 bufline
->buf
[len
] = '\0';
675 /* Chop trailing CRLF's .. */
676 while((bufline
->buf
[len
] == '\r') || (bufline
->buf
[len
] == '\n') || (bufline
->buf
[len
] == '\0'))
681 bufline
->buf
[++len
] = '\r';
682 bufline
->buf
[++len
] = '\n';
683 bufline
->buf
[++len
] = '\0';
695 * Flush data to the buffer. It tries to write as much data as possible
696 * to the given socket. Any return values are passed straight through.
697 * If there is no data in the socket, EWOULDBLOCK is set as an errno
698 * rather than returning 0 (which would map to an EOF..)
700 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
701 * and tag it so that we don't re-schedule another write until
705 rb_linebuf_flush(rb_fde_t
*F
, buf_head_t
* bufhead
)
711 * autoconf checks for this..but really just want to use it if we have a
712 * native version even if libircd provides a fake version...
720 static struct rb_iovec vec
[RB_UIO_MAXIOV
];
722 memset(vec
, 0, sizeof(vec
));
723 /* Check we actually have a first buffer */
724 if(bufhead
->list
.head
== NULL
)
726 /* nope, so we return none .. */
731 ptr
= bufhead
->list
.head
;
734 if(!bufline
->terminated
)
741 vec
[x
].iov_base
= bufline
->buf
+ bufhead
->writeofs
;
742 vec
[x
++].iov_len
= bufline
->len
- bufhead
->writeofs
;
751 if(!bufline
->terminated
)
754 vec
[x
].iov_base
= bufline
->buf
;
755 vec
[x
].iov_len
= bufline
->len
;
758 } while(++x
< RB_UIO_MAXIOV
);
766 xret
= retval
= rb_writev(F
, vec
, x
);
770 ptr
= bufhead
->list
.head
;
772 for(y
= 0; y
< x
; y
++)
776 if(xret
>= bufline
->len
- bufhead
->writeofs
)
778 xret
-= bufline
->len
- bufhead
->writeofs
;
780 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
781 bufhead
->writeofs
= 0;
785 bufhead
->writeofs
+= xret
;
794 /* this is the non-writev case */
796 /* Check we actually have a first buffer */
797 if(bufhead
->list
.head
== NULL
)
799 /* nope, so we return none .. */
804 bufline
= bufhead
->list
.head
->data
;
806 /* And that its actually full .. */
807 if(!bufline
->terminated
)
813 /* Now, try writing data */
814 retval
= rb_write(F
, bufline
->buf
+ bufhead
->writeofs
, bufline
->len
- bufhead
->writeofs
);
819 /* we've got data, so update the write offset */
820 bufhead
->writeofs
+= retval
;
822 /* if we've written everything *and* the CRLF, deallocate and update
824 if(bufhead
->writeofs
== bufline
->len
)
826 bufhead
->writeofs
= 0;
827 lrb_assert(bufhead
->len
>= 0);
828 rb_linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
831 /* Return line length */
838 * count linebufs for stats z
842 rb_count_rb_linebuf_memory(size_t * count
, size_t * rb_linebuf_memory_used
)
845 rb_bh_usage(rb_linebuf_heap
, count
, NULL
, rb_linebuf_memory_used
, NULL
);
848 *rb_linebuf_memory_used
= 0;