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