Mumble 1.2.x compatible server. Kind of working, at least in server loopback mode.
[umurmur.git] / src / google / protobuf-c / protobuf-c-data-buffer.c
1 /* Free blocks to hold around to avoid repeated mallocs... */
2 #define MAX_RECYCLED            16
3
4 /* Size of allocations to make. */
5 #define BUF_CHUNK_SIZE          8192
6
7 /* Max fragments in the iovector to writev. */
8 #define MAX_FRAGMENTS_TO_WRITE  16
9
10 /* This causes fragments not to be transferred from buffer to buffer,
11  * and not to be allocated in pools.  The result is that stack-trace
12  * based debug-allocators work much better with this on.
13  *
14  * On the other hand, this can mask over some abuses (eg stack-based
15  * foreign buffer fragment bugs) so we disable it by default.
16  */ 
17 #define GSK_DEBUG_BUFFER_ALLOCATIONS    0
18
19 #define BUFFER_RECYCLING                0
20
21 #include <sys/types.h>
22 #include <sys/uio.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <alloca.h>
27 #include "protobuf-c-data-buffer.h"
28
29 #undef TRUE
30 #define TRUE 1
31 #undef FALSE
32 #define FALSE 0
33
34 #define PROTOBUF_C_FRAGMENT_DATA_SIZE        4096
35 #define PROTOBUF_C_FRAGMENT_DATA(frag)     ((uint8_t*)(((ProtobufCDataBufferFragment*)(frag))+1))
36
37 /* --- ProtobufCDataBufferFragment implementation --- */
38 static inline int 
39 protobuf_c_data_buffer_fragment_avail (ProtobufCDataBufferFragment *frag)
40 {
41   return PROTOBUF_C_FRAGMENT_DATA_SIZE - frag->buf_start - frag->buf_length;
42 }
43 static inline uint8_t *
44 protobuf_c_data_buffer_fragment_start (ProtobufCDataBufferFragment *frag)
45 {
46   return PROTOBUF_C_FRAGMENT_DATA(frag) + frag->buf_start;
47 }
48 static inline uint8_t *
49 protobuf_c_data_buffer_fragment_end (ProtobufCDataBufferFragment *frag)
50 {
51   return PROTOBUF_C_FRAGMENT_DATA(frag) + frag->buf_start + frag->buf_length;
52 }
53
54 /* --- ProtobufCDataBufferFragment recycling --- */
55 #if BUFFER_RECYCLING
56 static int num_recycled = 0;
57 static ProtobufCDataBufferFragment* recycling_stack = 0;
58 #endif
59
60 static ProtobufCDataBufferFragment *
61 new_native_fragment(ProtobufCAllocator *allocator)
62 {
63   ProtobufCDataBufferFragment *frag;
64 #if !BUFFER_RECYCLING
65   frag = (ProtobufCDataBufferFragment *) allocator->alloc (allocator, BUF_CHUNK_SIZE);
66 #else  /* optimized (?) */
67   if (recycling_stack)
68     {
69       frag = recycling_stack;
70       recycling_stack = recycling_stack->next;
71       num_recycled--;
72     }
73   else
74     {
75       frag = (ProtobufCDataBufferFragment *) g_malloc (BUF_CHUNK_SIZE);
76     }
77 #endif  /* !GSK_DEBUG_BUFFER_ALLOCATIONS */
78   frag->buf_start = frag->buf_length = 0;
79   frag->next = 0;
80   return frag;
81 }
82
83 #if GSK_DEBUG_BUFFER_ALLOCATIONS || !BUFFER_RECYCLING
84 #define recycle(allocator, frag) allocator->free (allocator, frag)
85 #else   /* optimized (?) */
86 static void
87 recycle(ProtobufCDataBufferFragment* frag,
88         ProtobufCAllocator *allocator)
89 {
90   frag->next = recycling_stack;
91   recycling_stack = frag;
92   num_recycled++;
93 }
94 #endif  /* !GSK_DEBUG_BUFFER_ALLOCATIONS */
95
96 /* --- Global public methods --- */
97 /**
98  * protobuf_c_data_buffer_cleanup_recycling_bin:
99  * 
100  * Free unused buffer fragments.  (Normally some are
101  * kept around to reduce strain on the global allocator.)
102  */
103 void
104 protobuf_c_data_buffer_cleanup_recycling_bin ()
105 {
106 #if !GSK_DEBUG_BUFFER_ALLOCATIONS && BUFFER_RECYCLING
107   G_LOCK (recycling_stack);
108   while (recycling_stack != NULL)
109     {
110       ProtobufCDataBufferFragment *next;
111       next = recycling_stack->next;
112       g_free (recycling_stack);
113       recycling_stack = next;
114     }
115   num_recycled = 0;
116   G_UNLOCK (recycling_stack);
117 #endif
118 }
119       
120 /* --- Public methods --- */
121 /**
122  * protobuf_c_data_buffer_init:
123  * @buffer: buffer to initialize (as empty).
124  *
125  * Construct an empty buffer out of raw memory.
126  * (This is equivalent to filling the buffer with 0s)
127  */
128 void
129 protobuf_c_data_buffer_init(ProtobufCDataBuffer *buffer,
130                             ProtobufCAllocator *allocator)
131 {
132   buffer->first_frag = buffer->last_frag = NULL;
133   buffer->size = 0;
134   buffer->allocator = allocator;
135 }
136
137 #if defined(GSK_DEBUG) || GSK_DEBUG_BUFFER_ALLOCATIONS
138 static inline gboolean
139 verify_buffer (const ProtobufCDataBuffer *buffer)
140 {
141   const ProtobufCDataBufferFragment *frag;
142   size_t total = 0;
143   for (frag = buffer->first_frag; frag != NULL; frag = frag->next)
144     total += frag->buf_length;
145   return total == buffer->size;
146 }
147 #define CHECK_INTEGRITY(buffer) g_assert (verify_buffer (buffer))
148 #else
149 #define CHECK_INTEGRITY(buffer)
150 #endif
151
152 /**
153  * protobuf_c_data_buffer_append:
154  * @buffer: the buffer to add data to.  Data is put at the end of the buffer.
155  * @data: binary data to add to the buffer.
156  * @length: length of @data to add to the buffer.
157  *
158  * Append data into the buffer.
159  */
160 void
161 protobuf_c_data_buffer_append(ProtobufCDataBuffer    *buffer,
162                   const void   *data,
163                   size_t         length)
164 {
165   CHECK_INTEGRITY (buffer);
166   buffer->size += length;
167   while (length > 0)
168     {
169       size_t avail;
170       if (!buffer->last_frag)
171         {
172           buffer->last_frag = buffer->first_frag = new_native_fragment (buffer->allocator);
173           avail = protobuf_c_data_buffer_fragment_avail (buffer->last_frag);
174         }
175       else
176         {
177           avail = protobuf_c_data_buffer_fragment_avail (buffer->last_frag);
178           if (avail <= 0)
179             {
180               buffer->last_frag->next = new_native_fragment (buffer->allocator);
181               avail = protobuf_c_data_buffer_fragment_avail (buffer->last_frag);
182               buffer->last_frag = buffer->last_frag->next;
183             }
184         }
185       if (avail > length)
186         avail = length;
187       memcpy (protobuf_c_data_buffer_fragment_end (buffer->last_frag), data, avail);
188       data = (const char *) data + avail;
189       length -= avail;
190       buffer->last_frag->buf_length += avail;
191     }
192   CHECK_INTEGRITY (buffer);
193 }
194
195 void
196 protobuf_c_data_buffer_append_repeated_char (ProtobufCDataBuffer    *buffer, 
197                                  char          character,
198                                  size_t        count)
199 {
200   CHECK_INTEGRITY (buffer);
201   buffer->size += count;
202   while (count > 0)
203     {
204       size_t avail;
205       if (!buffer->last_frag)
206         {
207           buffer->last_frag = buffer->first_frag = new_native_fragment (buffer->allocator);
208           avail = protobuf_c_data_buffer_fragment_avail (buffer->last_frag);
209         }
210       else
211         {
212           avail = protobuf_c_data_buffer_fragment_avail (buffer->last_frag);
213           if (avail <= 0)
214             {
215               buffer->last_frag->next = new_native_fragment (buffer->allocator);
216               avail = protobuf_c_data_buffer_fragment_avail (buffer->last_frag);
217               buffer->last_frag = buffer->last_frag->next;
218             }
219         }
220       if (avail > count)
221         avail = count;
222       memset (protobuf_c_data_buffer_fragment_end (buffer->last_frag), character, avail);
223       count -= avail;
224       buffer->last_frag->buf_length += avail;
225     }
226   CHECK_INTEGRITY (buffer);
227 }
228
229 #if 0
230 void
231 protobuf_c_data_buffer_append_repeated_data (ProtobufCDataBuffer    *buffer, 
232                                  gconstpointer data_to_repeat,
233                                  gsize         data_length,
234                                  gsize         count)
235 {
236   ...
237 }
238 #endif
239
240 /**
241  * protobuf_c_data_buffer_append_string:
242  * @buffer: the buffer to add data to.  Data is put at the end of the buffer.
243  * @string: NUL-terminated string to append to the buffer.
244  *  The NUL is not appended.
245  *
246  * Append a string to the buffer.
247  */
248 void
249 protobuf_c_data_buffer_append_string(ProtobufCDataBuffer  *buffer,
250                          const char *string)
251 {
252   assert (string != NULL);
253   protobuf_c_data_buffer_append (buffer, string, strlen (string));
254 }
255
256 /**
257  * protobuf_c_data_buffer_append_char:
258  * @buffer: the buffer to add the byte to.
259  * @character: the byte to add to the buffer.
260  *
261  * Append a byte to a buffer.
262  */
263 void
264 protobuf_c_data_buffer_append_char(ProtobufCDataBuffer *buffer,
265                        char       character)
266 {
267   protobuf_c_data_buffer_append (buffer, &character, 1);
268 }
269
270 /**
271  * protobuf_c_data_buffer_append_string0:
272  * @buffer: the buffer to add data to.  Data is put at the end of the buffer.
273  * @string: NUL-terminated string to append to the buffer;
274  *  NUL is appended.
275  *
276  * Append a NUL-terminated string to the buffer.  The NUL is appended.
277  */
278 void
279 protobuf_c_data_buffer_append_string0      (ProtobufCDataBuffer    *buffer,
280                                 const char   *string)
281 {
282   protobuf_c_data_buffer_append (buffer, string, strlen (string) + 1);
283 }
284
285 /**
286  * protobuf_c_data_buffer_read:
287  * @buffer: the buffer to read data from.
288  * @data: buffer to fill with up to @max_length bytes of data.
289  * @max_length: maximum number of bytes to read.
290  *
291  * Removes up to @max_length data from the beginning of the buffer,
292  * and writes it to @data.  The number of bytes actually read
293  * is returned.
294  *
295  * returns: number of bytes transferred.
296  */
297 size_t
298 protobuf_c_data_buffer_read(ProtobufCDataBuffer    *buffer,
299                 void         *data,
300                 size_t         max_length)
301 {
302   size_t rv = 0;
303   size_t orig_max_length = max_length;
304   CHECK_INTEGRITY (buffer);
305   while (max_length > 0 && buffer->first_frag)
306     {
307       ProtobufCDataBufferFragment *first = buffer->first_frag;
308       if (first->buf_length <= max_length)
309         {
310           memcpy (data, protobuf_c_data_buffer_fragment_start (first), first->buf_length);
311           rv += first->buf_length;
312           data = (char *) data + first->buf_length;
313           max_length -= first->buf_length;
314           buffer->first_frag = first->next;
315           if (!buffer->first_frag)
316             buffer->last_frag = NULL;
317           recycle (buffer->allocator, first);
318         }
319       else
320         {
321           memcpy (data, protobuf_c_data_buffer_fragment_start (first), max_length);
322           rv += max_length;
323           first->buf_length -= max_length;
324           first->buf_start += max_length;
325           data = (char *) data + max_length;
326           max_length = 0;
327         }
328     }
329   buffer->size -= rv;
330   assert (rv == orig_max_length || buffer->size == 0);
331   CHECK_INTEGRITY (buffer);
332   return rv;
333 }
334
335 /**
336  * protobuf_c_data_buffer_peek:
337  * @buffer: the buffer to peek data from the front of.
338  *    This buffer is unchanged by the operation.
339  * @data: buffer to fill with up to @max_length bytes of data.
340  * @max_length: maximum number of bytes to peek.
341  *
342  * Copies up to @max_length data from the beginning of the buffer,
343  * and writes it to @data.  The number of bytes actually copied
344  * is returned.
345  *
346  * This function is just like protobuf_c_data_buffer_read() except that the 
347  * data is not removed from the buffer.
348  *
349  * returns: number of bytes copied into data.
350  */
351 size_t
352 protobuf_c_data_buffer_peek     (const ProtobufCDataBuffer *buffer,
353                      void            *data,
354                      size_t            max_length)
355 {
356   int rv = 0;
357   ProtobufCDataBufferFragment *frag = (ProtobufCDataBufferFragment *) buffer->first_frag;
358   CHECK_INTEGRITY (buffer);
359   while (max_length > 0 && frag)
360     {
361       if (frag->buf_length <= max_length)
362         {
363           memcpy (data, protobuf_c_data_buffer_fragment_start (frag), frag->buf_length);
364           rv += frag->buf_length;
365           data = (char *) data + frag->buf_length;
366           max_length -= frag->buf_length;
367           frag = frag->next;
368         }
369       else
370         {
371           memcpy (data, protobuf_c_data_buffer_fragment_start (frag), max_length);
372           rv += max_length;
373           data = (char *) data + max_length;
374           max_length = 0;
375         }
376     }
377   return rv;
378 }
379
380 /**
381  * protobuf_c_data_buffer_read_line:
382  * @buffer: buffer to read a line from.
383  *
384  * Parse a newline (\n) terminated line from
385  * buffer and return it as a newly allocated string.
386  * The newline is changed to a NUL character.
387  *
388  * If the buffer does not contain a newline, then NULL is returned.
389  *
390  * returns: a newly allocated NUL-terminated string, or NULL.
391  */
392 char *
393 protobuf_c_data_buffer_read_line(ProtobufCDataBuffer *buffer)
394 {
395   int len = 0;
396   char *rv;
397   ProtobufCDataBufferFragment *at;
398   int newline_length;
399   CHECK_INTEGRITY (buffer);
400   for (at = buffer->first_frag; at; at = at->next)
401     {
402       uint8_t *start = protobuf_c_data_buffer_fragment_start (at);
403       uint8_t *got;
404       got = memchr (start, '\n', at->buf_length);
405       if (got)
406         {
407           len += got - start;
408           break;
409         }
410       len += at->buf_length;
411     }
412   if (at == NULL)
413     return NULL;
414   rv = buffer->allocator->alloc (buffer->allocator, len + 1);
415   /* If we found a newline, read it out, truncating
416    * it with NUL before we return from the function... */
417   if (at)
418     newline_length = 1;
419   else
420     newline_length = 0;
421   protobuf_c_data_buffer_read (buffer, rv, len + newline_length);
422   rv[len] = 0;
423   CHECK_INTEGRITY (buffer);
424   return rv;
425 }
426
427 /**
428  * protobuf_c_data_buffer_parse_string0:
429  * @buffer: buffer to read a line from.
430  *
431  * Parse a NUL-terminated line from
432  * buffer and return it as a newly allocated string.
433  *
434  * If the buffer does not contain a newline, then NULL is returned.
435  *
436  * returns: a newly allocated NUL-terminated string, or NULL.
437  */
438 char *
439 protobuf_c_data_buffer_parse_string0(ProtobufCDataBuffer *buffer)
440 {
441   int index0 = protobuf_c_data_buffer_index_of (buffer, '\0');
442   char *rv;
443   if (index0 < 0)
444     return NULL;
445   rv = buffer->allocator->alloc (buffer->allocator, index0 + 1);
446   protobuf_c_data_buffer_read (buffer, rv, index0 + 1);
447   return rv;
448 }
449
450 /**
451  * protobuf_c_data_buffer_peek_char:
452  * @buffer: buffer to peek a single byte from.
453  *
454  * Get the first byte in the buffer as a positive or 0 number.
455  * If the buffer is empty, -1 is returned.
456  * The buffer is unchanged.
457  *
458  * returns: an unsigned character or -1.
459  */
460 int
461 protobuf_c_data_buffer_peek_char(const ProtobufCDataBuffer *buffer)
462 {
463   const ProtobufCDataBufferFragment *frag;
464
465   if (buffer->size == 0)
466     return -1;
467
468   for (frag = buffer->first_frag; frag; frag = frag->next)
469     if (frag->buf_length > 0)
470       break;
471   return * protobuf_c_data_buffer_fragment_start ((ProtobufCDataBufferFragment*)frag);
472 }
473
474 /**
475  * protobuf_c_data_buffer_read_char:
476  * @buffer: buffer to read a single byte from.
477  *
478  * Get the first byte in the buffer as a positive or 0 number,
479  * and remove the character from the buffer.
480  * If the buffer is empty, -1 is returned.
481  *
482  * returns: an unsigned character or -1.
483  */
484 int
485 protobuf_c_data_buffer_read_char (ProtobufCDataBuffer *buffer)
486 {
487   char c;
488   if (protobuf_c_data_buffer_read (buffer, &c, 1) == 0)
489     return -1;
490   return (int) (uint8_t) c;
491 }
492
493 /**
494  * protobuf_c_data_buffer_discard:
495  * @buffer: the buffer to discard data from.
496  * @max_discard: maximum number of bytes to discard.
497  *
498  * Removes up to @max_discard data from the beginning of the buffer,
499  * and returns the number of bytes actually discarded.
500  *
501  * returns: number of bytes discarded.
502  */
503 size_t
504 protobuf_c_data_buffer_discard(ProtobufCDataBuffer *buffer,
505                    size_t      max_discard)
506 {
507   int rv = 0;
508   CHECK_INTEGRITY (buffer);
509   while (max_discard > 0 && buffer->first_frag)
510     {
511       ProtobufCDataBufferFragment *first = buffer->first_frag;
512       if (first->buf_length <= max_discard)
513         {
514           rv += first->buf_length;
515           max_discard -= first->buf_length;
516           buffer->first_frag = first->next;
517           if (!buffer->first_frag)
518             buffer->last_frag = NULL;
519           recycle (buffer->allocator, first);
520         }
521       else
522         {
523           rv += max_discard;
524           first->buf_length -= max_discard;
525           first->buf_start += max_discard;
526           max_discard = 0;
527         }
528     }
529   buffer->size -= rv;
530   CHECK_INTEGRITY (buffer);
531   return rv;
532 }
533
534 static inline protobuf_c_boolean
535 errno_is_ignorable (int e)
536 {
537 #ifdef EWOULDBLOCK              /* for windows */
538   if (e == EWOULDBLOCK)
539     return 1;
540 #endif
541   return e == EINTR || e == EAGAIN;
542 }
543
544 /**
545  * protobuf_c_data_buffer_writev:
546  * @read_from: buffer to take data from.
547  * @fd: file-descriptor to write data to.
548  *
549  * Writes as much data as possible to the
550  * given file-descriptor using the writev(2)
551  * function to deal with multiple fragments
552  * efficiently, where available.
553  *
554  * returns: the number of bytes transferred,
555  * or -1 on a write error (consult errno).
556  */
557 int
558 protobuf_c_data_buffer_writev (ProtobufCDataBuffer       *read_from,
559                    int              fd)
560 {
561   int rv;
562   struct iovec *iov;
563   int nfrag, i;
564   ProtobufCDataBufferFragment *frag_at = read_from->first_frag;
565   CHECK_INTEGRITY (read_from);
566   for (nfrag = 0; frag_at != NULL
567 #ifdef MAX_FRAGMENTS_TO_WRITE
568        && nfrag < MAX_FRAGMENTS_TO_WRITE
569 #endif
570        ; nfrag++)
571     frag_at = frag_at->next;
572   iov = (struct iovec *) alloca (sizeof (struct iovec) * nfrag);
573   frag_at = read_from->first_frag;
574   for (i = 0; i < nfrag; i++)
575     {
576       iov[i].iov_len = frag_at->buf_length;
577       iov[i].iov_base = protobuf_c_data_buffer_fragment_start (frag_at);
578       frag_at = frag_at->next;
579     }
580   rv = writev (fd, iov, nfrag);
581   if (rv < 0 && errno_is_ignorable (errno))
582     return 0;
583   if (rv <= 0)
584     return rv;
585   protobuf_c_data_buffer_discard (read_from, rv);
586   return rv;
587 }
588
589 /**
590  * protobuf_c_data_buffer_writev_len:
591  * @read_from: buffer to take data from.
592  * @fd: file-descriptor to write data to.
593  * @max_bytes: maximum number of bytes to write.
594  *
595  * Writes up to max_bytes bytes to the
596  * given file-descriptor using the writev(2)
597  * function to deal with multiple fragments
598  * efficiently, where available.
599  *
600  * returns: the number of bytes transferred,
601  * or -1 on a write error (consult errno).
602  */
603 #undef MIN
604 #define MIN(a,b)   ((a) < (b) ? (a) : (b))
605 int
606 protobuf_c_data_buffer_writev_len (ProtobufCDataBuffer *read_from,
607                        int        fd,
608                        size_t      max_bytes)
609 {
610   int rv;
611   struct iovec *iov;
612   int nfrag, i;
613   size_t bytes;
614   ProtobufCDataBufferFragment *frag_at = read_from->first_frag;
615   CHECK_INTEGRITY (read_from);
616   for (nfrag = 0, bytes = 0; frag_at != NULL && bytes < max_bytes
617 #ifdef MAX_FRAGMENTS_TO_WRITE
618        && nfrag < MAX_FRAGMENTS_TO_WRITE
619 #endif
620        ; nfrag++)
621     {
622       bytes += frag_at->buf_length;
623       frag_at = frag_at->next;
624     }
625   iov = (struct iovec *) alloca (sizeof (struct iovec) * nfrag);
626   frag_at = read_from->first_frag;
627   for (bytes = max_bytes, i = 0; i < nfrag && bytes > 0; i++)
628     {
629       size_t frag_bytes = MIN (frag_at->buf_length, bytes);
630       iov[i].iov_len = frag_bytes;
631       iov[i].iov_base = protobuf_c_data_buffer_fragment_start (frag_at);
632       frag_at = frag_at->next;
633       bytes -= frag_bytes;
634     }
635   rv = writev (fd, iov, i);
636   if (rv < 0 && errno_is_ignorable (errno))
637     return 0;
638   if (rv <= 0)
639     return rv;
640   protobuf_c_data_buffer_discard (read_from, rv);
641   return rv;
642 }
643
644 /**
645  * protobuf_c_data_buffer_read_in_fd:
646  * @write_to: buffer to append data to.
647  * @read_from: file-descriptor to read data from.
648  *
649  * Append data into the buffer directly from the
650  * given file-descriptor.
651  *
652  * returns: the number of bytes transferred,
653  * or -1 on a read error (consult errno).
654  */
655 /* TODO: zero-copy! */
656 int
657 protobuf_c_data_buffer_read_in_fd(ProtobufCDataBuffer *write_to,
658                       int        read_from)
659 {
660   char buf[8192];
661   int rv = read (read_from, buf, sizeof (buf));
662   if (rv < 0)
663     return rv;
664   protobuf_c_data_buffer_append (write_to, buf, rv);
665   return rv;
666 }
667
668 /**
669  * protobuf_c_data_buffer_destruct:
670  * @to_destroy: the buffer to empty.
671  *
672  * Remove all fragments from a buffer, leaving it empty.
673  * The buffer is guaranteed to not to be consuming any resources,
674  * but it also is allowed to start using it again.
675  */
676 void
677 protobuf_c_data_buffer_reset(ProtobufCDataBuffer *to_destroy)
678 {
679   ProtobufCDataBufferFragment *at = to_destroy->first_frag;
680   CHECK_INTEGRITY (to_destroy);
681   while (at)
682     {
683       ProtobufCDataBufferFragment *next = at->next;
684       recycle (to_destroy->allocator, at);
685       at = next;
686     }
687   to_destroy->first_frag = to_destroy->last_frag = NULL;
688   to_destroy->size = 0;
689 }
690
691 void
692 protobuf_c_data_buffer_clear(ProtobufCDataBuffer *to_destroy)
693 {
694   ProtobufCDataBufferFragment *at = to_destroy->first_frag;
695   CHECK_INTEGRITY (to_destroy);
696   while (at)
697     {
698       ProtobufCDataBufferFragment *next = at->next;
699       recycle (to_destroy->allocator, at);
700       at = next;
701     }
702 }
703
704 /**
705  * protobuf_c_data_buffer_index_of:
706  * @buffer: buffer to scan.
707  * @char_to_find: a byte to look for.
708  *
709  * Scans for the first instance of the given character.
710  * returns: its index in the buffer, or -1 if the character
711  * is not in the buffer.
712  */
713 int
714 protobuf_c_data_buffer_index_of(ProtobufCDataBuffer *buffer,
715                     char       char_to_find)
716 {
717   ProtobufCDataBufferFragment *at = buffer->first_frag;
718   int rv = 0;
719   while (at)
720     {
721       uint8_t *start = protobuf_c_data_buffer_fragment_start (at);
722       uint8_t *saught = memchr (start, char_to_find, at->buf_length);
723       if (saught)
724         return (saught - start) + rv;
725       else
726         rv += at->buf_length;
727       at = at->next;
728     }
729   return -1;
730 }
731
732 /**
733  * protobuf_c_data_buffer_str_index_of:
734  * @buffer: buffer to scan.
735  * @str_to_find: a string to look for.
736  *
737  * Scans for the first instance of the given string.
738  * returns: its index in the buffer, or -1 if the string
739  * is not in the buffer.
740  */
741 int 
742 protobuf_c_data_buffer_str_index_of (ProtobufCDataBuffer *buffer,
743                          const char *str_to_find)
744 {
745   ProtobufCDataBufferFragment *frag = buffer->first_frag;
746   size_t rv = 0;
747   for (frag = buffer->first_frag; frag; frag = frag->next)
748     {
749       const uint8_t *frag_at = PROTOBUF_C_FRAGMENT_DATA (frag);
750       size_t frag_rem = frag->buf_length;
751       while (frag_rem > 0)
752         {
753           ProtobufCDataBufferFragment *subfrag;
754           const uint8_t *subfrag_at;
755           size_t subfrag_rem;
756           const char *str_at;
757           if (*frag_at != str_to_find[0])
758             {
759               frag_at++;
760               frag_rem--;
761               rv++;
762               continue;
763             }
764           subfrag = frag;
765           subfrag_at = frag_at + 1;
766           subfrag_rem = frag_rem - 1;
767           str_at = str_to_find + 1;
768           if (*str_at == '\0')
769             return rv;
770           while (subfrag != NULL)
771             {
772               while (subfrag_rem == 0)
773                 {
774                   subfrag = subfrag->next;
775                   if (subfrag == NULL)
776                     goto bad_guess;
777                   subfrag_at = protobuf_c_data_buffer_fragment_start (subfrag);
778                   subfrag_rem = subfrag->buf_length;
779                 }
780               while (*str_at != '\0' && subfrag_rem != 0)
781                 {
782                   if (*str_at++ != *subfrag_at++)
783                     goto bad_guess;
784                   subfrag_rem--;
785                 }
786               if (*str_at == '\0')
787                 return rv;
788             }
789 bad_guess:
790           frag_at++;
791           frag_rem--;
792           rv++;
793         }
794     }
795   return -1;
796 }
797
798 /**
799  * protobuf_c_data_buffer_drain:
800  * @dst: buffer to add to.
801  * @src: buffer to remove from.
802  *
803  * Transfer all data from @src to @dst,
804  * leaving @src empty.
805  *
806  * returns: the number of bytes transferred.
807  */
808 #if GSK_DEBUG_BUFFER_ALLOCATIONS
809 size_t
810 protobuf_c_data_buffer_drain (ProtobufCDataBuffer *dst,
811                   ProtobufCDataBuffer *src)
812 {
813   size_t rv = src->size;
814   ProtobufCDataBufferFragment *frag;
815   CHECK_INTEGRITY (dst);
816   CHECK_INTEGRITY (src);
817   for (frag = src->first_frag; frag; frag = frag->next)
818     protobuf_c_data_buffer_append (dst,
819                        protobuf_c_data_buffer_fragment_start (frag),
820                        frag->buf_length);
821   protobuf_c_data_buffer_discard (src, src->size);
822   CHECK_INTEGRITY (dst);
823   CHECK_INTEGRITY (src);
824   return rv;
825 }
826 #else   /* optimized */
827 size_t
828 protobuf_c_data_buffer_drain (ProtobufCDataBuffer *dst,
829                   ProtobufCDataBuffer *src)
830 {
831   size_t rv = src->size;
832
833   CHECK_INTEGRITY (dst);
834   CHECK_INTEGRITY (src);
835   if (src->first_frag == NULL)
836     return rv;
837
838   dst->size += src->size;
839
840   if (dst->last_frag != NULL)
841     {
842       dst->last_frag->next = src->first_frag;
843       dst->last_frag = src->last_frag;
844     }
845   else
846     {
847       dst->first_frag = src->first_frag;
848       dst->last_frag = src->last_frag;
849     }
850   src->size = 0;
851   src->first_frag = src->last_frag = NULL;
852   CHECK_INTEGRITY (dst);
853   return rv;
854 }
855 #endif
856
857 /**
858  * protobuf_c_data_buffer_transfer:
859  * @dst: place to copy data into.
860  * @src: place to read data from.
861  * @max_transfer: maximum number of bytes to transfer.
862  *
863  * Transfer data out of @src and into @dst.
864  * Data is removed from @src.  The number of bytes
865  * transferred is returned.
866  *
867  * returns: the number of bytes transferred.
868  */
869 #if GSK_DEBUG_BUFFER_ALLOCATIONS
870 size_t
871 protobuf_c_data_buffer_transfer(ProtobufCDataBuffer *dst,
872                     ProtobufCDataBuffer *src,
873                     size_t max_transfer)
874 {
875   size_t rv = 0;
876   ProtobufCDataBufferFragment *frag;
877   CHECK_INTEGRITY (dst);
878   CHECK_INTEGRITY (src);
879   for (frag = src->first_frag; frag && max_transfer > 0; frag = frag->next)
880     {
881       size_t len = frag->buf_length;
882       if (len >= max_transfer)
883         {
884           protobuf_c_data_buffer_append (dst, protobuf_c_data_buffer_fragment_start (frag), max_transfer);
885           rv += max_transfer;
886           break;
887         }
888       else
889         {
890           protobuf_c_data_buffer_append (dst, protobuf_c_data_buffer_fragment_start (frag), len);
891           rv += len;
892           max_transfer -= len;
893         }
894     }
895   protobuf_c_data_buffer_discard (src, rv);
896   CHECK_INTEGRITY (dst);
897   CHECK_INTEGRITY (src);
898   return rv;
899 }
900 #else   /* optimized */
901 size_t
902 protobuf_c_data_buffer_transfer(ProtobufCDataBuffer *dst,
903                     ProtobufCDataBuffer *src,
904                     size_t max_transfer)
905 {
906   size_t rv = 0;
907   CHECK_INTEGRITY (dst);
908   CHECK_INTEGRITY (src);
909   while (src->first_frag && max_transfer >= src->first_frag->buf_length)
910     {
911       ProtobufCDataBufferFragment *frag = src->first_frag;
912       src->first_frag = frag->next;
913       frag->next = NULL;
914       if (src->first_frag == NULL)
915         src->last_frag = NULL;
916
917       if (dst->last_frag)
918         dst->last_frag->next = frag;
919       else
920         dst->first_frag = frag;
921       dst->last_frag = frag;
922
923       rv += frag->buf_length;
924       max_transfer -= frag->buf_length;
925     }
926   dst->size += rv;
927   if (src->first_frag && max_transfer)
928     {
929       ProtobufCDataBufferFragment *frag = src->first_frag;
930       protobuf_c_data_buffer_append (dst, protobuf_c_data_buffer_fragment_start (frag), max_transfer);
931       frag->buf_start += max_transfer;
932       frag->buf_length -= max_transfer;
933       rv += max_transfer;
934     }
935   src->size -= rv;
936   CHECK_INTEGRITY (dst);
937   CHECK_INTEGRITY (src);
938   return rv;
939 }
940 #endif  /* !GSK_DEBUG_BUFFER_ALLOCATIONS */
941
942 #if 0
943 /**
944  * protobuf_c_data_buffer_printf:
945  * @buffer: the buffer to append to.
946  * @format: printf-style format string describing what to append to buffer.
947  * @Varargs: values referenced by @format string.
948  *
949  * Append printf-style content to a buffer.
950  */
951 void     protobuf_c_data_buffer_printf              (ProtobufCDataBuffer    *buffer,
952                                          const char   *format,
953                                          ...)
954 {
955   va_list args;
956   va_start (args, format);
957   protobuf_c_data_buffer_vprintf (buffer, format, args);
958   va_end (args);
959 }
960
961 /**
962  * protobuf_c_data_buffer_vprintf:
963  * @buffer: the buffer to append to.
964  * @format: printf-style format string describing what to append to buffer.
965  * @args: values referenced by @format string.
966  *
967  * Append printf-style content to a buffer, given a va_list.
968  */
969 void     protobuf_c_data_buffer_vprintf             (ProtobufCDataBuffer    *buffer,
970                                          const char   *format,
971                                          va_list       args)
972 {
973   gsize size = g_printf_string_upper_bound (format, args);
974   if (size < 1024)
975     {
976       char buf[1024];
977       g_vsnprintf (buf, sizeof (buf), format, args);
978       protobuf_c_data_buffer_append_string (buffer, buf);
979     }
980   else
981     {
982       char *buf = g_strdup_vprintf (format, args);
983       protobuf_c_data_buffer_append_foreign (buffer, buf, strlen (buf), g_free, buf);
984     }
985 }
986
987 /* --- protobuf_c_data_buffer_polystr_index_of implementation --- */
988 /* Test to see if a sequence of buffer fragments
989  * starts with a particular NUL-terminated string.
990  */
991 static gboolean
992 fragment_n_str(ProtobufCDataBufferFragment   *frag,
993                size_t                frag_index,
994                const char          *string)
995 {
996   size_t len = strlen (string);
997   for (;;)
998     {
999       size_t test_len = frag->buf_length - frag_index;
1000       if (test_len > len)
1001         test_len = len;
1002
1003       if (memcmp (string,
1004                   protobuf_c_data_buffer_fragment_start (frag) + frag_index,
1005                   test_len) != 0)
1006         return FALSE;
1007
1008       len -= test_len;
1009       string += test_len;
1010
1011       if (len <= 0)
1012         return TRUE;
1013       frag_index += test_len;
1014       if (frag_index >= frag->buf_length)
1015         {
1016           frag = frag->next;
1017           if (frag == NULL)
1018             return FALSE;
1019         }
1020     }
1021 }
1022
1023 /**
1024  * protobuf_c_data_buffer_polystr_index_of:
1025  * @buffer: buffer to scan.
1026  * @strings: NULL-terminated set of string.
1027  *
1028  * Scans for the first instance of any of the strings
1029  * in the buffer.
1030  *
1031  * returns: the index of that instance, or -1 if not found.
1032  */
1033 int     
1034 protobuf_c_data_buffer_polystr_index_of    (ProtobufCDataBuffer    *buffer,
1035                                 char        **strings)
1036 {
1037   uint8_t init_char_map[16];
1038   int num_strings;
1039   int num_bits = 0;
1040   int total_index = 0;
1041   ProtobufCDataBufferFragment *frag;
1042   memset (init_char_map, 0, sizeof (init_char_map));
1043   for (num_strings = 0; strings[num_strings] != NULL; num_strings++)
1044     {
1045       uint8_t c = strings[num_strings][0];
1046       uint8_t mask = (1 << (c % 8));
1047       uint8_t *rack = init_char_map + (c / 8);
1048       if ((*rack & mask) == 0)
1049         {
1050           *rack |= mask;
1051           num_bits++;
1052         }
1053     }
1054   if (num_bits == 0)
1055     return 0;
1056   for (frag = buffer->first_frag; frag != NULL; frag = frag->next)
1057     {
1058       const char *frag_start;
1059       const char *at;
1060       int remaining = frag->buf_length;
1061       frag_start = protobuf_c_data_buffer_fragment_start (frag);
1062       at = frag_start;
1063       while (at != NULL)
1064         {
1065           const char *start = at;
1066           if (num_bits == 1)
1067             {
1068               at = memchr (start, strings[0][0], remaining);
1069               if (at == NULL)
1070                 remaining = 0;
1071               else
1072                 remaining -= (at - start);
1073             }
1074           else
1075             {
1076               while (remaining > 0)
1077                 {
1078                   uint8_t i = (uint8_t) (*at);
1079                   if (init_char_map[i / 8] & (1 << (i % 8)))
1080                     break;
1081                   remaining--;
1082                   at++;
1083                 }
1084               if (remaining == 0)
1085                 at = NULL;
1086             }
1087
1088           if (at == NULL)
1089             break;
1090
1091           /* Now test each of the strings manually. */
1092           {
1093             char **test;
1094             for (test = strings; *test != NULL; test++)
1095               {
1096                 if (fragment_n_str(frag, at - frag_start, *test))
1097                   return total_index + (at - frag_start);
1098               }
1099             at++;
1100           }
1101         }
1102       total_index += frag->buf_length;
1103     }
1104   return -1;
1105 }
1106 #endif
1107