]> jfr.im git - irc/rqf/shadowircd.git/blob - libratbox/src/linebuf.c
a6c05f3ce694c08691421bbb286d2bcd95bc7506
[irc/rqf/shadowircd.git] / libratbox / src / linebuf.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * linebuf.c: Maintains linebuffers.
4 *
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
8 *
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.
13 *
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.
18 *
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
22 * USA
23 *
24 * $Id: linebuf.c 25375 2008-05-16 15:19:51Z androsyn $
25 */
26
27 #include <libratbox_config.h>
28 #include <ratbox_lib.h>
29 #include <commio-int.h>
30
31 #ifndef NOBALLOC
32 static rb_bh *rb_linebuf_heap;
33 #endif
34
35 static int bufline_count = 0;
36
37 #ifndef LINEBUF_HEAP_SIZE
38 #define LINEBUF_HEAP_SIZE 2048
39 #endif
40
41 /*
42 * rb_linebuf_init
43 *
44 * Initialise the linebuf mechanism
45 */
46
47 void
48 rb_linebuf_init(size_t heap_size)
49 {
50 #ifndef NOBALLOC
51 rb_linebuf_heap = rb_bh_create(sizeof(buf_line_t), heap_size, "librb_linebuf_heap");
52 #endif
53 }
54
55 static buf_line_t *
56 rb_linebuf_allocate(void)
57 {
58 buf_line_t *t;
59 #ifndef NOBALLOC
60 t = rb_bh_alloc(rb_linebuf_heap);
61 #else
62 t = rb_malloc(sizeof(buf_line_t));
63 #endif
64 return (t);
65
66 }
67
68 static void
69 rb_linebuf_free(buf_line_t * p)
70 {
71 #ifndef NOBALLOC
72 rb_bh_free(rb_linebuf_heap, p);
73 #else
74 rb_free(p);
75 #endif
76 }
77
78 /*
79 * rb_linebuf_new_line
80 *
81 * Create a new line, and link it to the given linebuf.
82 * It will be initially empty.
83 */
84 static buf_line_t *
85 rb_linebuf_new_line(buf_head_t * bufhead)
86 {
87 buf_line_t *bufline;
88 rb_dlink_node *node;
89
90 bufline = rb_linebuf_allocate();
91 if(bufline == NULL)
92 return NULL;
93 ++bufline_count;
94
95
96 node = rb_make_rb_dlink_node();
97
98 /* Stick it at the end of the buf list */
99 rb_dlinkAddTail(bufline, node, &bufhead->list);
100 bufline->refcount++;
101
102 /* And finally, update the allocated size */
103 bufhead->alloclen++;
104 bufhead->numlines++;
105
106 return bufline;
107 }
108
109
110 /*
111 * rb_linebuf_done_line
112 *
113 * We've finished with the given line, so deallocate it
114 */
115 static void
116 rb_linebuf_done_line(buf_head_t * bufhead, buf_line_t * bufline, rb_dlink_node * node)
117 {
118 /* Remove it from the linked list */
119 rb_dlinkDestroy(node, &bufhead->list);
120
121 /* Update the allocated size */
122 bufhead->alloclen--;
123 bufhead->len -= bufline->len;
124 lrb_assert(bufhead->len >= 0);
125 bufhead->numlines--;
126
127 bufline->refcount--;
128 lrb_assert(bufline->refcount >= 0);
129
130 if(bufline->refcount == 0)
131 {
132 /* and finally, deallocate the buf */
133 --bufline_count;
134 lrb_assert(bufline_count >= 0);
135 rb_linebuf_free(bufline);
136 }
137 }
138
139
140 /*
141 * skip to end of line or the crlfs, return the number of bytes ..
142 */
143 static inline int
144 rb_linebuf_skip_crlf(char *ch, int len)
145 {
146 int orig_len = len;
147
148 /* First, skip until the first non-CRLF */
149 for(; len; len--, ch++)
150 {
151 if(*ch == '\r')
152 break;
153 else if(*ch == '\n')
154 break;
155 }
156
157 /* Then, skip until the last CRLF */
158 for(; len; len--, ch++)
159 {
160 if((*ch != '\r') && (*ch != '\n'))
161 break;
162 }
163 lrb_assert(orig_len > len);
164 return (orig_len - len);
165 }
166
167
168
169 /*
170 * rb_linebuf_newbuf
171 *
172 * Initialise the new buffer
173 */
174 void
175 rb_linebuf_newbuf(buf_head_t * bufhead)
176 {
177 /* not much to do right now :) */
178 memset(bufhead, 0, sizeof(buf_head_t));
179 }
180
181 /*
182 * rb_linebuf_donebuf
183 *
184 * Flush all the lines associated with this buffer
185 */
186 void
187 rb_linebuf_donebuf(buf_head_t * bufhead)
188 {
189 while(bufhead->list.head != NULL)
190 {
191 rb_linebuf_done_line(bufhead, (buf_line_t *) bufhead->list.head->data, bufhead->list.head);
192 }
193 }
194
195 /*
196 * rb_linebuf_copy_line
197 *
198 * Okay..this functions comments made absolutely no sense.
199 *
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.
206 *
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.
210 *
211 * This still sucks in my opinion, but it seems to work.
212 *
213 * -Aaron
214 */
215 static int
216 rb_linebuf_copy_line(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
217 {
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.. */
223
224 /* If its full or terminated, ignore it */
225
226 bufline->raw = 0;
227 lrb_assert(bufline->len < BUF_DATA_SIZE);
228 if(bufline->terminated == 1)
229 return 0;
230
231 clen = cpylen = rb_linebuf_skip_crlf(ch, len);
232 if(clen == -1)
233 return -1;
234
235 /* This is the ~overflow case..This doesn't happen often.. */
236 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
237 {
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'))
242 {
243 *bufch = '\0';
244 cpylen--;
245 bufch--;
246 }
247 bufline->terminated = 1;
248 bufline->len = BUF_DATA_SIZE - 1;
249 bufhead->len += BUF_DATA_SIZE - 1;
250 return clen;
251 }
252
253 memcpy(bufch, ch, cpylen);
254 bufch += cpylen;
255 *bufch = '\0';
256 bufch--;
257
258 if(*bufch != '\r' && *bufch != '\n')
259 {
260 /* No linefeed, bail for the next time */
261 bufhead->len += cpylen;
262 bufline->len += cpylen;
263 bufline->terminated = 0;
264 return clen;
265 }
266
267 /* Yank the CRLF off this, replace with a \0 */
268 while(cpylen && (*bufch == '\r' || *bufch == '\n'))
269 {
270 *bufch = '\0';
271 cpylen--;
272 bufch--;
273 }
274
275 bufline->terminated = 1;
276 bufhead->len += cpylen;
277 bufline->len += cpylen;
278 return clen;
279 }
280
281 /*
282 * rb_linebuf_copy_raw
283 *
284 * Copy as much data as possible directly into a linebuf,
285 * splitting at \r\n, but without altering any data.
286 *
287 */
288 static int
289 rb_linebuf_copy_raw(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
290 {
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.. */
296
297 /* If its full or terminated, ignore it */
298
299 bufline->raw = 1;
300 lrb_assert(bufline->len < BUF_DATA_SIZE);
301 if(bufline->terminated == 1)
302 return 0;
303
304 clen = cpylen = rb_linebuf_skip_crlf(ch, len);
305 if(clen == -1)
306 return -1;
307
308 /* This is the overflow case..This doesn't happen often.. */
309 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
310 {
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;
318 return clen;
319 }
320
321 memcpy(bufch, ch, cpylen);
322 bufch += cpylen;
323 *bufch = '\0';
324 bufch--;
325
326 if(*bufch != '\r' && *bufch != '\n')
327 {
328 /* No linefeed, bail for the next time */
329 bufhead->len += cpylen;
330 bufline->len += cpylen;
331 bufline->terminated = 0;
332 return clen;
333 }
334
335 bufline->terminated = 1;
336 bufhead->len += cpylen;
337 bufline->len += cpylen;
338 return clen;
339 }
340
341
342 /*
343 * rb_linebuf_parse
344 *
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 ..
349 *
350 * A few notes here, which you'll need to understand before continuing.
351 *
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.
355 *
356 * - This *is* designed to turn into a reference-counter-protected setup
357 * to dodge copious copies.
358 */
359 int
360 rb_linebuf_parse(buf_head_t * bufhead, char *data, int len, int raw)
361 {
362 buf_line_t *bufline;
363 int cpylen;
364 int linecnt = 0;
365
366 /* First, if we have a partial buffer, try to squeze data into it */
367 if(bufhead->list.tail != NULL)
368 {
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 .. */
372 if(!raw)
373 cpylen = rb_linebuf_copy_line(bufhead, bufline, data, len);
374 else
375 cpylen = rb_linebuf_copy_raw(bufhead, bufline, data, len);
376
377 if(cpylen == -1)
378 return -1;
379
380 linecnt++;
381 /* If we've copied the same as what we've got, quit now */
382 if(cpylen == len)
383 return linecnt; /* all the data done so soon? */
384
385 /* Skip the data and update len .. */
386 len -= cpylen;
387 lrb_assert(len >= 0);
388 data += cpylen;
389 }
390
391 /* Next, the loop */
392 while(len > 0)
393 {
394 /* We obviously need a new buffer, so .. */
395 bufline = rb_linebuf_new_line(bufhead);
396
397 /* And parse */
398 if(!raw)
399 cpylen = rb_linebuf_copy_line(bufhead, bufline, data, len);
400 else
401 cpylen = rb_linebuf_copy_raw(bufhead, bufline, data, len);
402
403 if(cpylen == -1)
404 return -1;
405
406 len -= cpylen;
407 lrb_assert(len >= 0);
408 data += cpylen;
409 linecnt++;
410 }
411 return linecnt;
412 }
413
414
415 /*
416 * rb_linebuf_get
417 *
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.
420 */
421 int
422 rb_linebuf_get(buf_head_t * bufhead, char *buf, int buflen, int partial, int raw)
423 {
424 buf_line_t *bufline;
425 int cpylen;
426 char *start, *ch;
427
428 /* make sure we have a line */
429 if(bufhead->list.head == NULL)
430 return 0; /* Obviously not.. hrm. */
431
432 bufline = bufhead->list.head->data;
433
434 /* make sure that the buffer was actually *terminated */
435 if(!(partial || bufline->terminated))
436 return 0; /* Wait for more data! */
437
438 if(buflen < bufline->len)
439 cpylen = buflen - 1;
440 else
441 cpylen = bufline->len;
442
443 /* Copy it */
444 start = bufline->buf;
445
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.
448 */
449 if(bufline->raw && !raw)
450 {
451 /* skip leading EOL characters */
452 while(cpylen && (*start == '\r' || *start == '\n'))
453 {
454 start++;
455 cpylen--;
456 }
457 /* skip trailing EOL characters */
458 ch = &start[cpylen - 1];
459 while(cpylen && (*ch == '\r' || *ch == '\n'))
460 {
461 ch--;
462 cpylen--;
463 }
464 }
465
466 memcpy(buf, start, cpylen);
467
468 /* convert CR/LF to NULL */
469 if(!raw)
470 buf[cpylen] = '\0';
471
472 lrb_assert(cpylen >= 0);
473
474 /* Deallocate the line */
475 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
476
477 /* return how much we copied */
478 return cpylen;
479 }
480
481 /*
482 * rb_linebuf_attach
483 *
484 * attach the lines in a buf_head_t to another buf_head_t
485 * without copying the data (using refcounts).
486 */
487 void
488 rb_linebuf_attach(buf_head_t * bufhead, buf_head_t * new)
489 {
490 rb_dlink_node *ptr;
491 buf_line_t *line;
492
493 RB_DLINK_FOREACH(ptr, new->list.head)
494 {
495 line = ptr->data;
496 rb_dlinkAddTailAlloc(line, &bufhead->list);
497
498 /* Update the allocated size */
499 bufhead->alloclen++;
500 bufhead->len += line->len;
501 bufhead->numlines++;
502
503 line->refcount++;
504 }
505 }
506
507
508
509 /*
510 * rb_linebuf_putmsg
511 *
512 * Similar to rb_linebuf_put, but designed for use by send.c.
513 *
514 * prefixfmt is used as a format for the varargs, and is inserted first.
515 * Then format/va_args is appended to the buffer.
516 */
517 void
518 rb_linebuf_putmsg(buf_head_t * bufhead, const char *format, va_list * va_args, const char *prefixfmt, ...)
519 {
520 buf_line_t *bufline;
521 int len = 0;
522 va_list prefix_args;
523
524 /* make sure the previous line is terminated */
525 #ifndef NDEBUG
526 if(bufhead->list.tail)
527 {
528 bufline = bufhead->list.tail->data;
529 lrb_assert(bufline->terminated);
530 }
531 #endif
532 /* Create a new line */
533 bufline = rb_linebuf_new_line(bufhead);
534
535 if(prefixfmt != NULL)
536 {
537 va_start(prefix_args, prefixfmt);
538 len = rb_vsnprintf(bufline->buf, BUF_DATA_SIZE, prefixfmt, prefix_args);
539 va_end(prefix_args);
540 }
541
542 if(va_args != NULL)
543 {
544 len += rb_vsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
545 }
546
547 bufline->terminated = 1;
548
549 /* Truncate the data if required */
550 if(rb_unlikely(len > 510))
551 {
552 len = 510;
553 bufline->buf[len++] = '\r';
554 bufline->buf[len++] = '\n';
555 }
556 else if(rb_unlikely(len == 0))
557 {
558 bufline->buf[len++] = '\r';
559 bufline->buf[len++] = '\n';
560 bufline->buf[len] = '\0';
561 }
562 else
563 {
564 /* Chop trailing CRLF's .. */
565 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n') || (bufline->buf[len] == '\0'))
566 {
567 len--;
568 }
569
570 bufline->buf[++len] = '\r';
571 bufline->buf[++len] = '\n';
572 bufline->buf[++len] = '\0';
573 }
574
575 bufline->len = len;
576 bufhead->len += len;
577 }
578
579 void
580 rb_linebuf_putbuf(buf_head_t *bufhead, const char *buffer)
581 {
582 buf_line_t *bufline;
583 int len = 0;
584
585 /* make sure the previous line is terminated */
586 #ifndef NDEBUG
587 if(bufhead->list.tail)
588 {
589 bufline = bufhead->list.tail->data;
590 lrb_assert(bufline->terminated);
591 }
592 #endif
593 /* Create a new line */
594 bufline = rb_linebuf_new_line(bufhead);
595
596 if(rb_unlikely(buffer != NULL))
597 len = rb_strlcpy(bufline->buf, buffer, BUF_DATA_SIZE);
598
599 bufline->terminated = 1;
600
601 /* Truncate the data if required */
602 if(rb_unlikely(len > 510))
603 {
604 len = 510;
605 bufline->buf[len++] = '\r';
606 bufline->buf[len++] = '\n';
607 }
608 else if(rb_unlikely(len == 0))
609 {
610 bufline->buf[len++] = '\r';
611 bufline->buf[len++] = '\n';
612 bufline->buf[len] = '\0';
613 }
614 else
615 {
616 /* Chop trailing CRLF's .. */
617 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n') || (bufline->buf[len] == '\0'))
618 {
619 len--;
620 }
621
622 bufline->buf[++len] = '\r';
623 bufline->buf[++len] = '\n';
624 bufline->buf[++len] = '\0';
625 }
626
627 bufline->len = len;
628 bufhead->len += len;
629
630
631 }
632
633 void
634 rb_linebuf_put(buf_head_t * bufhead, const char *format, ...)
635 {
636 buf_line_t *bufline;
637 int len = 0;
638 va_list args;
639
640 /* make sure the previous line is terminated */
641 #ifndef NDEBUG
642 if(bufhead->list.tail)
643 {
644 bufline = bufhead->list.tail->data;
645 lrb_assert(bufline->terminated);
646 }
647 #endif
648 /* Create a new line */
649 bufline = rb_linebuf_new_line(bufhead);
650
651 if(rb_unlikely(format != NULL))
652 {
653 va_start(args, format);
654 len = rb_vsnprintf(bufline->buf, BUF_DATA_SIZE, format, args);
655 va_end(args);
656 }
657
658 bufline->terminated = 1;
659
660 /* Truncate the data if required */
661 if(rb_unlikely(len > 510))
662 {
663 len = 510;
664 bufline->buf[len++] = '\r';
665 bufline->buf[len++] = '\n';
666 }
667 else if(rb_unlikely(len == 0))
668 {
669 bufline->buf[len++] = '\r';
670 bufline->buf[len++] = '\n';
671 bufline->buf[len] = '\0';
672 }
673 else
674 {
675 /* Chop trailing CRLF's .. */
676 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n') || (bufline->buf[len] == '\0'))
677 {
678 len--;
679 }
680
681 bufline->buf[++len] = '\r';
682 bufline->buf[++len] = '\n';
683 bufline->buf[++len] = '\0';
684 }
685
686 bufline->len = len;
687 bufhead->len += len;
688 }
689
690
691
692 /*
693 * rb_linebuf_flush
694 *
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..)
699 *
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
702 * we have a CRLF.
703 */
704 int
705 rb_linebuf_flush(rb_fde_t *F, buf_head_t * bufhead)
706 {
707 buf_line_t *bufline;
708 int retval;
709
710 /*
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...
713 */
714 #ifdef HAVE_WRITEV
715 if(!rb_fd_ssl(F))
716 {
717 rb_dlink_node *ptr;
718 int x = 0, y;
719 int xret;
720 static struct rb_iovec vec[RB_UIO_MAXIOV];
721
722 memset(vec, 0, sizeof(vec));
723 /* Check we actually have a first buffer */
724 if(bufhead->list.head == NULL)
725 {
726 /* nope, so we return none .. */
727 errno = EWOULDBLOCK;
728 return -1;
729 }
730
731 ptr = bufhead->list.head;
732
733 bufline = ptr->data;
734 if(!bufline->terminated)
735 {
736 errno = EWOULDBLOCK;
737 return -1;
738
739 }
740
741 vec[x].iov_base = bufline->buf + bufhead->writeofs;
742 vec[x++].iov_len = bufline->len - bufhead->writeofs;
743 ptr = ptr->next;
744
745 do
746 {
747 if(ptr == NULL)
748 break;
749
750 bufline = ptr->data;
751 if(!bufline->terminated)
752 break;
753
754 vec[x].iov_base = bufline->buf;
755 vec[x].iov_len = bufline->len;
756 ptr = ptr->next;
757
758 } while(++x < RB_UIO_MAXIOV);
759
760 if(x == 0)
761 {
762 errno = EWOULDBLOCK;
763 return -1;
764 }
765
766 xret = retval = rb_writev(F, vec, x);
767 if(retval <= 0)
768 return retval;
769
770 ptr = bufhead->list.head;
771
772 for(y = 0; y < x; y++)
773 {
774 bufline = ptr->data;
775
776 if(xret >= bufline->len - bufhead->writeofs)
777 {
778 xret -= bufline->len - bufhead->writeofs;
779 ptr = ptr->next;
780 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
781 bufhead->writeofs = 0;
782 }
783 else
784 {
785 bufhead->writeofs += xret;
786 break;
787 }
788 }
789
790 return retval;
791 }
792 #endif
793
794 /* this is the non-writev case */
795
796 /* Check we actually have a first buffer */
797 if(bufhead->list.head == NULL)
798 {
799 /* nope, so we return none .. */
800 errno = EWOULDBLOCK;
801 return -1;
802 }
803
804 bufline = bufhead->list.head->data;
805
806 /* And that its actually full .. */
807 if(!bufline->terminated)
808 {
809 errno = EWOULDBLOCK;
810 return -1;
811 }
812
813 /* Now, try writing data */
814 retval = rb_write(F, bufline->buf + bufhead->writeofs, bufline->len - bufhead->writeofs);
815
816 if(retval <= 0)
817 return retval;
818
819 /* we've got data, so update the write offset */
820 bufhead->writeofs += retval;
821
822 /* if we've written everything *and* the CRLF, deallocate and update
823 bufhead */
824 if(bufhead->writeofs == bufline->len)
825 {
826 bufhead->writeofs = 0;
827 lrb_assert(bufhead->len >= 0);
828 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
829 }
830
831 /* Return line length */
832 return retval;
833 }
834
835
836
837 /*
838 * count linebufs for stats z
839 */
840
841 void
842 rb_count_rb_linebuf_memory(size_t * count, size_t * rb_linebuf_memory_used)
843 {
844 #ifndef NOBALLOC
845 rb_bh_usage(rb_linebuf_heap, count, NULL, rb_linebuf_memory_used, NULL);
846 #else
847 *count = 0;
848 *rb_linebuf_memory_used = 0;
849 #endif
850 }