1 /*
2 
3   silcbuffer.h
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 1998 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
17 
18 */
19 /* $Id$ */
20 
21 /****h* silcutil/SILC Buffer Interface
22  *
23  * DESCRIPTION
24  *
25  * SilcBuffer is very simple and easy to use, yet you can do to the
26  * buffer almost anything you want with its method functions. The buffer
27  * is constructed of four different data sections that in whole creates
28  * the allocated data area.
29  *
30  ***/
31 
32 #ifndef SILCBUFFER_H
33 #define SILCBUFFER_H
34 
35 /****s* silcutil/SilcBufferAPI/SilcBuffer
36  *
37  * NAME
38  *
39  *    typedef struct { ... } *SilcBuffer, SilcBufferStruct;
40  *
41  * DESCRIPTION
42  *
43  *    SILC Buffer object. Following short description of the fields
44  *    of the buffer.
45  *
46  * EXAMPLE
47  *
48  *    unsiged char *head;
49  *
50  *        Head of the allocated buffer. This is the start of the allocated
51  *        data area and remains as same throughout the lifetime of the buffer.
52  *        However, the end of the head area or the start of the currently valid
53  *        data area is variable.
54  *
55  *        --------------------------------
56  *        | head  | data         | tail  |
57  *        --------------------------------
58  *        ^       ^
59  *
60  *        Current head section in the buffer is sb->data - sb->head.
61  *
62  *    unsigned char *data;
63  *
64  *        Currently valid data area. This is the start of the currently valid
65  *        main data area. The data area is variable in all directions.
66  *
67  *        --------------------------------
68  *        | head  | data         | tail  |
69  *        --------------------------------
70  *                ^              ^
71  *
72  *        Current valid data area in the buffer is sb->tail - sb->data.
73  *
74  *     unsigned char *tail;
75  *
76  *        Tail of the buffer. This is the end of the currently valid data area
77  *        or start of the tail area. The start of the tail area is variable.
78  *
79  *        --------------------------------
80  *        | head  | data         | tail  |
81  *        --------------------------------
82  *                               ^       ^
83  *
84  *        Current tail section in the buffer is sb->end - sb->tail.
85  *
86  *    unsigned char *end;
87  *
88  *        End of the allocated buffer. This is the end of the allocated data
89  *        area and remains as same throughout the lifetime of the buffer.
90  *        Usually this field is not needed except when checking the size
91  *        of the buffer.
92  *
93  *        --------------------------------
94  *        | head  | data         | tail  |
95  *        --------------------------------
96  *                                       ^
97  *
98  *        Length of the entire buffer is (ie. truelen) sb->end - sb->head.
99  *
100  *     Currently valid data area is considered to be the main data area in
101  *     the buffer. However, the entire buffer is of course valid data and can
102  *     be used as such. Usually head section of the buffer includes different
103  *     kind of headers or similar. Data section includes the main data of
104  *     the buffer. Tail section can be seen as a reserve space of the data
105  *     section. Tail section can be pulled towards end, and thus the data
106  *     section becomes larger.
107  *
108  * SILC Buffer is not thread-safe.  If the same SilcBuffer context must be
109  * used in multithreaded environment concurrency control must be employed.
110  *
111  * SOURCE
112  */
113 typedef struct SilcBufferObject {
114   unsigned char *head;
115   unsigned char *data;
116   unsigned char *tail;
117   unsigned char *end;
118 } *SilcBuffer, SilcBufferStruct;
119 /***/
120 
121 /* Macros */
122 
123 /****f* silcutil/SilcBufferAPI/silc_buffer_data
124  *
125  * NAME
126  *
127  *    unsigned char *silc_buffer_data(SilcBuffer sb)
128  *
129  * DESCRIPTION
130  *
131  *    Returns pointer to the data area of the buffer.
132  *
133  * SOURCE
134  */
135 #define silc_buffer_data(x) (x)->data
136 /***/
137 
138 /****f* silcutil/SilcBufferAPI/silc_buffer_datalen
139  *
140  * NAME
141  *
142  *    #define silc_buffer_datalen ...
143  *
144  * DESCRIPTION
145  *
146  *    Macro that can be used in function argument list to give the data
147  *    pointer and the data length, instead of calling both silc_buffer_data
148  *    and silc_buffer_len separately.
149  *
150  * EXAMPLE
151  *
152  *    // Following are the same thing
153  *    silc_foo_function(foo, silc_buffer_datalen(buf));
154  *    silc_foo_function(foo, silc_buffer_data(buf), silc_buffer_len(buf));
155  *
156  * SOURCE
157  */
158 #define silc_buffer_datalen(x) (x) ? silc_buffer_data((x)) : NULL, \
159   (x) ? silc_buffer_len((x)) : 0
160 /***/
161 
162 /* Inline functions */
163 
164 /****d* silcutil/SilcBufferAPI/silc_buffer_truelen
165  *
166  * NAME
167  *
168  *    SilcUInt32 silc_buffer_truelen(SilcBuffer sb)
169  *
170  * DESCRIPTION
171  *
172  *    Returns the true length of the buffer.
173  *
174  ***/
175 static inline
silc_buffer_truelen(SilcBuffer x)176 SilcUInt32 silc_buffer_truelen(SilcBuffer x)
177 {
178   return (SilcUInt32)(x->end - x->head);
179 }
180 
181 /****d* silcutil/SilcBufferAPI/silc_buffer_len
182  *
183  * NAME
184  *
185  *    SilcUInt32 silc_buffer_len(SilcBuffer sb)
186  *
187  * DESCRIPTION
188  *
189  *    Returns the current length of the data area of the buffer.
190  *
191  ***/
192 static inline
silc_buffer_len(SilcBuffer x)193 SilcUInt32 silc_buffer_len(SilcBuffer x)
194 {
195   return (SilcUInt32)(x->tail - x->data);
196 }
197 
198 /****d* silcutil/SilcBufferAPI/silc_buffer_headlen
199  *
200  * NAME
201  *
202  *    SilcUInt32 silc_buffer_headlen(SilcBuffer sb)
203  *
204  * DESCRIPTION
205  *
206  *    Returns the current length of the head data area of the buffer.
207  *
208  ***/
209 static inline
silc_buffer_headlen(SilcBuffer x)210 SilcUInt32 silc_buffer_headlen(SilcBuffer x)
211 {
212   return (SilcUInt32)(x->data - x->head);
213 }
214 
215 /****d* silcutil/SilcBufferAPI/silc_buffer_taillen
216  *
217  * NAME
218  *
219  *    SilcUInt32 silc_buffer_taillen(SilcBuffer sb)
220  *
221  * DESCRIPTION
222  *
223  *    Returns the current length of the tail data area of the buffer.
224  *
225  ***/
226 static inline
silc_buffer_taillen(SilcBuffer x)227 SilcUInt32 silc_buffer_taillen(SilcBuffer x)
228 {
229   return (SilcUInt32)(x->end - x->tail);
230 }
231 
232 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc
233  *
234  * SYNOPSIS
235  *
236  *    static inline
237  *    SilcBuffer silc_buffer_alloc(SilcUInt32 len);
238  *
239  * DESCRIPTION
240  *
241  *    Allocates new SilcBuffer and returns it.  Returns NULL on error.
242  *
243  ***/
244 
245 static inline
silc_buffer_alloc(SilcUInt32 len)246 SilcBuffer silc_buffer_alloc(SilcUInt32 len)
247 {
248   SilcBuffer sb;
249 
250   /* Allocate new SilcBuffer */
251   sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
252   if (silc_unlikely(!sb))
253     return NULL;
254 
255   if (silc_likely(len)) {
256     /* Allocate the actual data area */
257     sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
258     if (silc_unlikely(!sb->head))
259       return NULL;
260 
261     /* Set pointers to the new buffer */
262     sb->data = sb->head;
263     sb->tail = sb->head;
264     sb->end = sb->head + len;
265   }
266 
267   return sb;
268 }
269 
270 /****f* silcutil/SilcBufferAPI/silc_buffer_free
271  *
272  * SYNOPSIS
273  *
274  *    static inline
275  *    void silc_buffer_free(SilcBuffer sb);
276  *
277  * DESCRIPTION
278  *
279  *    Frees SilcBuffer.  Can be called safely `sb' as NULL.
280  *
281  * NOTES
282  *
283  *    Must not be called for buffers allocated with silc_buffer_salloc,
284  *    silc_buffer_salloc_size, silc_buffer_scopy and silc_buffer_sclone.
285  *
286  ***/
287 
288 static inline
silc_buffer_free(SilcBuffer sb)289 void silc_buffer_free(SilcBuffer sb)
290 {
291   if (sb) {
292 #if defined(SILC_DEBUG)
293     if (sb->head)
294       memset(sb->head, 'F', silc_buffer_truelen(sb));
295 #endif
296     silc_free(sb->head);
297     silc_free(sb);
298   }
299 }
300 
301 /****f* silcutil/SilcBufferAPI/silc_buffer_steal
302  *
303  * SYNOPSIS
304  *
305  *    static inline
306  *    unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len);
307  *
308  * DESCRIPTION
309  *
310  *    Steals the data from the buffer `sb'.  This returns pointer to the
311  *    start of the buffer and the true length of that buffer.  The `sb'
312  *    cannot be used anymore after calling this function because the
313  *    data buffer was stolen.  The `sb' must be freed with silc_buffer_free.
314  *    The caller is responsible of freeing the stolen data buffer with
315  *    silc_free.
316  *
317  ***/
318 
319 static inline
silc_buffer_steal(SilcBuffer sb,SilcUInt32 * data_len)320 unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len)
321 {
322   unsigned char *buf = sb->head;
323   if (data_len)
324     *data_len = silc_buffer_truelen(sb);
325   sb->head = sb->data = sb->tail = sb->end = NULL;
326   return buf;
327 }
328 
329 /****f* silcutil/SilcBufferAPI/silc_buffer_purge
330  *
331  * SYNOPSIS
332  *
333  *    static inline
334  *    void silc_buffer_purge(SilcBuffer sb);
335  *
336  * DESCRIPTION
337  *
338  *    Same as silc_buffer_free but free's only the contents of the buffer
339  *    not the buffer itself.  The `sb' remains intact, data is freed.  Buffer
340  *    is ready for re-use after calling this function.
341  *
342  * NOTES
343  *
344  *    Must not be called for buffers allocated with silc_buffer_salloc,
345  *    silc_buffer_salloc_size, silc_buffer_scopy and silc_buffer_sclone.
346  *
347  ***/
348 
349 static inline
silc_buffer_purge(SilcBuffer sb)350 void silc_buffer_purge(SilcBuffer sb)
351 {
352   silc_free(silc_buffer_steal(sb, NULL));
353 }
354 
355 /****f* silcutil/SilcBufferAPI/silc_buffer_set
356  *
357  * SYNOPSIS
358  *
359  *    static inline
360  *    void silc_buffer_set(SilcBuffer sb,
361  *			   unsigned char *data,
362  *                         SilcUInt32 data_len);
363  *
364  * DESCRIPTION
365  *
366  *    Sets the `data' and `data_len' to the buffer pointer sent as argument.
367  *    The data area is automatically set to the `data_len'. This function
368  *    can be used to set the data to static buffer without needing any
369  *    memory allocations. The `data' will not be copied to the buffer.
370  *
371  * EXAMPLE
372  *
373  *    SilcBufferStruct buf;
374  *    silc_buffer_set(&buf, data, data_len);
375  *
376  ***/
377 
378 static inline
silc_buffer_set(SilcBuffer sb,unsigned char * data,SilcUInt32 data_len)379 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
380 {
381   sb->data = sb->head = data;
382   sb->tail = sb->end = data + data_len;
383 }
384 
385 /****f* silcutil/SilcBufferAPI/silc_buffer_pull
386  *
387  * SYNOPSIS
388  *
389  *    static inline
390  *    unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len);
391  *
392  * DESCRIPTION
393  *
394  *    Pulls current data area towards end. The length of the currently
395  *    valid data area is also decremented. Returns pointer to the data
396  *    area before pulling. Returns NULL on error.
397  *
398  * EXAMPLE
399  *
400  *    ---------------------------------
401  *    | head  | data       | tail     |
402  *    ---------------------------------
403  *            ^
404  *            Pulls the start of the data area.
405  *
406  *    ---------------------------------
407  *    | head     | data    | tail     |
408  *    ---------------------------------
409  *            ^
410  *
411  *    silc_buffer_pull(sb, 20);
412  *
413  ***/
414 
415 static inline
silc_buffer_pull(SilcBuffer sb,SilcUInt32 len)416 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
417 {
418   unsigned char *old_data = sb->data;
419 #if defined(SILC_DEBUG)
420   SILC_ASSERT(len <= silc_buffer_len(sb));
421 #else
422   if (silc_unlikely(len > silc_buffer_len(sb)))
423     return NULL;
424 #endif
425   sb->data += len;
426   return old_data;
427 }
428 
429 /****f* silcutil/SilcBufferAPI/silc_buffer_push
430  *
431  * SYNOPSIS
432  *
433  *    static inline
434  *    unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len);
435  *
436  * DESCRIPTION
437  *
438  *    Pushes current data area towards beginning. Length of the currently
439  *    valid data area is also incremented. Returns a pointer to the
440  *    data area before pushing. Returns NULL on error.
441  *
442  * EXAMPLE
443  *
444  *    ---------------------------------
445  *    | head     | data    | tail     |
446  *    ---------------------------------
447  *               ^
448  *               Pushes the start of the data area.
449  *
450  *    ---------------------------------
451  *    | head  | data       | tail     |
452  *    ---------------------------------
453  *               ^
454  *
455  *    silc_buffer_push(sb, 20);
456  *
457  ***/
458 
459 static inline
silc_buffer_push(SilcBuffer sb,SilcUInt32 len)460 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
461 {
462   unsigned char *old_data = sb->data;
463 #if defined(SILC_DEBUG)
464   SILC_ASSERT((sb->data - len) >= sb->head);
465 #else
466   if (silc_unlikely((sb->data - len) < sb->head))
467     return NULL;
468 #endif
469   sb->data -= len;
470   return old_data;
471 }
472 
473 /****f* silcutil/SilcBufferAPI/silc_buffer_pull_tail
474  *
475  * SYNOPSIS
476  *
477  *    static inline
478  *    unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len);
479  *
480  * DESCRIPTION
481  *
482  *    Pulls current tail section towards end. Length of the current valid
483  *    data area is also incremented. Returns a pointer to the data area
484  *    before pulling. Returns NULL on error.
485  *
486  * EXAMPLE
487  *
488  *    ---------------------------------
489  *    | head  | data       | tail     |
490  *    ---------------------------------
491  *                         ^
492  *                         Pulls the start of the tail section.
493  *
494  *    ---------------------------------
495  *    | head  | data           | tail |
496  *    ---------------------------------
497  *                         ^
498  *
499  *    silc_buffer_pull_tail(sb, 23);
500  *
501  ***/
502 
503 static inline
silc_buffer_pull_tail(SilcBuffer sb,SilcUInt32 len)504 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
505 {
506   unsigned char *old_tail = sb->tail;
507 #if defined(SILC_DEBUG)
508   SILC_ASSERT(len <= silc_buffer_taillen(sb));
509 #else
510   if (silc_unlikely(len > silc_buffer_taillen(sb)))
511     return NULL;
512 #endif
513   sb->tail += len;
514   return old_tail;
515 }
516 
517 /****f* silcutil/SilcBufferAPI/silc_buffer_push_tail
518  *
519  * SYNOPSIS
520  *
521  *    static inline
522  *    unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len);
523  *
524  * DESCRIPTION
525  *
526  *    Pushes current tail section towards beginning. Length of the current
527  *    valid data area is also decremented. Returns a pointer to the
528  *    tail section before pushing. Returns NULL on error.
529  *
530  * EXAMPLE
531  *
532  *    ---------------------------------
533  *    | head  | data           | tail |
534  *    ---------------------------------
535  *                             ^
536  *                             Pushes the start of the tail section.
537  *
538  *    ---------------------------------
539  *    | head  | data       | tail     |
540  *    ---------------------------------
541  *                             ^
542  *
543  *    silc_buffer_push_tail(sb, 23);
544  *
545  ***/
546 
547 static inline
silc_buffer_push_tail(SilcBuffer sb,SilcUInt32 len)548 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
549 {
550   unsigned char *old_tail = sb->tail;
551 #if defined(SILC_DEBUG)
552   SILC_ASSERT((sb->tail - len) >= sb->data);
553 #else
554   if (silc_unlikely((sb->tail - len) < sb->data))
555     return NULL;
556 #endif
557   sb->tail -= len;
558   return old_tail;
559 }
560 
561 /****f* silcutil/SilcBufferAPI/silc_buffer_put_head
562  *
563  * SYNOPSIS
564  *
565  *    static inline
566  *    unsigned char *silc_buffer_put_head(SilcBuffer sb,
567  *					  const unsigned char *data,
568  *					  SilcUInt32 len);
569  *
570  * DESCRIPTION
571  *
572  *    Puts data at the head of the buffer. Returns pointer to the copied
573  *    data area. Returns NULL on error.
574  *
575  * EXAMPLE
576  *
577  *    ---------------------------------
578  *    | head  | data       | tail     |
579  *    ---------------------------------
580  *    ^
581  *    Puts data to the head section.
582  *
583  *    silc_buffer_put_head(sb, data, data_len);
584  *
585  ***/
586 
587 static inline
silc_buffer_put_head(SilcBuffer sb,const unsigned char * data,SilcUInt32 len)588 unsigned char *silc_buffer_put_head(SilcBuffer sb,
589 				    const unsigned char *data,
590 				    SilcUInt32 len)
591 {
592 #if defined(SILC_DEBUG)
593   SILC_ASSERT(len <= silc_buffer_headlen(sb));
594 #else
595   if (silc_unlikely(len > silc_buffer_headlen(sb)))
596     return NULL;
597 #endif
598   return (unsigned char *)memcpy(sb->head, data, len);
599 }
600 
601 /****f* silcutil/SilcBufferAPI/silc_buffer_put
602  *
603  * SYNOPSIS
604  *
605  *    static inline
606  *    unsigned char *silc_buffer_put(SilcBuffer sb,
607  *				     const unsigned char *data,
608  *				     SilcUInt32 len);
609  *
610  * DESCRIPTION
611  *
612  *    Puts data at the start of the valid data area. Returns a pointer
613  *    to the copied data area.  Returns NULL on error.
614  *
615  * EXAMPLE
616  *
617  *    ---------------------------------
618  *    | head  | data       | tail     |
619  *    ---------------------------------
620  *            ^
621  *            Puts data to the data section.
622  *
623  *    silc_buffer_put(sb, data, data_len);
624  *
625  ***/
626 
627 static inline
silc_buffer_put(SilcBuffer sb,const unsigned char * data,SilcUInt32 len)628 unsigned char *silc_buffer_put(SilcBuffer sb,
629 			       const unsigned char *data,
630 			       SilcUInt32 len)
631 {
632 #if defined(SILC_DEBUG)
633   SILC_ASSERT(len <= silc_buffer_len(sb));
634 #else
635   if (silc_unlikely(len > silc_buffer_len(sb)))
636     return NULL;
637 #endif
638   return (unsigned char *)memcpy(sb->data, data, len);
639 }
640 
641 /****f* silcutil/SilcBufferAPI/silc_buffer_put_tail
642  *
643  * SYNOPSIS
644  *
645  *    static inline
646  *    unsigned char *silc_buffer_put_tail(SilcBuffer sb,
647  *					  const unsigned char *data,
648  *					  SilcUInt32 len);
649  *
650  * DESCRIPTION
651  *
652  *    Puts data at the tail of the buffer. Returns pointer to the copied
653  *    data area.  Returns NULL on error.
654  *
655  * EXAMPLE
656  *
657  *    ---------------------------------
658  *    | head  | data           | tail |
659  *    ---------------------------------
660  *                             ^
661  * 			       Puts data to the tail section.
662  *
663  *    silc_buffer_put_tail(sb, data, data_len);
664  *
665  ***/
666 
667 static inline
silc_buffer_put_tail(SilcBuffer sb,const unsigned char * data,SilcUInt32 len)668 unsigned char *silc_buffer_put_tail(SilcBuffer sb,
669 				    const unsigned char *data,
670 				    SilcUInt32 len)
671 {
672 #if defined(SILC_DEBUG)
673   SILC_ASSERT(len <= silc_buffer_taillen(sb));
674 #else
675   if (silc_unlikely(len > silc_buffer_taillen(sb)))
676     return NULL;
677 #endif
678   return (unsigned char *)memcpy(sb->tail, data, len);
679 }
680 
681 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc_size
682  *
683  * SYNOPSIS
684  *
685  *    static inline
686  *    SilcBuffer silc_buffer_alloc_size(SilcUInt32 len);
687  *
688  * DESCRIPTION
689  *
690  *    Allocates `len' bytes size buffer and moves the tail area automatically
691  *    `len' bytes so that the buffer is ready to use without calling the
692  *    silc_buffer_pull_tail.  Returns NULL on error.
693  *
694  ***/
695 
696 static inline
silc_buffer_alloc_size(SilcUInt32 len)697 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
698 {
699   SilcBuffer sb = silc_buffer_alloc(len);
700   if (silc_unlikely(!sb))
701     return NULL;
702   silc_buffer_pull_tail(sb, len);
703   return sb;
704 }
705 
706 /****f* silcutil/SilcBufferAPI/silc_buffer_reset
707  *
708  * SYNOPSIS
709  *
710  *    static inline
711  *    void silc_buffer_reset(SilcBuffer sb);
712  *
713  * DESCRIPTION
714  *
715  *    Resets the buffer to the state as if it was just allocated by
716  *    silc_buffer_alloc.  This does not clear the data area.  Use
717  *    silc_buffer_clear if you also want to clear the data area.
718  *
719  ***/
720 
721 static inline
silc_buffer_reset(SilcBuffer sb)722 void silc_buffer_reset(SilcBuffer sb)
723 {
724   sb->data = sb->tail = sb->head;
725 }
726 
727 /****f* silcutil/SilcBufferAPI/silc_buffer_clear
728  *
729  * SYNOPSIS
730  *
731  *    static inline
732  *    void silc_buffer_clear(SilcBuffer sb);
733  *
734  * DESCRIPTION
735  *
736  *    Clears and initialiazes the buffer to the state as if it was just
737  *    allocated by silc_buffer_alloc.
738  *
739  ***/
740 
741 static inline
silc_buffer_clear(SilcBuffer sb)742 void silc_buffer_clear(SilcBuffer sb)
743 {
744   memset(sb->head, 0, silc_buffer_truelen(sb));
745   silc_buffer_reset(sb);
746 }
747 
748 /****f* silcutil/SilcBufferAPI/silc_buffer_start
749  *
750  * SYNOPSIS
751  *
752  *    static inline
753  *    void silc_buffer_start(SilcBuffer sb);
754  *
755  * DESCRIPTION
756  *
757  *    Moves the data area at the start of the buffer.  The tail area remains
758  *    as is.
759  *
760  ***/
761 
762 static inline
silc_buffer_start(SilcBuffer sb)763 void silc_buffer_start(SilcBuffer sb)
764 {
765   sb->data = sb->head;
766 }
767 
768 /****f* silcutil/SilcBufferAPI/silc_buffer_end
769  *
770  * SYNOPSIS
771  *
772  *    static inline
773  *    void silc_buffer_end(SilcBuffer sb);
774  *
775  * DESCRIPTION
776  *
777  *    Moves the end of the data area to the end of the buffer.  The start
778  *    of the data area remains same.  If the start of data area is at the
779  *    start of the buffer, after this function returns the buffer's data
780  *    area length is the length of the entire buffer.
781  *
782  ***/
783 
784 static inline
silc_buffer_end(SilcBuffer sb)785 void silc_buffer_end(SilcBuffer sb)
786 {
787   sb->tail = sb->end;
788 }
789 
790 /****f* silcutil/SilcBufferAPI/silc_buffer_copy
791  *
792  * SYNOPSIS
793  *
794  *    static inline
795  *    SilcBuffer silc_buffer_copy(SilcBuffer sb);
796  *
797  * DESCRIPTION
798  *
799  *    Generates copy of a SilcBuffer. This copies everything inside the
800  *    currently valid data area, nothing more. Use silc_buffer_clone to
801  *    copy entire buffer.  Returns NULL on error.
802  *
803  ***/
804 
805 static inline
silc_buffer_copy(SilcBuffer sb)806 SilcBuffer silc_buffer_copy(SilcBuffer sb)
807 {
808   SilcBuffer sb_new;
809 
810   sb_new = silc_buffer_alloc_size(silc_buffer_len(sb));
811   if (silc_unlikely(!sb_new))
812     return NULL;
813   silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
814 
815   return sb_new;
816 }
817 
818 /****f* silcutil/SilcBufferAPI/silc_buffer_clone
819  *
820  * SYNOPSIS
821  *
822  *    static inline
823  *    SilcBuffer silc_buffer_clone(SilcBuffer sb);
824  *
825  * DESCRIPTION
826  *
827  *    Clones SilcBuffer. This generates new SilcBuffer and copies
828  *    everything from the source buffer. The result is exact clone of
829  *    the original buffer.  Returns NULL on error.
830  *
831  ***/
832 
833 static inline
silc_buffer_clone(SilcBuffer sb)834 SilcBuffer silc_buffer_clone(SilcBuffer sb)
835 {
836   SilcBuffer sb_new;
837 
838   sb_new = silc_buffer_alloc_size(silc_buffer_truelen(sb));
839   if (silc_unlikely(!sb_new))
840     return NULL;
841   silc_buffer_put(sb_new, sb->head, silc_buffer_truelen(sb));
842   sb_new->data = sb_new->head + silc_buffer_headlen(sb);
843   sb_new->tail = sb_new->data + silc_buffer_len(sb);
844 
845   return sb_new;
846 }
847 
848 /****f* silcutil/SilcBufferAPI/silc_buffer_realloc
849  *
850  * SYNOPSIS
851  *
852  *    static inline
853  *    SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize);
854  *
855  * DESCRIPTION
856  *
857  *    Reallocates buffer. Old data is saved into the new buffer. The buffer
858  *    is exact clone of the old one except that there is now more space
859  *    at the end of buffer.  This always returns the same `sb' unless `sb'
860  *    was NULL. Returns NULL on error.
861  *
862  ***/
863 
864 static inline
silc_buffer_realloc(SilcBuffer sb,SilcUInt32 newsize)865 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
866 {
867   SilcUInt32 hlen, dlen;
868   unsigned char *h;
869 
870   if (!sb)
871     return silc_buffer_alloc(newsize);
872 
873   if (silc_unlikely(newsize <= silc_buffer_truelen(sb)))
874     return sb;
875 
876   hlen = silc_buffer_headlen(sb);
877   dlen = silc_buffer_len(sb);
878   h = (unsigned char *)silc_realloc(sb->head, newsize);
879   if (silc_unlikely(!h))
880     return NULL;
881   sb->head = h;
882   sb->data = sb->head + hlen;
883   sb->tail = sb->data + dlen;
884   sb->end = sb->head + newsize;
885 
886   return sb;
887 }
888 
889 /****f* silcutil/SilcBufferAPI/silc_buffer_realloc_size
890  *
891  * SYNOPSIS
892  *
893  *    static inline
894  *    SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize);
895  *
896  * DESCRIPTION
897  *
898  *    Same as silc_buffer_realloc but moves moves the tail area
899  *    automatically so that the buffer is ready to use without calling the
900  *    silc_buffer_pull_tail.  Returns NULL on error.
901  *
902  ***/
903 
904 static inline
silc_buffer_realloc_size(SilcBuffer sb,SilcUInt32 newsize)905 SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize)
906 {
907   sb = silc_buffer_realloc(sb, newsize);
908   if (silc_unlikely(!sb))
909     return NULL;
910   silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
911   return sb;
912 }
913 
914 /****f* silcutil/SilcBufferAPI/silc_buffer_enlarge
915  *
916  * SYNOPSIS
917  *
918  *    static inline
919  *    SilcBuffer silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size);
920  *
921  * DESCRIPTION
922  *
923  *    Enlarges the buffer by the amount of `size' if it doesn't have that
924  *    must space in the data area and in the tail area.  Moves the tail
925  *    area automatically after enlarging so that the current data area
926  *    is at least the size of `size'.  If there is more space than `size'
927  *    in the data area this does not do anything.  If there is enough
928  *    space in the tail area this merely moves the tail area to reveal
929  *    the extra space.  Returns FALSE on error.
930  *
931  ***/
932 
933 static inline
silc_buffer_enlarge(SilcBuffer sb,SilcUInt32 size)934 SilcBool silc_buffer_enlarge(SilcBuffer sb, SilcUInt32 size)
935 {
936   if (size > silc_buffer_len(sb)) {
937     if (size > silc_buffer_taillen(sb) + silc_buffer_len(sb))
938       if (silc_unlikely(!silc_buffer_realloc(sb, silc_buffer_truelen(sb) +
939 					     (size - silc_buffer_taillen(sb) -
940 					      silc_buffer_len(sb)))))
941 	return FALSE;
942     silc_buffer_pull_tail(sb, size - silc_buffer_len(sb));
943   }
944   return TRUE;
945 }
946 
947 
948 /* SilcStack aware SilcBuffer routines */
949 
950 /****f* silcutil/SilcBufferAPI/silc_buffer_salloc
951  *
952  * SYNOPSIS
953  *
954  *    static inline
955  *    SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len);
956  *
957  * DESCRIPTION
958  *
959  *    Allocates new SilcBuffer and returns it.
960  *
961  *    This routine use SilcStack are memory source.  If `stack' is NULL
962  *    reverts back to normal allocating routine.
963  *
964  ***/
965 
966 static inline
silc_buffer_salloc(SilcStack stack,SilcUInt32 len)967 SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len)
968 {
969   SilcBuffer sb;
970 
971   if (!stack)
972     return silc_buffer_alloc(len);
973 
974   /* Allocate new SilcBuffer */
975   sb = (SilcBuffer)silc_scalloc(stack, 1, sizeof(*sb));
976   if (silc_unlikely(!sb))
977     return NULL;
978 
979   /* Allocate the actual data area */
980   sb->head = (unsigned char *)silc_smalloc(stack, len);
981   if (silc_unlikely(!sb->head))
982     return NULL;
983 
984   /* Set pointers to the new buffer */
985   sb->data = sb->head;
986   sb->tail = sb->head;
987   sb->end = sb->head + len;
988 
989   return sb;
990 }
991 
992 /****f* silcutil/SilcBufferAPI/silc_buffer_salloc_size
993  *
994  * SYNOPSIS
995  *
996  *    static inline
997  *    SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len);
998  *
999  * DESCRIPTION
1000  *
1001  *    Allocates `len' bytes size buffer and moves the tail area automatically
1002  *    `len' bytes so that the buffer is ready to use without calling the
1003  *    silc_buffer_pull_tail.
1004  *
1005  *    This routine use SilcStack are memory source.  If `stack' is NULL
1006  *    reverts back to normal allocating routine.
1007  *
1008  ***/
1009 
1010 static inline
silc_buffer_salloc_size(SilcStack stack,SilcUInt32 len)1011 SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len)
1012 {
1013   SilcBuffer sb = silc_buffer_salloc(stack, len);
1014   if (silc_unlikely(!sb))
1015     return NULL;
1016   silc_buffer_pull_tail(sb, len);
1017   return sb;
1018 }
1019 
1020 /****f* silcutil/SilcBufferAPI/silc_buffer_srealloc
1021  *
1022  * SYNOPSIS
1023  *
1024  *    static inline
1025  *    SilcBuffer silc_buffer_srealloc(SilcStack stack,
1026  *                                    SilcBuffer sb, SilcUInt32 newsize);
1027  *
1028  * DESCRIPTION
1029  *
1030  *    Reallocates buffer. Old data is saved into the new buffer. The buffer
1031  *    is exact clone of the old one except that there is now more space
1032  *    at the end of buffer.
1033  *
1034  *    This routine use SilcStack are memory source.  If `stack' is NULL
1035  *    reverts back to normal allocating routine.
1036  *
1037  ***/
1038 
1039 static inline
silc_buffer_srealloc(SilcStack stack,SilcBuffer sb,SilcUInt32 newsize)1040 SilcBuffer silc_buffer_srealloc(SilcStack stack,
1041 				SilcBuffer sb, SilcUInt32 newsize)
1042 {
1043   SilcUInt32 hlen, dlen;
1044   unsigned char *h;
1045 
1046   if (!stack)
1047     return silc_buffer_realloc(sb, newsize);
1048 
1049   if (!sb)
1050     return silc_buffer_salloc(stack, newsize);
1051 
1052   if (newsize <= silc_buffer_truelen(sb))
1053     return sb;
1054 
1055   hlen = silc_buffer_headlen(sb);
1056   dlen = silc_buffer_len(sb);
1057   h = (unsigned char *)silc_srealloc(stack, silc_buffer_truelen(sb),
1058 				     sb->head, newsize);
1059   if (!h) {
1060     /* Do slow and stack wasting realloc.  The old sb->head is lost and
1061        is freed eventually. */
1062     h = (unsigned char *)silc_smalloc(stack, newsize);
1063     if (silc_unlikely(!h))
1064       return NULL;
1065     memcpy(h, sb->head, silc_buffer_truelen(sb));
1066   }
1067 
1068   sb->head = h;
1069   sb->data = sb->head + hlen;
1070   sb->tail = sb->data + dlen;
1071   sb->end = sb->head + newsize;
1072 
1073   return sb;
1074 }
1075 
1076 /****f* silcutil/SilcBufferAPI/silc_buffer_srealloc_size
1077  *
1078  * SYNOPSIS
1079  *
1080  *    static inline
1081  *    SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
1082  *                                         SilcBuffer sb, SilcUInt32 newsize);
1083  *
1084  * DESCRIPTION
1085  *
1086  *    Same as silc_buffer_srealloc but moves moves the tail area
1087  *    automatically so that the buffer is ready to use without calling the
1088  *    silc_buffer_pull_tail.
1089  *
1090  *    This routine use SilcStack are memory source.  If `stack' is NULL
1091  *    reverts back to normal allocating routine.
1092  *
1093  ***/
1094 
1095 static inline
silc_buffer_srealloc_size(SilcStack stack,SilcBuffer sb,SilcUInt32 newsize)1096 SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
1097 				     SilcBuffer sb, SilcUInt32 newsize)
1098 {
1099   sb = silc_buffer_srealloc(stack, sb, newsize);
1100   if (silc_unlikely(!sb))
1101     return NULL;
1102   silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
1103   return sb;
1104 }
1105 
1106 /****f* silcutil/SilcBufferAPI/silc_buffer_senlarge
1107  *
1108  * SYNOPSIS
1109  *
1110  *    static inline
1111  *    SilcBuffer silc_buffer_senlarge(SilcStack stack, SilcBuffer sb,
1112  *                                    SilcUInt32 size);
1113  *
1114  * DESCRIPTION
1115  *
1116  *    Enlarges the buffer by the amount of `size' if it doesn't have that
1117  *    must space in the data area and in the tail area.  Moves the tail
1118  *    area automatically after enlarging so that the current data area
1119  *    is at least the size of `size'.  If there is more space than `size'
1120  *    in the data area this does not do anything.  If there is enough
1121  *    space in the tail area this merely moves the tail area to reveal
1122  *    the extra space.  Returns FALSE on error.
1123  *
1124  *    This routine use SilcStack are memory source.  If `stack' is NULL
1125  *    reverts back to normal allocating routine.
1126  *
1127  ***/
1128 
1129 static inline
silc_buffer_senlarge(SilcStack stack,SilcBuffer sb,SilcUInt32 size)1130 SilcBool silc_buffer_senlarge(SilcStack stack, SilcBuffer sb, SilcUInt32 size)
1131 {
1132   if (size > silc_buffer_len(sb)) {
1133     if (size > silc_buffer_taillen(sb) + silc_buffer_len(sb))
1134       if (silc_unlikely(!silc_buffer_srealloc(stack, sb,
1135 					      silc_buffer_truelen(sb) +
1136 					      (size - silc_buffer_taillen(sb) -
1137 					       silc_buffer_len(sb)))))
1138 	return FALSE;
1139     silc_buffer_pull_tail(sb, size - silc_buffer_len(sb));
1140   }
1141   return TRUE;
1142 }
1143 
1144 /****f* silcutil/SilcBufferAPI/silc_buffer_scopy
1145  *
1146  * SYNOPSIS
1147  *
1148  *    static inline
1149  *    SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb);
1150  *
1151  * DESCRIPTION
1152  *
1153  *    Generates copy of a SilcBuffer. This copies everything inside the
1154  *    currently valid data area, nothing more. Use silc_buffer_clone to
1155  *    copy entire buffer.
1156  *
1157  *    This routine use SilcStack are memory source.  If `stack' is NULL
1158  *    reverts back to normal allocating routine.
1159  *
1160  ***/
1161 
1162 static inline
silc_buffer_scopy(SilcStack stack,SilcBuffer sb)1163 SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb)
1164 {
1165   SilcBuffer sb_new;
1166 
1167   sb_new = silc_buffer_salloc_size(stack, silc_buffer_len(sb));
1168   if (silc_unlikely(!sb_new))
1169     return NULL;
1170   silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
1171 
1172   return sb_new;
1173 }
1174 
1175 /****f* silcutil/SilcBufferAPI/silc_buffer_sclone
1176  *
1177  * SYNOPSIS
1178  *
1179  *    static inline
1180  *    SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb);
1181  *
1182  * DESCRIPTION
1183  *
1184  *    Clones SilcBuffer. This generates new SilcBuffer and copies
1185  *    everything from the source buffer. The result is exact clone of
1186  *    the original buffer.
1187  *
1188  *    This routine use SilcStack are memory source.  If `stack' is NULL
1189  *    reverts back to normal allocating routine.
1190  *
1191  ***/
1192 
1193 static inline
silc_buffer_sclone(SilcStack stack,SilcBuffer sb)1194 SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb)
1195 {
1196   SilcBuffer sb_new;
1197 
1198   sb_new = silc_buffer_salloc_size(stack, silc_buffer_truelen(sb));
1199   if (silc_unlikely(!sb_new))
1200     return NULL;
1201   silc_buffer_put(sb_new, sb->head, silc_buffer_truelen(sb));
1202   sb_new->data = sb_new->head + silc_buffer_headlen(sb);
1203   sb_new->tail = sb_new->data + silc_buffer_len(sb);
1204 
1205   return sb_new;
1206 }
1207 
1208 #endif /* SILCBUFFER_H */
1209