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